- Home /
Concurrent coroutines working on same datastructure hang unity!
I have a list of gameobjects that represent the nodes of snake character in my game. I have 2 coroutines, one that adds more nodes to this list, and one that pulls all the nodes towards the head node when a certain condition is met. This pull coroutine is basically just sucking all the nodes towards the first node position using a lerp on their positions. But sometimes when the pull coroutine and the increment length coroutine run concurrently unity hangs.
// This is the pull coroutine
IEnumerator Pull() {
List<GameObject> nodes = GameObjectAccessor.instance.getSnakeData().getSnakeNodes();
Vector3 [] positions = new Vector3[nodes.Count + 1];
positions[0] = m_orbPos;
for(int i = 1; i < positions.Length; i++) {
positions[i] = nodes[i - 1].transform.position;
}
float distTailTravels = (nodes.Count - 1) * getGreedyHeadScale();
float pullSpeed = distTailTravels / orbPullTime;
float targetResetTime = getGreedyHeadScale() / pullSpeed;
float lastResetTime = 0.0f;
int targetOffset = 1;
bool finished = false;
while(!finished) {
Logger.Log("Starting Pull");
if(lastResetTime > targetResetTime) {
targetOffset++;
lastResetTime = 0.0f;
}
for(int i = 1; i < positions.Length; i++) {
if(i - targetOffset >= 0) {
Vector3 destination = positions[i - targetOffset];
nodes[i - 1].transform.position = Vector3.Lerp(nodes[i - 1].transform.position, destination, pullSpeed * Time.deltaTime);
} else {
nodes[i - 1].transform.position = positions[0];
nodes[i - 1].transform.rotation = nodes[0].transform.rotation;
NewSnakeMovement bodyMovement = nodes[i - 1].GetComponent<NewSnakeMovement>();
bodyMovement.resetTarget(positions[0]);
if(i == positions.Length - 1 && nodes[i - 1].transform.position == positions[0]) {
finished = true;
Logger.Log("######################### Pull completed ###########################");
}
}
}
yield return null;
lastResetTime += Time.deltaTime;
}
GameObjectAccessor.instance.getSnakeData().decrementLengthOnEvolution();
incrementScale();
// start countdown
GamePlayScreen.instance.go();
}
// This is the coroutine to increment length
IEnumerator incrementLengthCoroutine(int value) {
Logger.Log("Starting increment coroutine");
GameObject tail = null;
if (mSnakeNodes.Count > 1) {
tail = mSnakeNodes[mSnakeNodes.Count - 1];
GameObject tailParent = mSnakeNodes[mSnakeNodes.Count - 2];
mSnakeNodes.RemoveAt(mSnakeNodes.Count - 1);
tailParent.GetComponent<NewSnakeMovement>().ChildNode = null;
}
if(tail != null) {
// tail.GetComponent<NewSnakeMovement>().pauseForTime((value) * mSnakeMovement.getTimePerGrid());
tail.GetComponent<NewSnakeMovement>().pause();
}
for(int i = 0; i < value; i++) {
GameObject tailParent = mSnakeNodes[mSnakeNodes.Count - 1];
// get the body from pool
GameObject body = SnakeBodyPooler.instance.getObjectFromPool();
body.transform.position = tailParent.transform.position;
body.transform.rotation = tailParent.transform.rotation;
body.transform.localScale = tailParent.transform.localScale;
body.SetActive(true);
body.GetComponent<NewSnakeMovement>().pauseForTime((i) * mSnakeMovement.getTimePerGrid());
addSnakeNode(body);
yield return new WaitForEndOfFrame();
}
addSnakeNode (tail);
tail.GetComponent<NewSnakeMovement>().unPause();
Logger.Log("Ending increment coroutine");
}
// adds a node to the list
public void addSnakeNode(GameObject node) {
if(mSnakeNodes.Count > 0) {
GameObject lastNode = mSnakeNodes[mSnakeNodes.Count - 1];
NewSnakeMovement move = lastNode.GetComponent<NewSnakeMovement>();
move.ChildNode = node;
move.BodyIndex = mSnakeNodes.Count;
}
mSnakeNodes.Add(node);
}
If I change the increment length coroutine to be a simple for loop that runs in one frame then i dont face this problem. Also, its the Pull coroutine that hangs, and once it does it does NOT even log the "Starting Pull" statement at the start of the while loop. So its not even going into an infinite loop. Let me know if anyone spots a major flaw with this code. I have been looking at it for hours and my mind maybe just skipping over something obvious. Any suggestions to modify this approach are welcomed too. Thanks in advance.
An update, it does not actually hang unity because all the other elements of my game are still running. Its just that the Pull coroutine does not complete execution and i think the problem might be I am saving the positions of nodes in an array when the Pull coroutine starts and I only loop through this positions array. So during execution if new nodes are added to the node list, the positions array does not get updated and this causes the if(i == positions.Length - 1 && nodes[i - 1].transform.position == positions[0]) { finished = true; Logger.Log("######################### Pull completed ###########################");
ter$$anonymous$$ating check to never hit. The last node now was never pulled to position[0] since it never got added in the positions array. I think this is the problem and need to fix this logic. Still doesn't explain why it does not log the statement on the start of the while loop if it never ter$$anonymous$$ates!
Answer by lassade · Jan 14, 2016 at 10:02 PM
Based ona quick look at your code a have some suggestions:
You need to update the length of the positions vector every time you add more snake nodes. If you dont only a few nodes of the frist nodes will be moved. (if you want to run the coroutine like a second Update function)
I not sure about the code that calls this coroutines but, both coroutines have ends. What i mean is by the time the finished variable in Pull is set to true the coroutine will end since it will reach the end of the function and will only start again if you call StartCoroutine(Pull())
One common mistake is disable de gameObject (or script) that holds some coroutine, this will pause the coroutine (not sure if is the case in the .unPause(); and .pause(); functions).
It was indeed the issue with not updating the positions array. Thanks for the heads up on points 2 and 3 but those were already taken care of.
Answer by wgt_jimmy · Jan 15, 2016 at 05:44 PM
How about only taking the last node and putting it at the head of the list in the new position it needs to be?
Your answer

Follow this Question
Related Questions
Coroutine That was working previously not working in scene 0 Answers
Multiple coroutine exicution order 1 Answer
StopAllCoroutines not working 0 Answers
Unity hangs when trying to load an image from url using the WWW class 1 Answer
Coroutine only fires once instead of looping until stopped. 2 Answers