- Home /
Problem with "stun"-timer after player is being hit
In the 2,5D sidescroller I currently develop, I am trying to stun the player for a very brief moment of time once they get hit by an enemy (that also serves to play the "hit"-animation").
I started off by a simple collision script, reducing the players HP by 30 on each hit (player begins with currently 110). That worked just fine so far, each time the enemy touches the player, he loses 30 hp, but only once per touch.
Then I added an extra bit of code to my Update method in order to get the stun-timer in:
if (IsHit)
{
DamageTaken();
Hittimer = Time.time + Hitdown;
if (Hittimer >= Time.time)
{
GetComponent<CharacterController>().enabled = false;
Hitanim = true;
}
if (Hittimer <= Time.time)
{
IsHit = false;
GetComponent<CharacterController>().enabled = true;
Hitanim = false;
}
}
Hittimer isn't initialized before that if-statement, Hitdown is set to 0.2f. Hitanim is given to my Animation-script to handle the hit-animation. IsHit is initialized with false. My enemy-script detects collision and sets IsHit to true once collision occures (simple OnTriggerEnter script).
My problem now is, I seem to have an error in the sub-IFs that I can't find. The "stun" is permanent, as is the boolean for Hitanim. Though the boolean for IsHit instantly resets to false. The damage is done only once too, so I am quite sure that the issue is somewhere in those if-statements
I'd be glad for any help on it, or better methods to get my "stun" done, preferably in C#.
You should cache a reference to the character controller in Awake. It won't fix your problem, but it's an optimization you should always make. Alternatively, make the character controller a public member variable and link it up in the inspector.
Answer by PouletFrit · Jun 10, 2014 at 02:21 PM
You could use a Coroutine. A Coroutine is a function that can extend it's execution on a certain laps of time; in other word you can wait a certain amount of time before resuming it.
You can declare a Coroutine like this:
IEnumerator WaitForStunToEnd() {
// Wait a frame
yield return null;
// Wait 0.2 seconds
yield return new WaitForSeconds(0.2f);
}
To start a Coroutine use:
StartCoroutine(WaitForStunToEnd());
So In your case you just need set the damage taken, disable the character controller in your OnTriggerEnter AND start the coroutine.
In the coroutine, yield return new WaitForSeconds() for the time you need, then reenable the character controller.
Also if you don't want your player to be hit while stun keep using your hit boolean and just set it to true in your OnTriggerEnter and set it back to false in your coroutine.
thanks for the help, the stun works just fine so far, though it seems, even though having the coroutine reenable the character controller and setting IsHit to false, the controller seems to be set to true again way later than the IsHit. I just tried running into an enemy, got stunned but before I could move again I was even hit twice.
This is my coroutine:
IEnumerator WaitForStunToEnd() {
yield return new WaitForSeconds(0.5f);
GetComponent<CharacterController> ().enabled = true;
Hitanim = false;
IsHit = false;
}
I even go as far in my Enemy-script to only set IsHit back to true when the players character controller is also enabled during collision, also the Update methode of the Player is supposed to only trigger when that's the case. Unless of course an IF like the following doesn't quite work (though it does do the damage)
if (IsHit && GetComponent<CharacterController>().enabled == true)
{
DamageTaken();
}
You dont need any of that code in your update anymore. You would either A) Have a timer in your update incremeting it with Time.deltaTime or B) use a coroutine and use yield return, but you shouldn't mix both.
In the OnCollisionEnter function of your enemy script ins$$anonymous$$d of just setting IsHit to true start a function of your player script that will handle everything from there.
something along these lines (not sure how you do it, but not really important since it seems to work)
OnCollisionEnter(Collision collision) {
if (collision.gameObject.tag == "Player") {
collision.gameObject.GetComponent<PlayerScript>().PlayerHit();
}
}
and then in your player script:
public float stunTime = 0.2f;
private bool isStun;
// Let's cache the controller, just like iwaldrop suggested, it's a good idea
private CharacterController controller;
void Awake() {
controller = GetComponent<CharacterController>();
}
public void PlayerHit() {
// If we are already stun, quit; we don't want to be hit again when we are stun
if (isStun) {
return;
}
DamageTaken();
isStun = true;
controller.enabled = false;
StartCoroutine(WaitForStunToEnd());
}
private IEnumerator WaitForStunToEnd() {
yield return new WaitForSeconds(stunTime);
isStun = false;
controller.enabled = true;
}
I'm starting to feel stupid... I have it all set up as in your code, just set the stunTime to 1.0f for testing and ins$$anonymous$$d of the collision script I have:
void OnTriggerEnter(Collider collision)
{
if (collision.gameObject.tag == "Player") {
collision.gameObject.GetComponent<Player>().PlayerHit();
}
}
So basicly the same, just as Trigger ins$$anonymous$$d of Collision The stun seems to last just fine, though I seem to get damage each "tick" as long as I am at the enemy.
First of all, dont feel stupid... We all need to learn and I'm be wrong and misguiding you...
Try adding
Debug.Log("PlayerHit [isStun=" + isStun + "]");
at the start of the PlayerHit() function. And maybe a
Debug.Log("PlayerHit passed the isStun check");
right after the
if (isStun) {
return;
}
just to check if the condition is working.
and then another
Debug.Log("WaitForStunToEnd Done");
at the end of the coroutine
and try to check what's happening...
Alright, just tried out the debugging.
what I got is:
PlayerHit [isStun=False]
PlayerHit passed the isStun check
(posted those two directly under the if-statement, if I have them inside of it they don't come up at all)
then a ton of:
CharacterController.$$anonymous$$ove called on inactive controller
UnityEngine.CharacterController:$$anonymous$$ove(Vector3)
and a
WaitForStunToEnd Done
at the very end. I haven't noticed the Warning about the CharacterController until now, but considering I keep pressing/holding movement keys while stunned, that part is understandable.