We are going to add an AppStateManager to the project. Let's begin by making the object and then write the component script:
- In the root of the Hierarchy, create an empty game object, and name it AppStateManager.
-
In the Scripts folder create a new C# script named AppStateManager and drag it onto AppStateManager as a component.
Open the AppStateManager script for editing, as follows:
File: AppStateManager.cs
First, we'll declare the appState and a large set of public variables that will be explained as we use them:
using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using Vuforia; public class AppStateManager : MonoBehaviour { private AppStates appState; //Vuforia scripts that are used to get the state of the app. Can be found using FindObjectOfType<T>(); public ReconstructionBehaviour reconstructionBehaviour; public SurfaceBehaviour surfaceBehaviour; public ImageTrackableEventHandler imageTarget; ///UI public GameObject instructionHolder; public Text instructionsText; public Button doneButton; public Button resetButton; //For the game public UnityEvent OnStartGame; //UI resources for instructions public string pointDeviceText; public string pullBackText; }
The Start() function will register a listener for when tracking is first detected and set our state to INIT_ANIMATION:
void Start() { imageTarget.OnImageTrackableFoundFirstTime.AddListener( OnImageTrackableFoundFirstTime); } private void OnImageTrackableFoundFirstTime() { appState = AppStates.INIT_ANIMATION; }
The bulk of our state management is performed in Update(). The update action depends on the current state, where we use a big switch statement. This code directly follows the state definitions specified earlier in this section's User experience and app states topic. Begin writing the Update as follows:
void Update() { //We declare the bool values here because we want them to be set to false, unless the state is correct //This saves us from setting the values to false in each state. bool showDoneButton = false; bool showResetButton = false;
Now, we begin the big switch statement:
switch (appState) { //Detection phase case AppStates.OVERLAY_OUTLINE: instructionsText.text = pointDeviceText; surfaceBehaviour.GetComponent<Renderer>().enabled = false; break; // The animation that is played when the trackable is found for the first time case AppStates.INIT_ANIMATION: appState = AppStates.SCANNING; break; // Scanning phase case AppStates.SCANNING: ShowWireFrame(true); instructionsText.text = pullBackText; showDoneButton = true; break; // When the user taps done. This happens before the game can be played case AppStates.GAME_RENDERING: if ((reconstructionBehaviour != null) && (reconstructionBehaviour.Reconstruction != null)) { ShowWireFrame(false); surfaceBehaviour.GetComponent<Renderer>().enabled = false; imageTarget.ToggleOnStateChange = true; reconstructionBehaviour.Reconstruction.Stop(); OnStartGame.Invoke(); appState = AppStates.GAME_PLAY; } break; //This is where the user can shoot the ball case AppStates.GAME_PLAY: instructionHolder.gameObject.SetActive(false); showResetButton = true; break; //User taps on [RESET] button - Re-loads the level case AppStates.RESET_ALL: //Reloads this scene UnityEngine.SceneManagement.SceneManager.LoadScene(0); appState = AppStates.NONE; break; // Just a placeholder state, to make sure that the previous state runs for just one frame. case AppStates.NONE: break; }
After the switch statement, we include logic to decide when to display the done or cancel buttons, as follows:
if (doneButton != null && showDoneButton != doneButton.enabled) { doneButton.enabled = showDoneButton; doneButton.image.enabled = showDoneButton; doneButton.gameObject.SetActive(showDoneButton); } if (resetButton != null && showResetButton != resetButton.enabled) { resetButton.enabled = showResetButton; resetButton.image.enabled = showResetButton; resetButton.gameObject.SetActive(showResetButton); } }
That's the end if the Update function. It calls some helper functions as defined next, as follows:
void ShowWireFrame(bool show) { WireframeBehaviour[] wireframeBehaviours = FindObjectsOfType<WireframeBehaviour>(); foreach (WireframeBehaviour wireframeBehaviour in wireframeBehaviours) { wireframeBehaviour.ShowLines = show; } } //Called by the buttons public void TerrainDone() { appState = AppStates.GAME_RENDERING; } public void ResetAll() { appState = AppStates.RESET_ALL; }
I know this is a lot of code and I hope you don't mind following it, let alone typing it into your own app, but when you break it down, each section is just a few lines of code.