Search Unity

How to store a value before it's changed?

Discussion in 'Scripting' started by Kensei, Sep 12, 2014.

  1. Kensei

    Kensei

    Joined:
    Apr 26, 2013
    Posts:
    63
    Hi guys, I have a game with several states, and I need to store a PreviusState before I change the CurrentState. I'd like to know if there's a way to do that without manually assigning values. Here's the code I wrote in case it's confusing:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class GameController : MonoBehaviour
    5. {
    6.     public static float score = 0;
    7. //============================================
    8.     //Declare public variables.
    9.     public  float minX = -5.5f;
    10.     public  float maxX = 5.5f;
    11.     public float cash = 1000f;
    12.     public float timeLeft = 0f;
    13.     //Declare private variables.
    14.     private bool stateChanged = false;
    15. //============================================
    16.     //Declare propertiesl.
    17.     public GameState PreviousState {get;set;}
    18.     public GameState CurrentState {get;set;}
    19. //============================================
    20.     public enum GameState
    21.     {
    22.         AwaitingInput,
    23.         Paused,
    24.         Playing,
    25.         Turbo,
    26.         GameOver
    27.     }
    28.    
    29.     void Start ()
    30.     {
    31.         CurrentState = GameState.Playing;
    32.  
    33.     }
    34.    
    35.  
    36.     void Update ()
    37.     {
    38.         DefineGameState();
    39.        
    40.         if(timeLeft > 0 && CurrentState == GameState.Playing)
    41.         {
    42.             CurrentState = GameState.Turbo;
    43.             StartCoroutine(TurboTimer());
    44.         }
    45.     }
    46.  
    47.  
    48.     private void DefineGameState()                                // Change the timeScale depending on the current state and lock/unlock the cursor.
    49.     {
    50.         if (CurrentState == GameState.Paused || CurrentState == GameState.GameOver || CurrentState == GameState.AwaitingInput)
    51.         {
    52.             Time.timeScale = 0;
    53.             Screen.showCursor = true;
    54.             Screen.lockCursor = false;
    55.         }
    56.         else if (CurrentState == GameState.Turbo)
    57.         {
    58.             Time.timeScale = 1.5f;
    59.             Screen.showCursor = false;
    60.             Screen.lockCursor = true;
    61.         }
    62.         else if (CurrentState == GameState.Playing)
    63.         {
    64.             Time.timeScale = 1;
    65.             Screen.showCursor = false;
    66.             Screen.lockCursor = true;
    67.         }
    68.     }      
    69.     // Clamp the cash value to 0 and end the game if player runs out.
    70.     private void CashMonitor ()                                          
    71.     {
    72.         if (cash <= 0)
    73.         {
    74.             cash = 0;
    75.             CurrentState = GameState.GameOver;
    76.         }
    77.     }
    78.     private IEnumerator TurboTimer()                                  
    79.     {
    80.         while (timeLeft > 0f)
    81.         {  
    82.             // Activate Turbo mode and start countdown.
    83.             yield return new WaitForSeconds(1f);                  
    84.             timeLeft -= 1f;
    85.             Debug.Log ("Turbo started, time left:" + timeLeft);
    86.         }
    87.         if (timeLeft == 0)
    88.         {
    89.             CurrentState = PreviousState;
    90.         }
    91.     }
    92. }
    Basically when the player collects a certain item, another game mode will begin and a timer will start countdown. I need it to store the PreviousState so that when I pause and unpause I can return to the default or turbo modes respectively. I tried setting PreviousState in the setter of the CurrentState so that the assignment is done right before CurrentState is changed, but then I ran into the same problem I have with my current manual setup.

    I set my GUI so that when the player presses Escape, the gamestate is changed to Paused, and I made it to assign the current state to the previous state before that. Problem is that if the player presses escape twice, PreviousState becomes paused as well and I'm busted.

    TLDR: Is there a method that intercepts a poll and returns true, so that I can do something right before a value is changed?
     
  2. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    Sounds like you want a stack of states. Something like:

    Code (csharp):
    1. using System.Collections.Generic;
    2.  
    3. private Stack<GameState> stack = new Stack<GameState>();
    4.  
    5. public GameState CurrentState { get { return stack.Peek(); } }
    6.  
    7. public void PushState(GameState state) { stack.Push(state); }
    8.  
    9. public void PopState() { stack.Pop(); }
    When the player collects the item, push your new game state onto the stack:
    Code (csharp):
    1. GameController.PushState(myNewGameState);
    When the player pauses, push the pause state on top of that:
    Code (csharp):
    1. GameController.PushState(myPauseState);
    When the player exits a state such as pause:
    Code (csharp):
    1. GameController.PopState();
     
    StarManta, ThermalFusion and Kensei like this.
  3. Kensei

    Kensei

    Joined:
    Apr 26, 2013
    Posts:
    63
    Aww man it had to be one of the things I suck at...queues and stacks -.- Thanks.

    Edit: Nevermind...it was uber simple lol :D I somehow thought they were complicated. Thanks again bro.
     
    Last edited: Sep 12, 2014
  4. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    No problem. Glad I could help!
     
  5. IsGreen

    IsGreen

    Joined:
    Jan 17, 2014
    Posts:
    206
    Create your custom class to store last value.

    Code (CSharp):
    1. public class SaveLastValue<T> {
    2.  
    3.     T data = default(T);
    4.     T last = default(T);
    5.  
    6.     public SaveLastValue(){}
    7.     public SaveLastValue(T value){ data=value; }
    8.     public T Current{ get{ return data; } }
    9.     public T Last{ get{ return last; } }
    10.     public void Set(T value){ last=data; data=value; }
    11.     public static implicit operator T(SaveLastValue<T> d){ return d.Current; }
    12.  
    13. }
    Script example:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Data : MonoBehaviour{
    4.  
    5.     SaveLastValue<int> integer = new SaveLastValue<int>();
    6.  
    7.     void Start(){
    8.  
    9.         Debug.Log("Current: "+integer.Current+"  Last: "+integer.Last);
    10.         integer.Set(1);
    11.         Debug.Log("Current: "+integer.Current+"  Last: "+integer.Last);
    12.         integer.Set(10);
    13.         Debug.Log("Current: "+integer.Current+"  Last: "+integer.Last);
    14.         integer.Set(20);
    15.         Debug.Log("Current: "+integer.Current+"  Last: "+integer.Last);
    16.  
    17.     }
    18.  
    19. }
     
    Kensei likes this.
  6. Kensei

    Kensei

    Joined:
    Apr 26, 2013
    Posts:
    63
    Tried your example, did some tweaking but it also worked. I have to say it's a bit overkill for my needs but thank you very much anyway :)
     
  7. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Very nice! In that situation, I'd have probably used a coroutine to store the previous state and then set it back at the end, but I'd never considered a stack for this purpose. Very elegant.
     
  8. Kensei

    Kensei

    Joined:
    Apr 26, 2013
    Posts:
    63
    That was my initial approach, but it's not very flexible and I only managed to get it working with with only 2 states.