.Net Code Monkey RSS 2.0
 Thursday, January 29, 2015

Game State Engine for Unity3D - Part 4 - Subscribing to events

Following on from Part 3  where we extended the GameSceneManger to use the rule engine and raise events before and after a scene change, in this part we will make the subscribe state manager subscribe to those events and affect what happens when an invalid scene change is made.

Before we do this though I want to refactor the State class to break it into a static "State" class and break out the instance to a "GameManager" object.

GameManager

So the game manager will be responsible for all game state and will also be responsible for creating and holding a reference to the SceneManager.
/// <summary>
/// Responsible for managing the game's state
/// </summary>
public class GameManager : MonoBehaviour
{
    #region MyRegion

    /// <summary>
    /// Gets or sets the game scene manager.
    /// </summary>
    internal GameSceneManager SceneManager { get; set; }

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="GameManager"/> class.
    /// </summary>
    public GameManager()
    {
        // Create a new SceneManager and give it to the instance to use
        SceneManager = CreateSceneManager();
    }

    #endregion

    #region Methods

    /// <summary>
    /// Creates the scene manager.
    /// </summary>
    /// <returns></returns>
    private static GameSceneManager CreateSceneManager()
    {
        // Create a new rule engine
        RuleEngine<GameSceneTransition> sceneTransitionRuleEngine = new RuleEngine<GameSceneTransition>();

        // Add the rules
        AddGameSceneTransitionRules(sceneTransitionRuleEngine);

        // Create the scene manager with the rule engine
        return new GameSceneManager(sceneTransitionRuleEngine);
    }

    /// <summary>
    /// Adds the game scene transition rules to the specifed rule engine.
    /// </summary>
    /// <param name="sceneTransitionRuleEngine">The game scene rule engine to add rules to.</param>
    private static void AddGameSceneTransitionRules(RuleEngine<GameSceneTransition> sceneTransitionRuleEngine)
    {
        // Create the game scene transition
        GameSceneTransition nullToIntro = new GameSceneTransition(GameSceneIdentifier.Null, GameSceneIdentifier.Intro);
        GameSceneTransition introToMainMenu = new GameSceneTransition(GameSceneIdentifier.Intro, GameSceneIdentifier.MainMenu);
        GameSceneTransition mainMenuToNewGame = new GameSceneTransition(GameSceneIdentifier.MainMenu, GameSceneIdentifier.NewGame);
        GameSceneTransition mainMenuToLoadGame = new GameSceneTransition(GameSceneIdentifier.MainMenu, GameSceneIdentifier.LoadGame);
        GameSceneTransition mainMenuToPlayGame = new GameSceneTransition(GameSceneIdentifier.MainMenu, GameSceneIdentifier.PlayGame);
        GameSceneTransition mainMenuToSaveGame = new GameSceneTransition(GameSceneIdentifier.MainMenu, GameSceneIdentifier.SaveGame);
        GameSceneTransition mainMenuToQuitGame = new GameSceneTransition(GameSceneIdentifier.MainMenu, GameSceneIdentifier.QuitGame);

        // Create the rules
        var transitionNullToIntro = new GameSceneTransitionRule(nullToIntro);
        var transitionIntroToMainMenu = new GameSceneTransitionRule(introToMainMenu);
        var transitionMainMenuToNewGame = new GameSceneTransitionRule(mainMenuToNewGame);
        var transitionmainMenuToLoadGame = new GameSceneTransitionRule(mainMenuToLoadGame);
        var transitionmainMenuToPlayGame = new GameSceneTransitionRule(mainMenuToPlayGame);
        var transitionmainMenuToSaveGame = new GameSceneTransitionRule(mainMenuToSaveGame);
        var transitionmainMenuToQuitGame = new GameSceneTransitionRule(mainMenuToQuitGame);

        // Add the rules to the engine
        sceneTransitionRuleEngine.Add(transitionNullToIntro);
        sceneTransitionRuleEngine.Add(transitionIntroToMainMenu);
        sceneTransitionRuleEngine.Add(transitionMainMenuToNewGame);
        sceneTransitionRuleEngine.Add(transitionmainMenuToLoadGame);
        sceneTransitionRuleEngine.Add(transitionmainMenuToPlayGame);
        sceneTransitionRuleEngine.Add(transitionmainMenuToSaveGame);
        sceneTransitionRuleEngine.Add(transitionmainMenuToQuitGame);
    }

    #endregion
}

State

The state object is now only responsible for creating the GameManager and holding a Singleton instance of it.
/// <summary>
/// The State object follows a singleton pattern to ensure there is only ever
/// one instant of any of the StateManagers
/// </summary>
public class State : MonoBehaviour
{
    #region Fields

    /// <summary>
    /// Holds the static game manager instance.
    /// </summary>
    private static GameManager _gameManagerInstance = null;

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="State"/> class.
    /// </summary>
    protected State() { }

    #endregion

    #region Properties

    /// <summary>
    /// Gets a value indicating whether this <see cref="State"/> is active.
    /// </summary>
    /// <value><c>true</c> if is active; otherwise, <c>false</c>.</value>
    static public bool IsActive
    {
        get
        {
            return _gameManagerInstance != null;
        }
    }

    /// <summary>
    /// Gets the instance.
    /// </summary>
    /// <value>The instance.</value>
    public static GameManager GameManager
    {
        get
        {
            // First check if we have a reference to the instance has...
            if (State._gameManagerInstance == null)
            {
                // ... we dont, so see if we can get one if one exists...
                _gameManagerInstance = Object.FindObjectOfType(typeof(GameManager)) as GameManager;

                // ... check again if we now have an instance...
                if (_gameManagerInstance == null)
                {
                    _gameManagerInstance = GetNewGameManagerInstance();
                }
            }

            // Finally return the instance
            return State._gameManagerInstance;
        }
    }

    #endregion

    #region Methods

    /// <summary>
    /// Gets a new instance of the game amanger.
    /// </summary>
    /// <returns>
    /// The new instance.
    /// </returns>
    static GameManager GetNewGameManagerInstance()
    {
        // ... we still dont so we need to create one
        GameObject gameObject = new GameObject(StateManagerKeys.GameManger);

        // We need to ensure that the instance is not destroyed when
        // loading or changing to a new Scene
        DontDestroyOnLoad(gameObject);

        // Tie the instance of the GameManager to the game object
        _gameManagerInstance = gameObject.AddComponent<GameManager>();



        // Finally return the instance
        return _gameManagerInstance;
    }

    #endregion
}
Now we will look at getting the GameManager to subscribe to the GameSceneManager's events. The first task is to refactor the "CreateSceneManager" function in the GameSceneManager so that we split the creation of the GameSceneManager and the returning of the object onto two lines and inserting code to wire up the two event handlers in between. We will also remove the static modifiers from all methods as this is a hangover from when the methods and functions where in the static State class.
/// <summary>
/// Creates the scene manager.
/// </summary>
/// <returns></returns>
private GameSceneManager CreateSceneManager()
{
    // Create a new rule engine
    RuleEngine<GameSceneTransition> sceneTransitionRuleEngine = new RuleEngine<GameSceneTransition>();

    // Add the rules
    AddGameSceneTransitionRules(sceneTransitionRuleEngine);

    // Create the scene manager with the rule engine
    GameSceneManager gameSceneManager = new GameSceneManager(sceneTransitionRuleEngine);

    // Wire up the event handlers
    gameSceneManager.GameSceneChanged += gameSceneManager_GameSceneChanged;
    gameSceneManager.GameSceneChanging += gameSceneManager_GameSceneChanging;

    // return the scene manager
    return gameSceneManager;
}
We can now look at the handlers specifically. So taking the GameSceneChanging handler, what might we want to use this for? Well lets say for instance we have a boolean property on the GameSceneManager called "IsGameUnsaved" which is a flag which holds the game save state. If the game scene is about to be changed to NewGame, LoadGame or QuitGame and the player's game is unsaved, we may want to cancel the scene transition. You may have many methods which handle reasons to cancel a scene transition, so place calls to those methods here.
/// <summary>
/// Handles the GameSceneChanging event of the gameSceneManager control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="GameSceneChangingEventArgs"/> instance containing the event data.</param>
void gameSceneManager_GameSceneChanging(object sender, GameSceneChangingEventArgs e)
{
    CancelSceneTransitionIfUnsaved(e, IsGameUnsaved);
}

CancelSceneTransitionIfUnsaved

/// <summary>
/// Cancels the scene transition if the unsaved game state is true and 
/// the scene is about to be changed to a scene where the current game 
/// will be ended.
/// </summary>
/// <param name="e">
/// The <see cref="GameSceneChangingEventArgs"/> instance containing the event data.
/// </param>
/// <param name="gameIsUnsaved">
/// Set to <c>true</c> if the game is unsaved.
/// </param>
private void CancelSceneTransitionIfUnsaved(GameSceneChangingEventArgs e, bool gameIsUnsaved)
{
    // Check if the game is unsaved flag is set...
    if (gameIsUnsaved)
    {
        // ... is is so create a list of game scenes which are eligible for
        // checking if the game is unsaved before allowing the scene to change.
        List<GameSceneIdentifier> affectedGameScenes = new List<GameSceneIdentifier>
        {
            GameSceneIdentifier.LoadGame,
            GameSceneIdentifier.NewGame,
            GameSceneIdentifier.QuitGame
        };

        // Set the cancel flag if this list of eligible scenes contains 
        // the prospective scene
        e.Cancel = affectedGameScenes.Contains(e.ProspectiveSceneIdentifier);

        // You may want to extend the GameSceneChangingEventArgs event 
        // data class to include a property that can hold a cancel reason.
        // For example:
        // e.CancelReason = Resources.SceneCancelReasons.GameIsUnsaved;
    }
}
Before we move on to handling the GameSceneChanged event lets take a quick look another property which we exposed on the GameSceneManager. When we created the events one the GameSceneManager we also created property to hold a reference to a delegate method to allow a callback method to be called when the game scene transition fails. This was of type "GameSceneTransitionFailedDelegate" and was called "GameSceneTransitionFailedCallback". Lets slip back in to the GameManager's CreateSceneManager method and wire this up.
// Create the scene manager with the rule engine
GameSceneManager gameSceneManager = new GameSceneManager(sceneTransitionRuleEngine);

// Wire up the event handlers
gameSceneManager.GameSceneChanged += gameSceneManager_GameSceneChanged;
gameSceneManager.GameSceneChanging += gameSceneManager_GameSceneChanging;

// Wire up any callbacks
gameSceneManager.GameSceneTransitionFailedCallback = gameSceneManager_GameSceneTransitionFailedCallback;

// return the scene manager
return gameSceneManager;
We will then need to create a new method "gameSceneManager_GameSceneTransitionFailedCallback" which will be the call back method. For the moment we will just write the reason as debug information.

gameSceneManager_GameSceneTransitionFailedCallback

/// <summary>
/// The callback method which is called when the games scene manager 
/// acknowledges that a game scene transition has failed.
/// </summary>
/// <param name="reason">
/// Identifies the reason the scene transition failed.
/// </param>
private void gameSceneManager_GameSceneTransitionFailedCallback(string reason)
{
    // Handle the failure. Maybe display the message to the player?
    System.Diagnostics.Debug.WriteLine(reason);
}
If we need to perform any actions after the game scene has changed we can do this in the "gameSceneManager_GameSceneChanged" event handler.

Next

In Part 5 we will start plumbing all of this into the unity project and see how it works.

Previous Parts

Part 1
Part 2
Part 3

Full Code

The full code for this part can be found at the UnityStateManager repository.

Thursday, January 29, 2015 7:34:17 AM (GMT Standard Time, UTC+00:00)  #    Comments [0] -
C# | Game Scene Manager | Game State Manager | Unity3D
 Wednesday, January 28, 2015

Game State Engine for Unity3D - Part 3 - Extending Game Scene Manager to Use Rule Engine

Following on from Part 2 where we created the rules and conditions, in this article we are going to use those rules in the GameSceneManger to determine what are valid scene transitions.

We will add property which will hold a value which will identify what game scene is the current one. We will then add a method that takes a game scene identifier and uses it to set the current game state.
/// <summary>
/// Gets (or privately sets) the identity which indicates the current game scene.
/// </summary>
/// <value>The identity of the game scene.</value>
public GameSceneIdentifier GameSceneIdentifier { get; private set; }

/// <summary>
/// Sets the game scene identifier.
/// </summary>
/// <param name="gameSceneIdentifier">Game scene identifier.</param>
public void SetGameScene(GameSceneIdentifier gameSceneIdentifier)
{
    // First check if the specified game scene identifier differs from 
    // the current game scene identifier...
    if (this.GameSceneIdentifier != gameSceneIdentifier)
    {
        // ... it does so call a method to handle any actions we may
        // need to perform when the game scene identifier is changed.
        OnGameSceneChange(gameSceneIdentifier);
    }
}
The method first checks if the current identifier is not the same as the specified scene identifier as we do not yet know who much code will run when we change the scene. If the scenes differ we will go ahead and call a method which handles any actions which are needed to be performed when the game scene changes. So you may be thinking "How come we don't know how much code is going to be run when this method is called? Surely we do know this right?" Well, we probably have a fair idea, but this code is intended to be able to be used in many different games, and also we may want a concept of allowing other object to subscribe to some events  surrounding the actual game scene change process. The event subscribers may happen to run a lot of code so lets only raise these events if we really need to. So lets create a method which will handle changing the scene and raising the events.
/// <summary>
/// Called when the game scene needs to be changed
/// </summary>
/// <param name="newGameState">The identifier for the new game scene.</param>
private void OnGameSceneChange(GameSceneIdentifier newGameSceneIdentifier)
{
    // We will capture the 'Original' scene as we will need to know what 
    // it originally was after it has changed
    GameSceneIdentifier originalSceneIdentifier = this.GameSceneIdentifier;

    // Create the game scene transition we are about to make happen and 
    // use this in the rule engine to validate that the game scene 
    // transition is valid.
    GameSceneTransition sceneTransition =
        new GameSceneTransition(originalSceneIdentifier, newGameSceneIdentifier);
    RuleEngine.ActualValue = sceneTransition;
    bool isValidTransition = RuleEngine.MatchAll();

    // Check if the transition is valid...
    if (!isValidTransition)
    { 
        // It's not so do something... 
        //      Maybe throw an exception,?
        //      Raise and event?
        //      Just exit the method?
    }
Hmm.. So what should we do if the transition is not valid? Should we throw an exception? Should we raise an event, should we just bomb out of the method back to the calling code? Maybe we can let the calling code decide by adding a new property to the class with a delegate type which can hold reference to a callback method. We can check if this has been set and if it has we can call that before exiting the method.

GameSceneTransitionFailedDelegate

So first lest create a delegate that will define the method signature for this callback. It will have a single parameter which will contain the reason for the transition failure.
/// <summary>
/// Defines the expected signature for a method that should be called if a 
/// game scene transition fails.
/// </summary>
/// <param name="reason">
/// Identifies the reason the scene transition failed.
/// </param>
public delegate void GameSceneTransitionFailedDelegate(string reason);

GameSceneTransitionFailedReasons

We now need a resources file to hold the reason text. Normally I would use a proper .Net resources file to allow localization, however I am aware that Unity does not handle resources in the same way as the .Net framework and as it is not the scope of this project to discuss that I will just create a simple static class with constant fields.
/// <summary>
/// Encapsulates game scene transition failure resons
/// </summary>
internal static class GameSceneTransitionFailedReasons
{
    internal const string InvalidTransition = "The transition was invalid";
    internal const string CancelledTransition = "The transition was cancelled";
}
In the GameSceneManager we will add a property to hold a reference to the game transition failed callback.
/// <summary>
/// Sets the game scene transition failed callback. The parameter contains
/// </summary>
/// <value>
/// The call back to be called if the game scene transition failed.
/// </value>
public GameSceneTransitionFailedDelegate GameSceneTransitionFailedCallback { get; set; }
And in the OnGameSceneChange() method we can now check there is a callback method reference and if there is call the  method with the reason given as to why it was called.
// Check if the transition is valid...
if (!isValidTransition)
{
    // It's not so we will check the GameSceneTransitionFailedCallback
    // has a reference and call that with a parameter indicating the reason
    if (GameSceneTransitionFailedCallback != null)
    {
        GameSceneTransitionFailedCallback(GameSceneTransitionFailedReasons.InvalidTransition);
    }
}
So now we can advise the calling code that if the transition is invalid (according to the rule engine that the transition has failed.) What about if there is code elsewhere that needs to be aware of the game scene change? Ideally it would be useful to be able to notify code elsewhere that the change has happened or maybe more importantly is about happen and give this code a chance to cancel the change. For this we are going to need to events that can be subscribed to. One will be raised before the change happens and will allow cancellation of the change and the other will be raised after the change has happened.
 
So lets define in the GameSceneManager class the events that we will be raising.

/// <summary>
/// Occurs when the game scene has changed.
/// </summary>
public event GameSceneChangedHandler GameSceneChanged;

/// <summary>
/// Occurs when the game scene is changing.
/// </summary>
public event GameSceneChangingHandler GameSceneChanging;
This wont compile yet as we need to create the delegates that will define the signature of any method which will handle these events. So the two delegates we will add into the file where we define our delegates are:
/// <summary>
/// Defines the expected signature for a method that can handle when a game 
/// scene has changed.
/// </summary>
/// <param name="sender">Sender.</param>
/// <param name="e">The event data.</param>
public delegate void GameSceneChangedHandler(object sender, GameSceneChangedEventArgs e);

/// <summary>
/// Defines the expected signature for a method that can handle when a game 
/// scene is about to be changed.
/// </summary>
/// <param name="sender">Sender.</param>
/// <param name="e">The event data.</param>
public delegate void GameSceneChangingHandler(object sender, GameSceneChangingEventArgs e);
As you can see both delegates will allow the events to carry some data about the event, so lets create the classes which will carry the event data.

GameSceneChangedEventArgs

/// <summary>
/// Contains game scene changed event data.
/// </summary>
public class GameSceneChangedEventArgs
{
    #region Constructors

    /// <summary>
    /// Hides the default constructor for the <see cref="GameSceneChangedEventArgs"/> class.
    /// </summary>
    private GameSceneChangedEventArgs() { }

    /// <summary>
    /// Initializes a new instance of the <see cref="GameSceneChangedEventArgs"/> class.
    /// </summary>
    /// <param name="currentSceneIdentifier">
    /// Indicates the identifier of the current game scene after the change happened.
    /// </param>
    /// <param name="originalSceneIdentifier">
    /// Indicates the identifier of the original game scene before the change happened.
    /// </param>
    public GameSceneChangedEventArgs(GameSceneIdentifier currentSceneIdentifier,
        GameSceneIdentifier originalSceneIdentifier)
    {
        CurrentSceneIdentifier = currentSceneIdentifier;
        OriginalSceneIdentifier = originalSceneIdentifier;
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets or privately sets the identifier of the game scene after the change happened.
    /// </summary>
    /// <value>The identifier of the currnt scene.</value>
    public GameSceneIdentifier CurrentSceneIdentifier { get; private set; }

    /// <summary>
    /// Gets or privately sets the identifier of the game scene before the change happened.
    /// </summary>
    /// <value>The identifier of the original scene.</value>
    public GameSceneIdentifier OriginalSceneIdentifier { get; private set; }

    #endregion
}

GameSceneChangingEventArgs

/// <summary>
/// Contains game scene changing event data.
/// </summary>
public class GameSceneChangingEventArgs : EventArgs
{
    #region Constructors

    /// <summary>
    /// Hides the default constructor for the <see cref="GameSceneChangingEventArgs"/> class.
    /// </summary>
    private GameSceneChangingEventArgs() { }

    /// <summary>
    /// Initializes a new instance of the <see cref="GameSceneChangingEventArgs"/> class.
    /// </summary>
    /// <param name="currentSceneIdentifier">
    /// Indicates the identifier of the current scene before the scene change.
    /// </param>
    /// <param name="prospectiveSceneIdentifier">
    /// Indicates identifier of what the new scene will be after the change.
    /// </param>
    internal GameSceneChangingEventArgs(GameSceneIdentifier currentSceneIdentifier,
        GameSceneIdentifier prospectiveSceneIdentifier)
    {
        CurrentSceneIdentifier = currentSceneIdentifier;
        ProspectiveSceneIdentifier = prospectiveSceneIdentifier;
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets or sets a value indicating whether a request to cancel the scene change has been made.
    /// </summary>
    /// <value><c>true</c> if this instance cancel; otherwise, <c>false</c>.</value>
    public bool Cancel { get; set; }

    /// <summary>
    /// Gets or privately sets the identifier of the current game scene before the change happens.
    /// </summary>
    /// <value>
    /// The identifier of the current game scene before the change happens.
    /// </value>
    public GameSceneIdentifier CurrentSceneIdentifier { get; private set; }

    /// <summary>
    /// Gets or privately sets the identifier of the prospective game scene if the change is allowed to happen.
    /// </summary>
    /// <value>
    /// The identifier of the prospective game scene if the change is allowed to happen.
    /// </value>
    public GameSceneIdentifier ProspectiveSceneIdentifier { get; private set; }

    #endregion
}
Moving back to the GameSceneManager we will now create two methods which will be responsible for checking the presence of any attached event handlers and then raising these two events to the subscribers if required.
/// <summary>
/// Raises the game scene changed event.
/// </summary>
/// <param name="originalScene">Indicates the original scene.</param>
/// <param name="newGameScene">Indicates the new game scene.</param>
private void RaiseGameSceneChangedEvent(GameSceneIdentifier originalSceneIdentifier, 
    GameSceneIdentifier newGameSceneIdentifier)
{
    // See if we have any delgates attached that need to 
    // be called after we change game scene
    if (GameSceneChanged != null)
    {
        // Create new game scene changed event data
        GameSceneChangedEventArgs gameSceneChangedEventArgs =
            new GameSceneChangedEventArgs(newGameSceneIdentifier, originalSceneIdentifier);

        // Raise the event
        GameSceneChanged(this, gameSceneChangedEventArgs);
    }
}

/// <summary>
/// Raises the game scene changing event.
/// </summary>
/// <param name="originalScene">Indicates the original scene.</param>
/// <param name="newGameScene">Indicates the new game scene.</param>
/// <param name="cancelled">Set to true if the scene change was cancelled.</param>
private void RaiseGameSceneChangingEvent(GameSceneIdentifier originalSceneIdentifier, 
    GameSceneIdentifier newGameSceneIdentifier, ref bool cancelled)
{
    // First see if we have any delgates attached that need to 
    // be called before we change game scene
    if (GameSceneChanging != null)
    {
        // Create new game scene changing event data
        GameSceneChangingEventArgs gameSceneChangingEventArgs =
            new GameSceneChangingEventArgs(originalSceneIdentifier, newGameSceneIdentifier);

        // Raise the game scene changing event handler
        GameSceneChanging(this, gameSceneChangingEventArgs);

        // Set cancelled flag scene from the event args
        cancelled = gameSceneChangingEventArgs.Cancel;
    }
}
These two methods can now be called from OnGameSceneChange before and after (as applicable) the game scene property is actually changed. Full method is shown below.
/// <summary>
/// Called when the game scene needs to be changed
/// </summary>
/// <param name="newGameState">The identifier for the new game scene.</param>
private void OnGameSceneChange(GameSceneIdentifier newGameSceneIdentifier)
{
    // We will capture the 'Original' scene as we will need to know what 
    // it originally was after it has changed
    GameSceneIdentifier originalSceneIdentifier = this.GameSceneIdentifier;

    // Create the game scene transition we are about to make happen and 
    // use this in the rule engine to validate that the game scene 
    // transition is valid.
    GameSceneTransition sceneTransition =
        new GameSceneTransition(originalSceneIdentifier, newGameSceneIdentifier);
    RuleEngine.ActualValue = sceneTransition;
    bool isValidTransition = RuleEngine.MatchAll();

    // Check if the transition is valid...
    if (isValidTransition)
    {
        // It's not so we will check the GameSceneTransitionFailedCallback
        // has a reference and call that with a parameter indicating the reason
        if (GameSceneTransitionFailedCallback != null)
        {
            GameSceneTransitionFailedCallback(GameSceneTransitionFailedReasons.InvalidTransition);
        }
return;
} // As we intend to raise an event before the same scene is changed // in case any subscribers wish to cancel the scene change we need a // flag which we can use to signify cancellation. Initially we will // assume the game scene wont be cancelled... bool cancelled = false; // Raise the game scene changing event to give any subscribers chance // to cancel this process RaiseGameSceneChangingEvent(originalSceneIdentifier, newGameSceneIdentifier, ref cancelled); // Check to see if the event was cancelled... if (cancelled) { // ... it was so we will check the GameSceneTransitionFailedCallback // has a reference and call that with a parameter indicating the reason if (GameSceneTransitionFailedCallback != null) { GameSceneTransitionFailedCallback(GameSceneTransitionFailedReasons.CancelledTransition); }

return;
} // Getting here indicates that the event was not cancelled, so // we can actually set the game scene! this.GameSceneIdentifier = newGameSceneIdentifier; // Raise the game scene changed event to notify any listeners that // the game state has changed. RaiseGameSceneChangedEvent(originalSceneIdentifier, newGameSceneIdentifier); }

Next

In Part 4 we will look at wiring the events and failed transition callback up to the State object.

Previous Parts

Part 1
Part 2

Full Code

The full code for this part can be found at the UnityStateManager repository.



Wednesday, January 28, 2015 6:47:57 AM (GMT Standard Time, UTC+00:00)  #    Comments [0] -
C# | Game Scene Manager | Game State Manager | Unity3D
 Monday, January 26, 2015

Game State Engine for Unity3D - Part 2 - Scene Rules and Conditions

Following on from Part 1 where we created the static State class and SceneManager, in this article we are going to add some transition rules which the RuleManager will expose to the GameSceneManger to determine what are valid scene transitions.

So in the State class we will create a method called "AddGameSceneTransitionRules" which will take the RuleEngine with a "GameSceneTransition" type parameter. In this method we will create all of the transitions which we consider to be valid like Intro to Main Menu, main Menu to Save Game, etc.
/// <summary>
/// Adds the game scene transition rules to the specifed rule engine.
/// </summary>
/// <param name="sceneTransitionRuleEngine">The game scene rule engine to add rules to.</param>
private static void AddGameSceneTransitionRules(RuleEngine<GameSceneTransition> sceneTransitionRuleEngine)
{
    // Create the game scene transition
    GameSceneTransition nullToIntro = new GameSceneTransition(GameSceneIdentifier.Null, GameSceneIdentifier.Intro);
    GameSceneTransition introToMainMenu = new GameSceneTransition(GameSceneIdentifier.Intro, GameSceneIdentifier.MainMenu);
    GameSceneTransition mainMenuToNewGame = new GameSceneTransition(GameSceneIdentifier.MainMenu, GameSceneIdentifier.NewGame);
    GameSceneTransition mainMenuToLoadGame = new GameSceneTransition(GameSceneIdentifier.MainMenu, GameSceneIdentifier.LoadGame);
    GameSceneTransition mainMenuToPlayGame = new GameSceneTransition(GameSceneIdentifier.MainMenu, GameSceneIdentifier.PlayGame);
    GameSceneTransition mainMenuToSaveGame = new GameSceneTransition(GameSceneIdentifier.MainMenu, GameSceneIdentifier.SaveGame);
    GameSceneTransition mainMenuToQuitGame = new GameSceneTransition(GameSceneIdentifier.MainMenu, GameSceneIdentifier.QuitGame);

    // Create the rules
    var transitionNullToIntro = new GameSceneTransitionRule(nullToIntro);
    var transitionIntroToMainMenu = new GameSceneTransitionRule(introToMainMenu);
    var transitionMainMenuToNewGame = new GameSceneTransitionRule(mainMenuToNewGame);
    var transitionmainMenuToLoadGame = new GameSceneTransitionRule(mainMenuToLoadGame);
    var transitionmainMenuToPlayGame = new GameSceneTransitionRule(mainMenuToPlayGame);
    var transitionmainMenuToSaveGame = new GameSceneTransitionRule(mainMenuToSaveGame);
    var transitionmainMenuToQuitGame = new GameSceneTransitionRule(mainMenuToQuitGame);

    // Add the rules to the engine
    sceneTransitionRuleEngine.Add(transitionNullToIntro);
    sceneTransitionRuleEngine.Add(transitionIntroToMainMenu);
    sceneTransitionRuleEngine.Add(transitionMainMenuToNewGame);
    sceneTransitionRuleEngine.Add(transitionmainMenuToLoadGame);
    sceneTransitionRuleEngine.Add(transitionmainMenuToPlayGame);
    sceneTransitionRuleEngine.Add(transitionmainMenuToSaveGame);
    sceneTransitionRuleEngine.Add(transitionmainMenuToQuitGame);
}
Note: I refactored the name of the ruleEngine variable in the "State.CreatSceneManager()" method to "sceneTransitionRuleEngine" and added Loadgame to the "GameSceneIdentifier" enumeration.

The  game scene transition rule engine requires a class to represent a rule, and the rule will require a class to represent the conditions of the rule so lets create those as "GameSceneTransitionRule" and "GameSceneTransitionCondition".

GameSceneTransitionRule

/// <summary>
/// Represents a rule that must be met for game scene transition
/// </summary>
internal class GameSceneTransitionRule : BaseRule<GameSceneTransition>
{
    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="StateTransitionRule"/> class.
    /// </summary>
    /// <param name="threshold">The threshold.</param>
    /// <param name="actual">The actual.</param>
    public GameSceneTransitionRule(GameSceneTransition threshold)
        : base(threshold)
    {
        Initialize();
    }

    #endregion

    #region Methods

    /// <summary>
    /// Initializes the instance.
    /// </summary>
    public override void Initialize()
    {
        // Clear any existing conditions
        Conditions.Clear();

        // Create our conditions
        var condition1 = new GameSceneTransitionCondition(Threshold);

        // ...and add them to our collection of conditions
        Conditions.Add(condition1);
    }

    /// <summary>
    /// Matches the conditions.
    /// </summary>
    /// <returns></returns>
    public override bool MatchConditions()
    {
        return base.MatchAllConditions();
    }

    #endregion
}

GameSceneTransitionCondition


/// <summary>
/// Represents a condition that must be met for game scene transition
/// </summary>
internal class GameSceneTransitionCondition : BaseCondition<GameSceneTransition>
{
    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="GameSceneTransitionCondition"/> class.
    /// </summary>
    /// <param name="threshold">The threshold.</param>
    public GameSceneTransitionCondition(GameSceneTransition threshold)
        : base(threshold) { }

    #endregion

    #region ICondition<int> Members

    /// <summary>
    /// Determines whether this instance is satisfied.
    /// </summary>
    /// <returns></returns>
    public override bool IsSatisfied
    {
        get { return Value.Equals(Threshold); }
    }

    #endregion
}

Next

In Part 3 we will look at how to extend the "GameSceneManager" to use the rule engine and its rules

Full Code

The full code can be found at the UnityStateManager repository on my GitHub





Monday, January 26, 2015 7:14:39 AM (GMT Standard Time, UTC+00:00)  #    Comments [0] -
C# | Game Scene Manager | Game State Manager | Unity3D

Game State Engine for Unity3D - Part 1 - The Static State and Game Scene Manager

In this series of articles I am going to create a game state manager for use with Unity 3D. This will be built in C# .Net 3.5.

Reading such articles as THIS or  THIS have advised me to use a singleton patter for the game state engine. I am however going to veer of slightly and just have a state object that will use a singleton pattern. The instance will then hold hold references to instances of the SceneManager and any other managers which we require. So with this in mind, lets create a singleton state class. This will inherit from MonoBehaviour as it will be attached to a unity object later in the project.

State

The state object will follow the normal singleton pattern albeit with a slight twist in the code to work better with Unity3D. We will also add a property to hold a reference to a SceneManager and we will create a simple construction method to create this. This method may be extended a little later to add the scene change rules. We will put this in a "StateManager" folder in the Assets folder.

/// <summary>
  /// The State object follows a singleton pattern to ensure there is only ever 
  /// one instant of any of the StateManagers
  /// </summary>
  public class State : MonoBehaviour
  {
      #region Fields

      /// <summary>
      /// Holds the static instance.
      /// </summary>
      private static State _instance = null;

      #endregion

      #region Constructors

      /// <summary>
      /// Initializes a new instance of the <see cref="State"/> class.
      /// </summary>
      protected State() { }

      #endregion

      #region Properties

      /// <summary>
      /// Gets a value indicating whether this <see cref="State"/> is active.
      /// </summary>
      /// <value><c>true</c> if is active; otherwise, <c>false</c>.</value>
      static public bool IsActive
      {
          get
          {
              return _instance != null;
          }
      }

      /// <summary>
      /// Gets the instance.
      /// </summary>
      /// <value>The instance.</value>
      public static State Instance
      {
          get
          {
              // First check if we have a reference to the instance has...
              if (State._instance == null)
              {
                  // ... we dont, so see if we can get one if one exists... 
                  _instance = Object.FindObjectOfType(typeof(State)) as State;

                  // ... check again if we now have an instance...
                  if (_instance == null)
                  {
                      _instance = GetNewInstance();
                  }
              }

              // Finally return the instance
              return State._instance;
          }
      }

      /// <summary>
      /// Gets or sets the game scene manager.
      /// </summary>
      private GameSceneManager SceneManager { get; set; }

      #endregion

      #region Methods

      /// <summary>
      /// Gets a new instance.
      /// </summary>
      /// <returns>
      /// The new instance.
      /// </returns>
      static State GetNewInstance()
      {
          // ... we still dont so we need to create one
          GameObject gameObject = new GameObject(StateManagerKeys.GameManger);

          // We need to ensure that the instance is not destroyed when 
          // loading or changing to a new Scene
          DontDestroyOnLoad(gameObject);

          // Tie the instance of the GameManager to the game object
          _instance = gameObject.AddComponent<State>();

          // Create a new SceneManager and give it to the instance to use
          _instance.SceneManager = CreateSceneManager();

          // Finally return the instance
          return _instance;
      }

      /// <summary>
      /// Creates the scene manager.
      /// </summary>
      /// <returns></returns>
      private static GameSceneManager CreateSceneManager()
      {
           return new GameSceneManager();
      }

      #endregion
  }

GameSceneManager

The game scene manager will handle any scene transitions, say from "Intro" to "Main Menu", or from the "Play Game" scene to the "Quite Game" scene. For the manager to know what is a valid transition and what is not, say "Intro" to "Save Game" we need to have some rules, so for this I am going to use the Simple Rule Engine I created in a previous article.

However, this may not be the rule engine that we continue to use as the game development progresses, but the public members will likely remain the same so before we add the rule engine we need to define a contract which we want any rule engines to abide to. Thankfully the other project already has one "IRuleEngine" so we will compile that project, create a folder called Libraries in the Assets folder,  import the dll and reference it in this project. We can then use that interface. Thinking now I maybe should have called the rule engine in the other project "SimpleRuleEngine" so I could better differentiate if the need for a more complex rule engine comes along.

Moving back to the game scene manager we will create this class in a new folder called "SceneManager" and then add private property to hold the rule engine contract. It will only need to be private as it will not need to be exposed out side of this class. We will then create a constructor that will load the GameSceneManager with an object that abides by the IRuleEngine contract. While we are here we will hide the default constructor as we don't want this object instantiated without a rule engine.

/// <summary>
 /// Manages game scene transitions
 /// </summary>
 internal class GameSceneManager
 {
     #region Properties

     /// <summary>
     /// Gets or sets the rule engine.
     /// </summary>
     /// <value>
     /// The rule engine.
     /// </value>
     private IRuleEngine RuleEngine { get; set; }

     #endregion

     #region Constructors

     /// <summary>
     /// Prevents a default instance of the <see cref="GameSceneManager"/> class from being created.
     /// </summary>
     private GameSceneManager() { }

     /// <summary>
     /// Initializes a new instance of the <see cref="GameSceneManager"/> class.
     /// </summary>
     /// <param name="ruleEngine">The rule engine.</param>
     public GameSceneManager(IRuleEngine ruleEngine)
     {
         // Guard against a null RuleEngine
         if (ruleEngine == null) { throw new ArgumentNullException("ruleEngine");  }

         // Set the rule engine reference
         RuleEngine = ruleEngine;
     }

     #endregion
 }
So if we build the project we can see now that we need to modify the call to the constructor and pass in a rule engine object. We will use the engine from the other project but the engine requires a type so we'd better create one of those first. We will crate a GameSceneTransition object to represent the change from one Game Scene to another. This will be based strongly on the "GameStateTransition" from my simple rule engine article. We will add this into the SceneManager folder next to the GameSceneManager.

GameSceneTransition

  /// <summary>
  /// Represents the transition from one game scene to another.
  /// </summary>
  public class GameSceneTransition : IEquatable<GameSceneTransition>
  {
      #region Properties

      /// <summary>
      /// Gets (or privately sets) the GameState that can be transitioned from.
      /// </summary>
      /// <value>From.</value>
      public GameSceneIdentifier TransitionFrom { get; private set; }

      /// <summary>
      /// Gets (or privately sets) the GameState that can be transitioned to.
      /// </summary>
      /// <value>To.</value>
      public GameSceneIdentifier TransitionTo { get; private set; }

      #endregion

      #region Constructors

      /// <summary>
      /// Prevents a default instance of the <see cref="GameSceneTransition"/> class from being created.
      /// </summary>
      private GameSceneTransition() { }

      /// <summary>
      /// Initializes a new instance of the <see cref="GameSceneTransition"/> class.
      /// </summary>
      /// <param name="transitionFrom">The scene to transition from.</param>
      /// <param name="transitionTo">The scene to transition to.</param>
      public GameSceneTransition(GameSceneIdentifier transitionFrom, GameSceneIdentifier transitionTo)
      {
          TransitionFrom = transitionFrom;
          TransitionTo = transitionTo;
      }

      #endregion

      #region IEquatable<GameSceneTransition> Members

      /// <summary>
      /// Determines whether the specified <see cref="GameSceneTransition"/> is equal to the current <see cref="GameSceneTransition"/>.
      /// </summary>
      /// <param name="rule">The <see cref="GameSceneTransition"/> to compare with the current <see cref="GameSceneTransition"/>.</param>
      /// <returns><c>true</c> if the specified <see cref="GameSceneTransition"/> is equal to the current
      /// <see cref="GameSceneTransition"/>; otherwise, <c>false</c>.</returns>
      public bool Equals(GameSceneTransition gameSceneTransition)
      {
          return (TransitionFrom == gameSceneTransition.TransitionFrom) &&
                  (TransitionTo == gameSceneTransition.TransitionTo);
      }

      #endregion
  }
The GameSceneTransition uses a GameSceneIdentifier enumeration to state which scene is being transitioned to or from.

GameSceneIdentifier

This is an enumeration which represents all of the scenes which will occur in the game
 /// <summary>
 /// Represents all of the scenes which will occur in the game
 /// </summary>
 public enum GameSceneIdentifier
 {
     /// <summary>
     /// The scene state of null (Before the game is initialised).
     /// </summary>
     Null,

     /// <summary>
     /// The intro scene, where the splash screen could be shown.
     /// </summary>
     Intro,

     /// <summary>
     /// The scene where the player can select their profile.
     /// </summary>
     SelectProfile,

     /// <summary>
     /// The scene where the main menu should be displayed.
     /// </summary>
     MainMenu,

     /// <summary>
     /// The scene where a new game is created.
     /// </summary>
     NewGame,

     /// <summary>
     /// The scene where the game is played.
     /// </summary>
     PlayGame,

     /// <summary>
     /// The state where the game is saved.
     /// </summary>
     SaveGame,

     /// <summary>
     /// The scene where the game is quitted.
     /// </summary>
     QuitGame
 }
Now we can go back to the State object and create the rule engine in the "CreateSceneManager" function.
 /// <summary>
 /// Creates the scene manager.
 /// </summary>
 /// <returns></returns>
 private static GameSceneManager CreateSceneManager()
 {
     // Create a new rule engine
     RuleEngine<GameSceneTransition> ruleEngine = new RuleEngine<GameSceneTransition>();

     // Add the rules
     // TODO: AddGameSceneTransitionRules(ruleEngine);

     // Create the scene manager with the rule engine
     return new GameSceneManager(ruleEngine as IRuleEngine);
 }

Next time we will create a method to add some rules.

Next

In Part 2 we will look at creating rules and the conditions for game scene manager to use.

Full Code

The full code can be found at the UnityStateManager repository on my GitHub.


Monday, January 26, 2015 6:17:43 AM (GMT Standard Time, UTC+00:00)  #    Comments [0] -
C# | Game Scene Manager | Game State Manager | Unity3D
Archive
<January 2015>
SunMonTueWedThuFriSat
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567
Blogroll
 DAVID HEINEMEIER HANSSON
Creator of ruby on Rails
 Harry Pierson
Passion * Technology * Ruthless Competence
 Joshua Flanagan
A .NET Software Developer
 Martin Ffowler
Author, speaker, and loud-mouth on the design of enterprise software
 Michael Schwarz's Blog
Developing applications on the Microsoft platform since Windows 3.1!
 Scot GU
Scott Guthrie lives in Seattle and builds a few products for Microsoft
 Scott Hanselman
Programming Life and the Zen of Computers
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2015
Duane Wingett
Sign In
Statistics
Total Posts: 64
This Year: 11
This Month: 11
This Week: 4
Comments: 51
Themes
Pick a theme:
All Content © 2015, Duane Wingett
DasBlog theme 'Business' created by Christoph De Baene (delarou)