I’m working on a simulator projet that has many stages, the execution can be in one and only one stage at a time.
I created a coroutine for every stage and created a SwitchManager that switches between stages (coroutines) as needed.
my problem is described in detail as a comment in the right place in the code.
Here’s an example code:
StageInfo.cs
using UnityEngine;
using System.Collections;
public class StageInfo
{
public bool status;
public IEnumerator Functionality;
public StageInfo(bool status, IEnumerator functionality)
{
this.status = status;
this.Functionality = functionality;
}
}
Main.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Main : Singelton<Main>
{
// fields...
public Machine machine;
public AudioSource audioSource;
public NewPlayer player;
public checklistOne, checklistTwo, checklistThree;
public enum Stage
{
Overview,
ShowChecklistOne,
TeleportToPositionOne,
MoveCradle,
OpenArms,
RaiseArms,
MoveSpool,
LowerArms,
TeleportToPositionFour,
None,
}
public SortedDictionary<Stage, StageInfo> stageBox; // the data structure that stores the stages with their status and their coroutines!
private Stage currentStage;
private IEnumerator currentRoutine;
public Stage CurrentStage // it lunches the suitable coroutine when you assign a new state
{
get { return currentStage; }
set
{
currentStage = value;
if (currentRoutine != null)
StopCoroutine(currentRoutine);
currentRoutine = stageBox[currentStage].Functionality;
StartCoroutine(currentRoutine);
}
}
// methods & coroutines...
private new void Awake()
{
// initializing stageBox:
stageBox = new SortedDictionary<Stage, StageInfo>()
{
{Stage.Overview, new StageInfo(false, Overview()) },
{Stage.ShowChecklistOne, new StageInfo(false, ShowChecklistOne()) },
{Stage.TeleportToPositionOne, new StageInfo(false, TeleportToPositionOne()) },
{Stage.MoveCradle, new StageInfo(false, MoveCradle()) },
{Stage.OpenArms, new StageInfo(false, OpenArms()) },
{Stage.RaiseArms, new StageInfo(false, RaiseArms()) },
{Stage.MoveSpool, new StageInfo(false, MoveSpool()) },
{Stage.LowerArms, new StageInfo(false, LowerArms()) },
{Stage.TeleportToPositionFour, new StageInfo(false, TeleportToPositionFour()) },
{Stage.None, new StageInfo(false, null )},
};
}
private void Start()
{
CurrentStage = Stage.Overview; /*************** ENTRY POINT ***************/
// this is a property with Set statement that handles coroutine stopping and starting
}
// this method handle the Switch between the different coroutines by stoping one and starting another.
private void ManageSwitch()
{
// Switch to the first Stage which status is false
foreach (Stage s in stageBox.Keys)
{
if (stageBox~~.status == false)~~
{
print("Picked Stage: " + s);
CurrentStage = s;
return;
}
}
// if all stages are done go to Stage.None:
currentStage = Stage.None;
}
/************************************************************************
* *
* STAGES’ COROUTINES *
* *
************************************************************************/
private IEnumerator Overview()
{
print(“START Overview”);
Highlight.Instance.list.Clear();
yield return player.FadeIn();
stageBox[Stage.Overview].status = true;
print(“END Overview”);
ManageSwitch();
}
private IEnumerator ShowChecklistOne()
{
print(“START ShowChecklistOne”);
Highlight.Instance.list.Clear();
yield return checklistOne.CreateChecklist(new int[] { 0, 1, 2, 3, 4, 38, 39, 40 });
StartCoroutine(Environment.Instance.ShowMoteAfter(5, checklistOne.moteLocation)); // showing mote after 5 seconds if user didn’t interact
while (true) // waiting for user interaction to move checklist to destionation
{
if (Interaction.Instance.GetDown(Interaction.InputButton.Select))
{
yield return checklistOne.MoveToDestination();
stageBox[Stage.ShowChecklistOne].status = true;
yield return Environment.Instance.HideMoteAndStopShowAfterRoutine();
print(“END ShowChecklistOne”);
break;
}
else
yield return null;
}
ManageSwitch();
}
private IEnumerator TeleportToPositionOne()
{
print(“START TeleportToPisitionOne”);
Highlight.Instance.list.Clear();
yield return Environment.Instance.ShowFootPrint(Environment.Instance.footPrintLocations[1]);
StartCoroutine(Environment.Instance.ShowMoteAfter(5, Environment.Instance.moteLocations[1])); // showing mote after 5 seconds if user didn’t interact
while (true)
{
if (Interaction.Instance.GetDown(Interaction.InputButton.Select))
{
yield return player.TeleportTo(1);
stageBox[Stage.TeleportToPositionOne].status = true;
// hidiing Environment objects:
Environment.Instance.HideFootPrint();
yield return Environment.Instance.HideMoteAndStopShowAfterRoutine();
print(“END TeleportToPositionOne”);
break;
}
else
yield return null;
}
ManageSwitch();
}
private IEnumerator MoveCradle()
{
print(“START MoveCradle”);
machine.cradleHologram.SetActive(true); // showing hologram
Highlight.Instance.Unhighlight();
Highlight.Instance.list.Add(machine.cradle.gameObject); // highlighting mainConsole’s buttons & the cradle:
foreach (Button button in machine.mainConsole.buttonsArray)
Highlight.Instance.list.Add(button.gameObject);
Highlight.Instance.HighlightObjects();
while (true)
{
machine.mainConsole.ShowElementOnButton(machine.mainConsole.cradleRightButton, 0); // showing text
Interaction.Instance.EnableMainConsole(true); // Enabling interaction for MainConsole
if (machine.cradle.isInRightDestination) // success…
{
machine.cradleHologram.SetActive(false);
machine.mainConsole.RemoveElementAndMarker();
Highlight.Instance.Unhighlight();
checklistOne.CheckElement(0);
stageBox[Stage.MoveCradle].status = true;
print(“END MoveCradle”);
break;
}
else
yield return null;
}
ManageSwitch();
}
private IEnumerator OpenArms()
{
print(" START OpenArms");
while (true)
{
if (!CheckMoveCradle())
// the problem is when CheckMoveCradle() returns false…
/* CheckMoveCradle will turn stageBox[Stage.MoveCradle].status to
* false and return false.
* That breaks the while loop and call the ManageSwitch() which in
* turn will stop the current running coroutine and switch
* the execution to MoveCradle coroutine. HERE’S MY PROBLEM!!!
* (note: by this time, MoveCradle() coroutine is being executed
* for the second time, not first!).
* When StartCoroutine(MoveCradle()) is called from inside
* ManageSwitch(), it doesn’t start a new instance of the coroutine,
* or start executing it from beginning (as I understand it should do!!!),
* instead it just enter the coroutne at its last line, do nothing but
* return to the
* caller method (ManageSwitch()) even though there’s no Yield the the end.
* My Question is why doesn’t it start executing MoveCradle()
* coroutine from beginning?!
*/
break;
if (machine.armRig.armsDistance >= machine.armRig.warningDistance) // success…
{
machine.armRig.leftArmHologram.SetActive(false); // hiding holograms
machine.armRig.rightArmHologram.SetActive(false);
machine.mainConsole.RemoveElementAndMarker();
Highlight.Instance.Unhighlight();
checklistOne.CheckElement(1);
stageBox[Stage.OpenArms].status = true;
print(“END OpenArms”);
break;
}
else
yield return null;
}
ManageSwitch();
}
/************************************************************************
* *
* Check methods *
* *
************************************************************************/
private bool CheckMoveCradle()
{
if (machine.cradle.isInRightDestination)
{
stageBox[Stage.MoveCradle].status = true;
return true;
}
else
{
stageBox[Stage.MoveCradle].status = false;
checklistOne.UncheckElement(0);
return false;
}
}
}