Hi, I’m following a series on youtube on 3D development, with which I created the following bit of code:
while (attackProgress <= 1) {
if (attackProgress >= .5f && !hasDamaged) {
hasDamaged = true;
targetEntity.TakeDamage(damage);
}
attackProgress += Time.fixedDeltaTime * attackSpeed;
float interpolation = (-Mathf.Pow(attackProgress, 2) + attackProgress) * 4;
transform.position = Vector3.Lerp(position, attackPosition, interpolation);
yield return null;
}
Every time the game gets into this while loop it crashes. I cannot find the reason. I hope someone could help.
Well, this doesn’t look like it should lock up the game, since yield return null
should wait until the next frame to run the loop again. Two things come to mind: How are you starting the coroutine? You should be doing something like StartCoroutine(MyCoroutine());
Second: Any chance that attackSpeed is 0?
Remove all the code inside the while loop (comment it out) except for the yield return null; statement.
If it still crashes, your crash is somewhere else. If it stops crashing, then maybe look into the .TakeDamage() function.
1 Like
Kurt-Dekker:
Remove all the code inside the while loop (comment it out) except for the yield return null; statement.
If it still crashes, your crash is somewhere else. If it stops crashing, then maybe look into the .TakeDamage() function.
I tried this as well. The program still crashes. However, If I comment out the entire while loop ( meaning the declaration and entire code block ) and put yield return null on the last line of Attack(), the crash doesn’t occur. So it has to be the while-loop doesn’t it?
dgoyette:
Well, this doesn’t look like it should lock up the game, since yield return null
should wait until the next frame to run the loop again. Two things come to mind: How are you starting the coroutine? You should be doing something like StartCoroutine(MyCoroutine());
Second: Any chance that attackSpeed is 0?
[RequireComponent(typeof(CapsuleCollider))]
[RequireComponent(typeof(NavMeshAgent))]
public class Enemy : Entity {
[Range(0.1f, 3f)]
public float attackDistance = .5f;
[Range(0.5f, 5f)]
public float timeBetweenAttacks = 1f;
[Range(0.5f, 5f)]
public float attackSpeed = 3f;
[Range(1f, 50f)]
public float damage = 10f;
private enum State {
Idle,
Chasing,
Attacking
};
private State currentState;
private NavMeshAgent pathfinder;
private Transform target;
private Entity targetEntity;
private Material skin;
private Color originalColor;
private float nextAttackTime;
private float collisionRadius;
private float targetCollisionRadius;
private bool hasTarget;
protected override void Start () {
base.Start();
pathfinder = GetComponent<NavMeshAgent>();
skin = GetComponent<Renderer>().material;
originalColor = skin.color;
if (GameObject.FindGameObjectWithTag("Player") != null) {
currentState = State.Chasing;
hasTarget = true;
target = GameObject.FindGameObjectWithTag("Player").transform;
targetEntity = target.GetComponent<Entity>();
targetEntity.OnDeath += OnTargetDeath;
targetCollisionRadius = target.GetComponent<CapsuleCollider>().radius;
collisionRadius = GetComponent<CapsuleCollider>().radius;
StartCoroutine(UpdatePath());
}
}
private void FixedUpdate () {
if (hasTarget) {
if (Time.time >= nextAttackTime) {
float sqrDistanceToTarget = (target.position - transform.position).sqrMagnitude;
if (sqrDistanceToTarget < Mathf.Pow(attackDistance + collisionRadius + targetCollisionRadius, 2)) {
nextAttackTime = Time.time + timeBetweenAttacks;
StartCoroutine(Attack());
}
}
}
}
private void OnTargetDeath () {
hasTarget = false;
currentState = State.Idle;
}
IEnumerator Attack () {
currentState = State.Attacking;
pathfinder.enabled = false;
Vector3 position = transform.position;
Vector3 direction = (target.position - transform.position).normalized;
Vector3 attackPosition = target.position - direction * (collisionRadius);
float attackProgress = 0;
bool hasDamaged = false;
skin.color = Color.red;
while (attackProgress <= 1) {
if (attackProgress >= .5f && !hasDamaged) {
hasDamaged = true;
targetEntity.TakeDamage(damage);
}
attackProgress += Time.fixedDeltaTime * attackSpeed;
float interpolation = (-Mathf.Pow(attackProgress, 2) + attackProgress) * 4;
transform.position = Vector3.Lerp(position, attackPosition, interpolation);
yield return null;
}
skin.color = originalColor;
currentState = State.Chasing;
pathfinder.enabled = true;
}
IEnumerator UpdatePath () {
float refreshRate = 0.2f;
while (hasTarget) {
if (currentState == State.Chasing) {
Vector3 direction = (target.position - transform.position).normalized;
Vector3 targetPosition = target.position - direction * (collisionRadius + targetCollisionRadius + (attackDistance / 2));
if (!dead) {
pathfinder.SetDestination(targetPosition);
}
yield return new WaitForSeconds(refreshRate);
}
}
}
}
Here is the full class. The way I see it attackSpeed can never be null, and the coroutine starts in the FixedUpdate method.
I can’t really thing of an obvious issue with this code, but for starting i’d check if the timescale is not zero.
Then I would fill it lots of Debug.Logs or just debug the values on visual studio to understand what’s going on.
Correct me if I’m wrong but if the timescale was zero, wouldn’t the game just be practically paused? In my case unity freezes up completely and has to be forced to shut down.
This is what happens when I add the following Debug.Logs:
Code:
IEnumerator Attack () {
currentState = State.Attacking;
pathfinder.enabled = false;
Vector3 position = transform.position;
Vector3 direction = (target.position - transform.position).normalized;
Vector3 attackPosition = target.position - direction * (collisionRadius);
float attackProgress = 0;
bool hasDamaged = false;
skin.color = Color.red;
Debug.Log("Test 1");
while (attackProgress <= 1) {
Debug.Log("Test 2");
if (attackProgress >= .5f && !hasDamaged) {
Debug.Log("Test 3");
hasDamaged = true;
targetEntity.TakeDamage(damage);
}
Debug.Log("Test 4");
attackProgress += Time.fixedDeltaTime * attackSpeed;
Debug.Log("Test 5");
float interpolation = (-Mathf.Pow(attackProgress, 2) + attackProgress) * 4;
Debug.Log("Test 6");
transform.position = Vector3.Lerp(position, attackPosition, interpolation);
Debug.Log("Test 7");
yield return null;
}
skin.color = originalColor;
currentState = State.Chasing;
pathfinder.enabled = true;
}
Result:
OR
I can’t seem to reproduce either result consistently and it appears to be random.
Any more ideas?
Is your attack progress ever going ti be higher 1? If not your loop is infinite forever and ever
The math comes straight out of the tutorial, where it works fine. If it would ever get to be above 1 that would be consistent in both the tutorial and my code. So I don’t think that is the issue.
Can you remove the if statement for testing? Is it crashing too? Sorry, on mobile so cant try myself.
With the entire if-statement (declaration and codeblock) commented out, unity freezes as well.
So other coroutines with a while loop work? Maybe reinstall unity could be a good try
In the full class I posted earlier you can also see an UpdatePath coroutine. This contains a while-loop and works fine. Unity is actually freshly installed. I Reinstalled it on this computer yesterday.
You need to move the yield statement out of the if (currentState == State.Chasing)
block.
If the mob isn’t ‘chasing’ the yield isn’t ever hit, so you get a lockup.
4 Likes
Munchy2007:
You need to move the yield statement out of the if (currentState == State.Chasing)
block.
If the mob isn’t ‘chasing’ the yield isn’t ever hit, so you get a lockup.
Yep that solved the issue. Thanks a lot
2 Likes
I had the exact same issue but this didnt work for me. For anybody else having the same experience, this post fixed my case