In my script i did that when the player is on the top of the platform move it up.
It's working fine. But now i want to make that once it got up play the clip "Down".
using UnityEngine;
using System.Collections;
using System.Reflection;
public class DetectPlayer : MonoBehaviour {
GameObject target;
public void ClearLog()
{
var assembly = Assembly.GetAssembly(typeof(UnityEditor.ActiveEditorTracker));
var type = assembly.GetType("UnityEditorInternal.LogEntries");
var method = type.GetMethod("Clear");
method.Invoke(new object(), null);
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.name == "Platform")
{
Debug.Log("Touching Platform");
}
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.name == "OnTop Detector")
{
Debug.Log("On Top of Platform");
GameObject findGo = GameObject.Find("ThirdPersonController");
GameObject findGo1 = GameObject.Find("Elevator");
findGo.transform.parent = findGo1.transform;
target = GameObject.Find("Elevator");
target.GetComponent<Animation>().Play("Up");
}
}
}
After the line
target.GetComponent<Animation>().Play("Up");
I want when it finish playing it play the down:
target.GetComponent<Animation>().Play("Down");
While both answers should work, another method of doing this with coroutine and the IsPlaying function. You use the coroutine solution if you also want to perform other task after the animation.
For the Animation system:
The old Unity animation playback system. This should not be used in your new Project unless you are still using old Unity version.
IEnumerator playAndWaitForAnim(GameObject target, string clipName)
{
Animation anim = target.GetComponent<Animation>();
anim.Play(clipName);
//Wait until Animation is done Playing
while (anim.IsPlaying(clipName))
{
yield return null;
}
//Done playing. Do something below!
Debug.Log("Done Playing");
}
For the Animator system
This is the new Unity animation playback system. This should be used in your new Project instead of the Animation API. In terms of performance, it's better to use the Animator.StringToHash and compare the current state by hash number instead of the IsName function which compares string since the hash is faster.
Let's say that you have state names called Jump, Move and Look. You get their hashes as below then use the function for playing and waiting for them them below:
const string animBaseLayer = "Base Layer";
int jumpAnimHash = Animator.StringToHash(animBaseLayer + ".Jump");
int moveAnimHash = Animator.StringToHash(animBaseLayer + ".Move");
int lookAnimHash = Animator.StringToHash(animBaseLayer + ".Look");
public IEnumerator PlayAndWaitForAnim(Animator targetAnim, string stateName)
{
//Get hash of animation
int animHash = 0;
if (stateName == "Jump")
animHash = jumpAnimHash;
else if (stateName == "Move")
animHash = moveAnimHash;
else if (stateName == "Look")
animHash = lookAnimHash;
//targetAnim.Play(stateName);
targetAnim.CrossFadeInFixedTime(stateName, 0.6f);
//Wait until we enter the current state
while (targetAnim.GetCurrentAnimatorStateInfo(0).fullPathHash != animHash)
{
yield return null;
}
float counter = 0;
float waitTime = targetAnim.GetCurrentAnimatorStateInfo(0).length;
//Now, Wait until the current state is done playing
while (counter < (waitTime))
{
counter += Time.deltaTime;
yield return null;
}
//Done playing. Do something below!
Debug.Log("Done Playing");
}
For a solution specifically for your particular problem with the collision callback function (OnTriggerEnter), there are two possible ways to do that:
1.Start a coroutine function to play the animation after trigger detection:
void OnTriggerEnter(Collider other)
{
if (other.gameObject.name == "OnTop Detector")
{
Debug.Log("On Top of Platform");
GameObject findGo = GameObject.Find("ThirdPersonController");
GameObject findGo1 = GameObject.Find("Elevator");
findGo.transform.parent = findGo1.transform;
target = GameObject.Find("Elevator");
StartCoroutine(playAnim(target));
}
}
IEnumerator playAnim(GameObject target)
{
Animation anim = target.GetComponent<Animation>();
anim.Play("Up");
//Wait until Up is done Playing the play down
while (anim.IsPlaying("Up"))
{
yield return null;
}
//Now Play Down
anim.Play("Down");
}
OR
2.Make the OnTriggerEnter function a coroutine(IEnumerator) instead of void function:
IEnumerator OnTriggerEnter(Collider other)
{
if (other.gameObject.name == "OnTop Detector")
{
Debug.Log("On Top of Platform");
GameObject findGo = GameObject.Find("ThirdPersonController");
GameObject findGo1 = GameObject.Find("Elevator");
findGo.transform.parent = findGo1.transform;
target = GameObject.Find("Elevator");
Animation anim = target.GetComponent<Animation>();
anim.Play("Up");
//Wait until Up is done Playing the play down
while (anim.IsPlaying("Up"))
{
yield return null;
}
//Now Play Down
anim.Play("Down");
}
}
One way to do this without needing to check manually at all is to use queuing
target.GetComponent<Animation>().PlayQueued("Down", QueueMode.CompleteOthers);
This code will wait for any other animation currently playing on the object to finish before playing the queued animation.
The Unity API page regarding this topic
Related
I am trying to write a script for an enemy to chase a player for about 2 seconds and then stopping. I want to have the player run into a boxcollider and when this happens the enemy will chase the player for 2 seconds. I've been trying for a while and had no luck. I'm hoping someone with more skill can help me write this code so it works properly using Unity 2d. Thanks
void Start()
{
var x = 0;
var y = 0;
player = GameObject.Find("Player").GetComponent<PlayerMovement>().playerBox;
}
public void OnCollisionEnter2D(Collision2D collision)
{
//If the player is touching the knights targetting box, then run the command to chase.
if (collision.collider.tag == "Player")
{
isChasing = true;
chase();
}
}
public void chase()
{
if (isChasing)
{
var x = playerTransform.position.x - enemyTransform.position.x;
var y = playerTransform.position.y - enemyTransform.position.y;
knightRB.velocity = new Vector2(x / 20, y / 20);
StartCoroutine(StopChasing());
}
}
IEnumerator StopChasing()
{
yield return new WaitForSeconds(2);
isChasing = false;
}
Below is my implementation:
Transfrom target;
bool isChasing;
float speed = 5;
RigidBody knightRB;
// -----------------------------------------------------------------------------
// Sets up the knight
void Start()
{
target = GameObject.Find("Player").transform;
knightRB = GetComponent<RigidBody>();
}
// -----------------------------------------------------------------------------
//Checks for a collision with the player
void OnCollisionEnter2D(Collision2D collision)
{
//If the player is touching the knights targetting box, then run the command to chase.
if (collision.collider.tag == "Player" && !isChasing)
{
StartCoRoutine(ChaseSequence());
}
}
// -----------------------------------------------------------------------------
// handles the chase
void FixedUpdate()
{
Chase();
}
void Chase()
{
if (!isChasing)
return;
var direction = (target.position - transform.position).normalized;
knightRB.MovePosition(transfrom.position + direction * Time.deltaTime * speed);
}
// -----------------------------------------------------------------------------
// Stops and starts the chase sequence
IEnumerator ChaseSequence()
{
isChasing = true;
yield return new WaitForSeconds(2);
isChasing = false;
}
There are some assumptions I have made. Mainly that the this script belongs on the knight. Also the collider is what interacts with the environment. Meaning physically. If you have a trigger that does the detection you should be using OnTriggerEnter instead of OnCollisionEnter
I'm new to Unity and C#, trying to make a 2D game.
The animator controller screenshot with all the transition is attached, the (isGrounded) bool from idle to jump is false, from jump to idle is true, and from run to jump is false, but in this case, the character doesn't jump, it shakes in its place transitioning from idle to jump very quickly. When I remove the jump animation the character moves normally but of course no jumping. What could be the problem here?
This is also the CharacterController2D script:
public float jumpForce = 600f;
// player health
public int playerHealth = 1;
// LayerMask to determine what is considered ground for the player
public LayerMask whatIsGround;
internal void EnemyBounce(CharacterController2D characterController2D)
{
throw new NotImplementedException();
}
// Transform just below feet for checking if player is grounded
public Transform groundCheck;
// player can move?
// we want this public so other scripts can access it but we don't want to show in editor as it might confuse designer
[HideInInspector]
public bool playerCanMove = true;
// SFXs
public AudioClip coinSFX;
public AudioClip deathSFX;
public AudioClip fallSFX;
public AudioClip jumpSFX;
public AudioClip victorySFX;
// private variables below
// store references to components on the gameObject
Transform _transform;
Rigidbody2D _rigidbody;
Animator _animator;
AudioSource _audio;
// hold player motion in this timestep
float _vx;
float _vy;
// player tracking
bool _facingRight = true;
bool _isGrounded = false;
bool _isRunning = false;
bool _canDoubleJump = false;
// store the layer the player is on (setup in Awake)
int _playerLayer;
// number of layer that Platforms are on (setup in Awake)
int _platformLayer;
void Awake()
{
// get a reference to the components we are going to be changing and store a reference for efficiency purposes
_transform = GetComponent<Transform>();
_rigidbody = GetComponent<Rigidbody2D>();
if (_rigidbody == null) // if Rigidbody is missing
Debug.LogError("Rigidbody2D component missing from this gameobject");
_animator = GetComponent<Animator>();
if (_animator == null) // if Animator is missing
Debug.LogError("Animator component missing from this gameobject");
_audio = GetComponent<AudioSource>();
if (_audio == null)
{ // if AudioSource is missing
Debug.LogWarning("AudioSource component missing from this gameobject. Adding one.");
// let's just add the AudioSource component dynamically
_audio = gameObject.AddComponent<AudioSource>();
}
// determine the player's specified layer
_playerLayer = this.gameObject.layer;
// determine the platform's specified layer
_platformLayer = LayerMask.NameToLayer("Platform");
}
// this is where most of the player controller magic happens each game event loop
void Update()
{
// exit update if player cannot move or game is paused
if (!playerCanMove || (Time.timeScale == 0f))
return;
// determine horizontal velocity change based on the horizontal input
_vx = CrossPlatformInputManager.GetAxisRaw("Horizontal");
// Determine if running based on the horizontal movement
if (_vx != 0)
{
_isRunning = true;
}
else
{
_isRunning = false;
}
// set the running animation state
_animator.SetBool("Running", _isRunning);
// get the current vertical velocity from the rigidbody component
_vy = _rigidbody.velocity.y;
// Check to see if character is grounded by raycasting from the middle of the player
// down to the groundCheck position and see if collected with gameobjects on the
// whatIsGround layer
_isGrounded = Physics2D.Linecast(_transform.position, groundCheck.position, whatIsGround);
// allow double jump after grounded
if (_isGrounded)
{
_canDoubleJump = true;
}
// Set the grounded animation states
_animator.SetBool("Grounded", _isGrounded);
if (_isGrounded && CrossPlatformInputManager.GetButtonDown("Jump")) // If grounded AND jump button pressed, then allow the player to jump
{
DoJump();
}
else if (_canDoubleJump && CrossPlatformInputManager.GetButtonDown("Jump")) // If candoublejump and jump button pressed, then allow player to double jump
{
DoJump();
// disable double hump after double jumping since you can only really do it once
_canDoubleJump = false;
}
// If the player stops jumping mid jump and player is not yet falling
// then set the vertical velocity to 0 (he will start to fall from gravity)
if (CrossPlatformInputManager.GetButtonUp("Jump") && _vy > 0f)
{
_vy = 0f;
}
// Change the actual velocity on the rigidbody
_rigidbody.velocity = new Vector2(_vx * moveSpeed, _vy);
// if moving up then don't collide with platform layer
// this allows the player to jump up through things on the platform layer
// NOTE: requires the platforms to be on a layer named "Platform"
Physics2D.IgnoreLayerCollision(_playerLayer, _platformLayer, (_vy > 0.0f));
}
// Checking to see if the sprite should be flipped
// this is done in LateUpdate since the Animator may override the localScale
// this code will flip the player even if the animator is controlling scale
void LateUpdate()
{
// get the current scale
Vector3 localScale = _transform.localScale;
if (_vx > 0) // moving right so face right
{
_facingRight = true;
}
else if (_vx < 0)
{ // moving left so face left
_facingRight = false;
}
// check to see if scale x is right for the player
// if not, multiple by -1 which is an easy way to flip a sprite
if (((_facingRight) && (localScale.x < 0)) || ((!_facingRight) && (localScale.x > 0)))
{
localScale.x *= -1;
}
// update the scale
_transform.localScale = localScale;
}
// if the player collides with a MovingPlatform, then make it a child of that platform
// so it will go for a ride on the MovingPlatform
void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.tag == "MovingPlatform")
{
this.transform.parent = other.transform;
}
}
// if the player exits a collision with a moving platform, then unchild it
void OnCollisionExit2D(Collision2D other)
{
if (other.gameObject.tag == "MovingPlatform")
{
this.transform.parent = null;
}
}
//make the player jump
void DoJump()
{
// reset current vertical motion to 0 prior to jump
_vy = 0f;
// add a force in the up direction
_rigidbody.AddForce(new Vector2(4, jumpForce));
// play the jump sound
PlaySound(jumpSFX);
}
// do what needs to be done to freeze the player
void FreezeMotion()
{
playerCanMove = false;
_rigidbody.velocity = new Vector2(0, 0);
_rigidbody.isKinematic = true;
}
// do what needs to be done to unfreeze the player
void UnFreezeMotion()
{
playerCanMove = true;
_rigidbody.isKinematic = false;
}
// play sound through the audiosource on the gameobject
void PlaySound(AudioClip clip)
{
_audio.PlayOneShot(clip);
}
// public function to apply damage to the player
public void ApplyDamage(int damage)
{
if (playerCanMove)
{
playerHealth -= damage;
if (playerHealth <= 0)
{ // player is now dead, so start dying
PlaySound(deathSFX);
StartCoroutine(KillPlayer());
}
}
}
// public function to kill the player when they have a fall death
public void FallDeath()
{
if (playerCanMove)
{
playerHealth = 0;
PlaySound(fallSFX);
StartCoroutine(KillPlayer());
}
}
// coroutine to kill the player
IEnumerator KillPlayer()
{
if (playerCanMove)
{
// freeze the player
FreezeMotion();
// play the death animation
_animator.SetTrigger("Death");
// After waiting tell the GameManager to reset the game
yield return new WaitForSeconds(2.0f);
if (GameManager.gm) // if the gameManager is available, tell it to reset the game
GameManager.gm.ResetGame();
else // otherwise, just reload the current level
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
public void CollectCoin(int amount)
{
PlaySound(coinSFX);
if (GameManager.gm) // add the points through the game manager, if it is available
GameManager.gm.AddPoints(amount);
}
// public function on victory over the level
public void Victory()
{
PlaySound(victorySFX);
FreezeMotion();
_animator.SetTrigger("Victory");
if (GameManager.gm) // do the game manager level compete stuff, if it is available
GameManager.gm.LevelCompete();
}
// public function to respawn the player at the appropriate location
public void Respawn(Vector3 spawnloc)
{
UnFreezeMotion();
playerHealth = 1;
_transform.parent = null;
_transform.position = spawnloc;
_animator.SetTrigger("Respawn");
}
public void EnemyBounce()
{
DoJump();
}
}
I also tried adding a separate Jump script, it doesn't work.
Just gonna take some notes while reading the code...
This:
// Determine if running based on the horizontal movement
if (_vx != 0)
{
_isRunning = true;
}
else
{
_isRunning = false;
}
// set the running animation state
_animator.SetBool("Running", _isRunning);
Can be replaced by
_isRunning = _vx != 0;
_animator.SetBool("Running", _isRunning);
or even just creating a property accessor
bool _isRunning {
get {
return _vx != 0;
}
}
As for your actual question; I can't see your transitions but I'm assuming there's something wrong with them, keep trying to debug it by removing SetBools, and check the Animator on what is being triggered. The Any State -> Idle looks suspicious.
Basically I want to make a player that can transform into demon at will whenever the user press the power-up button however I want the transformation to end after 60 seconds (when the transformation ends I want the player revert back to his original state). I also want the transformation to end if the player gets hit by an enemy. So far I've made this code and it works but I'm having trouble resetting the yield wait for seconds back to 60 seconds when if the player gets hit by an enemy and if the user decided to press the button to transform the player back into a demon. Can anyone help me with this problem?
In my hierarchy I have my player as the parent and my demon player as the child. A playermovement script attached to the player as well as the transformation script below:
public GameObject demon;
public BoxCollider2D col;
public Renderer rend;
public ParticleSystem par1;
public static Vector3 target;
void Start () {
target = transform.position;
}
void Update () {
target.z = transform.position.z;
}
public void DemonCharacter() {
StartCoroutine (PowerUpCoroutine ());
}
private IEnumerator PowerUpCoroutine() {
yield return new WaitForSeconds (0.3f);
par1.Play (); // particle system animation to cover transformation happening
par1.transform.position = target;
yield return new WaitForSeconds (0.2f);
demon.SetActive (true); // activates demon gameobject
rend.enabled = false; // deactivate players spriterenderer
col.enabled = false;
yield return new WaitForSeconds (60f);
demon.SetActive (false); // deactivates demon gameobject
rend.enabled = true; // activate players spriterenderer
col.enabled = true;
par1.Stop ();
}
And on my demon player, I attached this script;
I works but when the user clicks on the button to transform into a demon the yield waitforseconds doesn't stop, so when the player transform into a demon seconds later the demon player transforms back into the player rather than resetting the yield wait for seconds.
public BoxCollider2D Playercol;
public Renderer PlayerRend;
void Start()
{
}
void Update ()
{
}
void OnTriggerEnter2D(Collider2D col) {
if (col.tag == "enemy") {
demon.SetActive (false);
PlayerRend.enabled = true;
Playercol.enabled = true;
}
}
Another way than what #m.rogalski suggested would be to use a simple float variable as timer:
public GameObject demon;
public BoxCollider2D col;
public Renderer rend;
public ParticleSystem par1;
public static Vector3 target;
private float demonTimer;
void Start()
{
target = transform.position;
demonTimer = 0.0f;
}
void Update()
{
target.z = transform.position.z;
if (demonTimer > 0.0f)
{
demonTimer -= Time.deltaTime;
if (demonTimer <= 0.0f)
{
demon.SetActive(false);
rend.enabled = true;
col.enabled = true;
}
}
}
public void DemonCharacter()
{
par1.Play();
par1.transform.position = target;
demon.SetActive(true);
rend.enabled = false;
col.enabled = false;
demonTimer = 60.0f;
}
public void CancelDemon()
{
demonTimer = 0.0f;
}
Hope this helps,
My suggestion would be for you to modify your Coroutine to not use WaitForSeconds but use it's own timing calculations.
// create the flag indicating interruption
bool _interrupt = false;
// create your coroutine
IEnumerator PowerUpCoroutine()
{
// set the time you want to hold transformation
const float TRANSFORMATION_INTERVAL = 60.0f;
// currently elapsed time
float currentlyElapsed = 0.0f;
// add your logic for pre-transformation
while ( currentlyElapsed < TRANSFORMATION_INTERVAL && !_interrupt )
{
yield return null;
currentlyElapsed += Time.deltaTime;
}
// add post-transformation logic
// revert transformation process
_interrupt = false;
}
Now if you run this coroutine calling StartCoroutine(PowerUpCoroutine()); you can interrupt it setting _interrupt flag to true. eg :
public void Interrupt()
{
_interrupt = true;
}
// in some update :
if ( gotHitThisFrame == true )
Interrupt();
The part with touching the object and playing the animation is working fine now i want to add the walls script part.
In this case i change a cube height.
What i need to do is that only when the player touch an object it will raise/change the height of another object.
Son in the first case i find when the player is touching an object:
using UnityEngine;
using System.Collections;
using System.Reflection;
public class DetectPlayer : MonoBehaviour {
GameObject target;
int counter = 0;
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.name == "ThirdPersonController") // "Platform"
{
Debug.Log("Touching Platform");
}
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.name == "ThirdPersonController") // "OnTop Detector"
{
counter = 0;
Debug.Log("On Top of Platform");
target = GameObject.Find("Elevator");
GameObject findGo = GameObject.Find("ThirdPersonController");
GameObject findGo1 = GameObject.Find("Elevator");
findGo.transform.parent = findGo1.transform;
GameObject go = GameObject.Find("CubeToRaise");
go.GetComponent<RaiseWalls>();
Debug.Log("The button clicked, raising the wall");
StartCoroutine(playAnim(target));
}
}
void OnTriggerExit(Collider other)
{
GameObject findGo = GameObject.Find("ThirdPersonController");
findGo.transform.parent = null;
}
IEnumerator playAnim(GameObject target)
{
Animation anim = target.GetComponent<Animation>();
foreach (AnimationState clip in anim)
{
// do initialisation or something on clip
clip.speed = 1;
}
while (true)
{
if (counter == 1)
break;
anim.Play("Up");
while (anim.IsPlaying("Up"))
{
yield return null;
}
anim.Play("Down");
while (anim.IsPlaying("Down"))
{
yield return null;
}
yield return null;
counter++;
}
}
void OnGUI()
{
GUI.Box(new Rect(300, 300, 200, 20),
"Times lift moved up and down " + counter);
}
}
At this part i'm calling the second script RaiseWalls:
using UnityEngine;
using System.Collections;
public class RaiseWalls : MonoBehaviour
{
public GameObject gameObjectToRaise;
public float speed;
// Use this for initialization
void Start()
{
speed = 2;
}
void Update()
{
gameObjectToRaise.transform.localScale += new Vector3(0, 50, 0);
}
}
GameObject go = GameObject.Find("CubeToRaise");
go.GetComponent<RaiseWalls>();
Debug.Log("The button clicked, raising the wall");
Now the DetectPlayer is attached on one game object.
The RaiseWalls script is attached on another game object.
On the RaiseWalls script i want to set the speed of the object height change. Now it's changing the height by 50 but many times. I want it to change it by 50 but in slow motion like a slowly building/raising wall. Like electronic fence that raise from bottom to top effect.
Second problem i want that first it will raise the wall or walls once it finished raising the walls move to the next part in the DetectPlayer script:
StartCoroutine(playAnim(target));
Steps:
When the player is touching the object in DetectPlayer script raise the wall/s in RaiseWalls script in specific speed.
When the walls raised only then make the StartCoroutine.
So, you want to raise the wall 50 in total, but with a controllable speed.
First make a counter in RaiseWalls:
float raiseAmount;
Also record the total to raise it, which makes it easier to change later:
float raiseTotal = 50;
Then, in your Update, raise it by a little, but record how much was raised
if(raiseAmount < raiseTotal ) // i.e. we haven't raised it fully
{
// work out how much to raise it
float raiseThisFrame = speed * Time.DeltaTime; // to account for frame rate
// now we cap it to make sure it doesn't go over 50
if(raiseAmount + raiseThisFrame > raiseTotal )
{
raiseThisFrame = raiseTotal - raiseAmount;
}
// add raiseThisFrame to raiseAmount
raiseAmount += raiseThisFrame;
gameObjectToRaise.transform.localScale += new Vector3(0, raiseThisFrame, 0);
}
In the script this is the original where the part of the parent was inside the OnTriggerEnter function:
using UnityEngine;
using System.Collections;
using System.Reflection;
public class DetectPlayer : MonoBehaviour {
GameObject target;
public void ClearLog()
{
var assembly = Assembly.GetAssembly(typeof(UnityEditor.ActiveEditorTracker));
var type = assembly.GetType("UnityEditorInternal.LogEntries");
var method = type.GetMethod("Clear");
method.Invoke(new object(), null);
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.name == "Platform")
{
Debug.Log("Touching Platform");
}
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.name == "OnTop Detector")
{
Debug.Log("On Top of Platform");
target = GameObject.Find("Elevator");
GameObject findGo = GameObject.Find("ThirdPersonController");
GameObject findGo1 = GameObject.Find("Elevator");
findGo.transform.parent = findGo1.transform;
StartCoroutine(playAnim(target));
}
}
IEnumerator playAnim(GameObject target)
{
Animation anim = target.GetComponent<Animation>();
int counter = 0;
foreach (AnimationState clip in anim)
{
// do initialisation or something on clip
clip.speed = 1;
}
while (true)
{
if (counter == 10)
break;
//Play Up Anim
anim.Play("Up");
//Wait until Up is done Playing the play down
while (anim.IsPlaying("Up"))
{
yield return null;
}
//Now Play Down Anim
anim.Play("Down");
//Wait until down is done Playing
while (anim.IsPlaying("Down"))
{
yield return null;
}
yield return null; //Make sure there is no freezing
//Return to the top of the while|true loop
counter++;
}
}
}
And this is after i removed the parent part from the OnTriggerEnter and put it in the Start function i created:
using UnityEngine;
using System.Collections;
using System.Reflection;
public class DetectPlayer : MonoBehaviour {
GameObject target;
void Start()
{
GameObject findGo = GameObject.Find("ThirdPersonController");
GameObject findGo1 = GameObject.Find("Elevator");
findGo.transform.parent = findGo1.transform;
}
public void ClearLog()
{
var assembly = Assembly.GetAssembly(typeof(UnityEditor.ActiveEditorTracker));
var type = assembly.GetType("UnityEditorInternal.LogEntries");
var method = type.GetMethod("Clear");
method.Invoke(new object(), null);
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.name == "Platform")
{
Debug.Log("Touching Platform");
}
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.name == "OnTop Detector")
{
Debug.Log("On Top of Platform");
target = GameObject.Find("Elevator");
StartCoroutine(playAnim(target));
}
}
IEnumerator playAnim(GameObject target)
{
Animation anim = target.GetComponent<Animation>();
int counter = 0;
foreach (AnimationState clip in anim)
{
// do initialisation or something on clip
clip.speed = 1;
}
while (true)
{
if (counter == 10)
break;
//Play Up Anim
anim.Play("Up");
//Wait until Up is done Playing the play down
while (anim.IsPlaying("Up"))
{
yield return null;
}
//Now Play Down Anim
anim.Play("Down");
//Wait until down is done Playing
while (anim.IsPlaying("Down"))
{
yield return null;
}
yield return null; //Make sure there is no freezing
//Return to the top of the while|true loop
counter++;
}
}
}
In both cases it's working fine when the player is on the platform and "On Top of Platform".
The problem is while the Platfrom is moving up and down and i move the player to fall out of the Platform to the ground then everything stuttering/shaking.
It happen only when the Platform is moving up or down and while it's moving up and down i move the player out of it.
And once the platform finish moving up and down after 10 times everything back to be fine. I tried to move the Parent part to the Start function but it didn't help. Still stuttering/shaking when player is moving out of the platform/elevator while it's moving up/down.
This is a small video clip i recorded showing the problem:
Video clip stuttering
The solution was to keep the first original code.
And to add OnTriggerExit function and unparent the gameobject:
void OnTriggerExit(Collider other)
{
GameObject findGo = GameObject.Find("ThirdPersonController");
findGo.transform.parent = null;
}