- Home /
Multiple coroutines starting from Update() freeze/crash Unity
I'm having a problem with a script I'm developing.
The idea is to move a character from A(x,y) to B(x',y') in a 2D top-down grid-like map. I've done all the path calculations and the coroutine I'm using is working. The problem is, it works if I don't try to call it again before its completion. In other words and using an example: suppose I'm in A(x,y) and want to go to B(x',y'). I click the map, the calculations are made, the path is set and the coroutine starts. While the character is walking, I suddenly want to change directions and go to C(x'',y''). At this point, if I click the map again, Unity just freezes, while occupying more and more memory, and later on it crashes.
Maybe someone can help me figure this out. I've tried updating a boolean between true and false to use as a flag outside the coroutine, and also tried to call the StopCoroutine function, to no avail. If you have any idea as to why this is happening, please help.
Here's the code, by the way:
class PlayerScript : MonoBehaviour {
public GameObject map_go;
public float speed;
private Map map;
private List<Vector3> path;
public void Start() {
path = new List<Vector3>();
map = map_go.GetComponent<Map>();
StartCoroutine(move());
}
public void Update() {
Vector3 clickpoint = transform.position;
Vector3 destination;
bool clicked = false;
if (Input.GetMouseButtonDown(0)) {
clickpoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
clicked = true;
}
else if (Input.touchCount > 0) {
clickpoint = Camera.main.ScreenToWorldPoint(new Vector3
(Input.GetTouch(0).position.x,
Input.GetTouch(0).position.y,
transform.position.z));
clicked = true;
}
if (clicked) {
int dest_x = Mathf.RoundToInt(clickpoint.x);
int dest_y = Mathf.RoundToInt(clickpoint.y);
destination = new Vector3(dest_x, dest_y,transform.position.z);
path = map.calculatePath(transform.position, destination);
//StartCoroutine(move(path));
}
}
IEnumerator move () {
Vector3 dest = Vector3.zero;
while (true) {
while (path.Count == 0) yield return null;
for (int i = 0; i < path.Count; i++) {
dest = path[i];
Vector3 src = transform.position;
float step = (speed / (src - dest).magnitude) * Time.fixedDeltaTime;
float t = 0;
while (t <= 1.0f) {
t += step;
transform.position = Vector3.Lerp (src,dest,t);
yield return new WaitForFixedUpdate();
}
transform.position = dest;
}
path = new List<Vector3>();
}
}
}
Thanks in advance!
try starting your coroutine from Start()
and have it wait until you've got a path to follow then run your current code from move
- just have it do a yield return null
if you're waiting.... the overhead from having it wait without a path is negligible, and faster in overall performance than starting a coroutine.
also, move the GetComponent
from Update()
to Start()
- there's no need to do it every Update()
, unless map_go
changes... in which case use a getter/setter for it and get the component when you set map_go
.
hope that helps.
Thanks for the answer.
Following your instructions, I ended up with the following result:
class PlayerScript : $$anonymous$$onoBehaviour {
public GameObject map_go;
public float speed;
private $$anonymous$$ap map;
private List<Vector3> path;
public void Start() {
map = map_go.GetComponent<$$anonymous$$ap>();
StartCoroutine(move());
}
public void Update() {
Vector3 clickpoint = transform.position;
Vector3 destination;
bool clicked = false;
if (Input.Get$$anonymous$$ouseButtonDown(0)) {
clickpoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
clicked = true;
}
else if (Input.touchCount > 0) {
clickpoint = Camera.main.ScreenToWorldPoint(new Vector3
(Input.GetTouch(0).position.x,
Input.GetTouch(0).position.y,
transform.position.z));
clicked = true;
}
if (clicked) {
int dest_x = $$anonymous$$athf.RoundToInt(clickpoint.x);
int dest_y = $$anonymous$$athf.RoundToInt(clickpoint.y);
destination = new Vector3(dest_x, dest_y,transform.position.z);
path = map.calculatePath(transform.position, destination);
//StartCoroutine(move(path));
}
}
IEnumerator move () {
while (true) {
while (path == null) yield return null;
foreach (Vector3 dest in path) {
Vector3 src = transform.position;
float step = (speed / (src - dest).magnitude) * Time.fixedDeltaTime;
float t = 0;
while (t <= 1.0f) {
t += step;
transform.position = Vector3.Lerp (src,dest,t);
yield return new WaitForFixedUpdate();
}
transform.position = dest;
}
path = null;
}
}
}
The Good News: thanks for the tip on the GetComponent, totally forgot about that. And the coroutine always running on the background is also a pretty nice touch.
The Bad News: still crashing Unity when I try to start another move while the first is occurring. $$anonymous$$aybe I didn't understand your instructions clearly, but I suppose this was the result you were thinking...
Your answer
Follow this Question
Related Questions
How to an animation clip based on characters(letters) in an array 1 Answer
How to increment score by one, every time player moves the device face down ? 2 Answers
WaitForSeconds using Update? 1 Answer
Coroutine not working properly 1 Answer
Synchronization in execution of methods of a class using co-routines. 1 Answer