LevelManager

You must customize this code to correspond to your mini-game logic

updated 4/27/2020 (Small changes to LoadLevel2, StartLevel2 to improve Level transition with ScreenFader. Player is disabled, Level2Objects aren't loaded until Button is pressed ) Assumes you are using: (If you choose not to use these, remove any code associated with these script components)

Also, Event has been named: onMiniGameOver, it was not consistent between the diagrams and the code. It is used in LevelManager and MiniGameState.

  • CameraFollow Script //remove from code if not using

  • ScreenFader Script //remove from code if not using

Includes:

  • timer method - reloadTimer( timeInSeconds )

  • GameObject[ ] gameObjectLayers - array of Level-specific Parent GameObjects for each level's features

  • Event Listeners:

    • GameData: onPlayerDataUpdate - Listener: CheckLevelEnd( )

    • PlayerController: onPlayerDied - Listener: ReloadMiniGame( )

    • PlayerController: onPlayerReachExit - Listener: NextLevel( )

  • Event Publisher onMiniGameOver - Listener: MiniGState

    You must add your custom code for Level3 You must modify MiniGState, and EndState to include logic for consequences for the miniGame.

Final Updates: 4/27/2020 - Not Required to Implement:

When play-testing, the reloading of the MiniGame does not actually reload the UnityScene, so it's necessary to create a new method: LoadLevel1( ) with additional logic added to StartLevel1( ). These are only relevant when play-testing, otherwise, when the using StateManager, the full Scene is reloaded, everything gets reset correctly.

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using System.Collections;
using UnityEngine.SceneManagement;

public class LevelManager : MonoBehaviour
{

    public enum LevelState
    {
        start,
        level1,
        level2,
        level3,
        lose, 
        win
    }

    public UnityEvent onMiniGameOver = new UnityEvent();  //broadcast to MiniGState

    LevelState curLevel; // FSM - 1 unit of memory

    int maxLevelScore = 30; //when to change levels, set in inspector

    //UI game Objects - LevelValue, StartGameButton, StartGamePanel
    [SerializeField]
    Button startGameButton;
    
    [SerializeField]
    Text startPanelText; // text on startGameButton
    
    [SerializeField]
    CanvasGroup startPanelCg; //if using an ResultsPanel
    
    [SerializeField]
    Text levelText;
    
    [SerializeField]
     Text timerText;
    
    [SerializeField] 
    Transform playerSpawnPoint;
    
    GameObject player;
    PlayerController playerController;
    ScreenFader fader;
    CameraFollow cameraFollow;
    //references to custom script components
    
    
    [SerializeField]
    GameObject[] gameObjectLayers;

    void Start()
    {
    //SETUP Event Listeners
        playerController = FindObjectOfType<PlayerController>(); //Assumes only 1 exists
        player = playerController.gameObject;
        playerController.onPlayerDied.AddListener(ReloadMiniGame);
        playerController.onPlayerReachExit.AddListener(NextLevel);
        
        GameData.instanceRef.onPlayerDataUpdate.AddListener(CheckLevelEnd);

        fader = FindObjectOfType<ScreenFader>();
        cameraFollow = FindObjectOfType<CameraFollow>();

        startGameButton.onClick.AddListener(NextLevel);
        
        gameObjectLayers[0].SetActive(true); //Level1 gameObjects, spawner, background, floor
        gameObjectLayers[1].SetActive(false); //Level1 gameObjects, spawner, background, floor
        gameObjectLayers[2].SetActive(false); //Level1 gameObjects, spawner, background, floor
        curLevel = LevelState.start;

        //show Start Panel at start of Scene.
        if (startPanelCg != null)
        {
            Utility.ShowCG(startPanelCg);
        }

    }

    /// <summary>
    /// Checks the level end.
    /// Checks the data to see if the level has ended
    /// </summary>
       public void CheckLevelEnd()
    {
        int levelScore = GameData.instanceRef.LevelScore;

        //Debug.Log("Check if level is over " + levelScore);

        //Add code here to see if health is <=0
        if (GameData.instanceRef.Health <= 0)
        {
            curLevel = LevelState.lose;
            MiniGameOver();
        }
        else if (levelScore >= maxLevelScore)
        { ///level has changed
          ///reset levelScore
            GameData.instanceRef.LevelScore = 0; //reset GameData.LevelScore
            NextLevel(); //go to next level - call FSM
        }

    }//end CheckLevelEnd

  //This method implements the Finite State Machine to Manage Level Logic.
    //You will modify this code to correspond to your game's logic
    //This method is always called when an event has occured to end the level
    //Event types: Score > LevelScore, health <= 0, Player falls, Player reaches exit
    public void NextLevel()
    {
        switch (curLevel) //check the curLevel, find matching case below
        {

            case LevelState.start: // called when StartPanel, StartGameButton is clicked
                curLevel = LevelState.level1; //change level
                StartLevel1();
                break;

            case LevelState.level1: //called when in Level1 from checkLevelEnd( )
                curLevel = LevelState.level2; //change level
                LoadLevel2();
                break;

            case LevelState.level2: //called when in Level2 from checkLevelEnd( )
                curLevel = LevelState.level3; //change level
                LoadLevel3();
                break;

            case LevelState.level3: //called when in Level3 from checkLevelEnd( )
                curLevel = LevelState.win;
                MiniGameOver();
                break;

            default:
                Debug.Log("No match on curLevel");
                break;
        } //end switch-case

    }//end NextLevel

    ////YOU WILL MODIFY THESE METHODS SO THEY CORRESPOND TO YOUR GAME"S LOGIC
  
    //NEW LOGIC ADDED For When reloading during play-testing
     void LoadLevel1()
    {
        player.SetActive(false);
        StopSpawner(LevelState.level3); //- needs changed for l3
        StopAllCoroutines(); //stop timer
                             // fader.FadeReset(); //fadeOut - fadeInstartGameButton.onClick.RemoveAllListeners();

        startGameButton.onClick.AddListener(StartLevel1); //- needs changed for l3
        startPanelText.text = "Start Level 1"; //-- needs changed for l3

        Utility.ShowCG(startPanelCg);  //show panel }
    }

    //expanded logic so this works better when reloading during play-testing
        void StartLevel1()
    {
        //STARTS Gameplay, Spawner, etc
        //Player in correct position already
        gameObjectLayers[2].SetActive(false); //level3 - needs changed for l3
        gameObjectLayers[0].SetActive(true); //level1 - needs changed for l3

        ResetPlayerPosition(); //move player to spawn point
        player.SetActive(true);
        StartSpawner(LevelState.level1);// -- needs changed for l3

        levelText.text = "Level 1"; //- needs changed for l3

        Utility.HideCG(startPanelCg); //hide panel
        StartCoroutine(reloadTimer(20));


    }
    
    

    //stop level1, prepare for starting level2
    void LoadLevel2()
    {
        player.SetActive(false);
        StopSpawner(LevelState.level1); //- needs changed for l3

        StopAllCoroutines(); //stop timer
        fader.FadeReset(); //fadeOut - fadeIn
        startGameButton.onClick.RemoveAllListeners();

        startGameButton.onClick.AddListener(StartLevel2); //- needs changed for l3
        startPanelText.text = "Start Level 2"; //-- needs changed for l3

        Utility.ShowCG(startPanelCg);  //show panel
    }

    //When "Start Level 2" button is selected
    public void StartLevel2()
    {
        gameObjectLayers[0].SetActive(false); //level1 - needs changed for l3
        gameObjectLayers[1].SetActive(true); //level2 - needs changed for l3

        ResetPlayerPosition(); //move player to spawn point
        player.SetActive(true);
        StartSpawner(LevelState.level2);// -- needs changed for l3

        levelText.text = "Level 2"; //- needs changed for l3

        Utility.HideCG(startPanelCg); //hide panel
        StartCoroutine(reloadTimer(20));  //change per level?
    }


    void LoadLevel3()
    {
        //add your code here
    }

    public void StartLevel3()
    {
       // add your code here
    }


    //What happens when the MiniGame is over due to winning?
    //How does a player leave the MiniGameScene?
   //What happens when the MiniGame is over due to winning?
    //How does a player leave the MiniGameScene: Win or Lose?
    void MiniGameOver()
    {
        if( curLevel == LevelState.win)
        {
            GameData.instanceRef.miniGameWinner = true;
        }
        else //must be LevelState.Lose
        {
            GameData.instanceRef.miniGameWinner = false;
        }
        //In all cases do the following
        if (onMiniGameEnd != null) //some listener (MiniGState)
        {
            onMiniGameOver.Invoke();
        }
        //invoke custom event to notify MiniGState where sceneChange logic an be executed.
    } //end MiniGameOver



    /// <summary>
    /// Starts the spawner.
    /// </summary>
    /// <param name="level">Level.</param>
    void StartSpawner( LevelState level)
    {
        int index = (int)level - 1; //array index is one less than level number
        Spawner spawner = gameObjectLayers[index].GetComponentInChildren<Spawner>();
        spawner.gameObject.SetActive(true);
        spawner.activeSpawning = true;
        spawner.StartSpawning(); //Make sure to remove this code from Start in the spawner script
    }

    /// <summary>
    /// Stops the spawner.
    /// </summary>
    /// <param name="level">Level.</param>
    void StopSpawner(LevelState level)
    {
        int index = (int)level - 1; //array index is one less than level number
        Spawner spawner = gameObjectLayers[index].GetComponentInChildren<Spawner>();
        spawner.gameObject.SetActive(false);
        spawner.StopAllSpawning(); 
    }


    //moves player to left-edge when new level loaded
    public void ResetPlayerPosition()
    {   
        player.transform.localPosition = playerSpawnPoint.transform.localPosition;
        cameraFollow.ResetPosition();
    }

    /// <summary>
    /// Reloads the Scene when the timer runs out
    /// </summary>
    /// <returns>The timer.</returns>
    /// <param name="reloadTimeInSeconds">Reload time in seconds.</param>
    IEnumerator reloadTimer(float reloadTimeInSeconds)
    {
        float counter = 0;

        while (counter < reloadTimeInSeconds)
        {
            counter += Time.deltaTime;
            timerText.text = "Timer: " + Mathf.RoundToInt(counter).ToString() + " of " + Mathf.RoundToInt(reloadTimeInSeconds).ToString(); ;
            yield return null;
        }
        //Timer is over - if the timer runs out
        ReloadMiniGame();
    }

    /// <summary>
    /// 
    /// </summary>
     public void ReloadMiniGame()
    {
        //ADD NEW METHOD TO RESET ALL GameObjects for Restart
        LoadLevel1();

        GameData.instanceRef.ResetMiniGameData();

        Scene curScene = SceneManager.GetActiveScene();
        fader.EndScene(curScene.buildIndex);

        if (StateManager.instanceRef != null)
        {
            StateManager.instanceRef.SwitchState(GameScene.MiniGameScene);  //Going to 
        }
        else
        {
            Debug.Log("Play Testing MiniGame - no StateManager so can't switch scene");
            //Use Unity to Reload the scene
            SceneManager.LoadScene(curScene.buildIndex);
            //or Create method to LoadLevel1, reset all things or use new LoadLevel1() method
            //LoadLevel1();
        }
    } //end ReloadMiniGame

}// end class

Last updated