.Net Code Monkey RSS 2.0
 Friday, February 06, 2015

Game State Manager for Unity3D - Part 6 - Adding An Event Manager

Previous Part - Part 5

One of my colleagues, John Collinson, made me aware that it is common to have a centralised implementation for raising events in game development, so following his pattern I will now extend the UnityStateManager to encompass an EventManager as well. So the first thing we will do is to create a new empty "GameEventManager" class.
/// <summary>
/// Handles raising of game events
/// </summary>
internal class GameEventManager
{
}
This class will be accessed via the GameManager in the same way that the GameSceneManager is. We will add a property called "EventManager" of type "GameEventManager" the "GameManager" class. We will extend the Game Manager's constructor to initialise the GameEventManager. We will add a method which will create a new instance of the
/// <summary>
/// Gets (or privately sets) the game event manager.
/// </summary>
/// <value>
/// The event manager.
/// </value>
internal GameEventManager EventManager { get; private set; } 
   
 
/// <summary>
/// Initializes a new instance of the <see cref="GameManager"/> class.
/// </summary>
public GameManager()
{
    // Create a new EventManager and give it to the instance to use
    EventManager = CreateEventManager();

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

/// <summary>
/// Creates the event  manager.
/// </summary>
/// <returns></returns>
private GameEventManager CreateEventManager()
{
    // Create a new instance of the game's event manager
    GameEventManager gameEventManager = new GameEventManager();

    // return the scene manager
    return gameEventManager;
}
The members of the game's event manager will now be able to be called from the static State object in a consistent way like so...
State.GameManager.EventManager.Blah(...)
We can now look at moving the events that are raised in the GameSceneManager into the GameEventManager. First we will look at the GameSceneChanged event. We pick up the event declaration and the "RaiseGameSceneChangedEvent" method from the GameSceneManger, move it to the GameEventManagerand  change the modifier from "private" to "public".
#region GameSceneManger.GameSceneChanged

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

/// <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>
public 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);
    }
}

#endregion

We now have to pop into the GameSceneManager's "OnGameSceneChange" to update the call to "RaiseGameSceneChangedEvent" as this is now in the new GameEventmanager class.
// Raise the game scene changed event to notify any listeners that
// the game state has changed.
State.GameManager.EventManager.RaiseGameSceneChangedEvent(originalSceneIdentifier, newGameSceneIdentifier);
Then a quick change to the wiring up of the event handlers in GameManager.CreateSceneManager. Leave the GameSceneChanging event handler wiring as it is for the moment. We will change that when we move that event.

// Wire up the event handlers
EventManager.GameSceneChanged += gameSceneManager_GameSceneChanged;
gameSceneManager.GameSceneChanging += gameSceneManager_GameSceneChanging;
Lastly don't forget to import any namespaces to correct any remaining compile errors. We must now follow the same process for the "GameSceneChanging" event.

Once this is complete we can run the Unity project and everything should still work.

Note: Some people prefer to use the term "TriggerBlahBlahBlah" rather than "RaiseBlahBlahBlah". I come from a Visual basic background originally so tend to use "Raise..."     

All Parts

Part 1
Part 2
Part 3
Part 4
Part 5

Full Code

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


Friday, February 06, 2015 7:16:24 AM (GMT Standard Time, UTC+00:00)  #    Comments [0] -
C# | Game Scene Manager | Game State Manager | Unity3D | Game Event Manager
 Wednesday, February 04, 2015

Game State Engine for Unity3D - Part 5 - Plumbing it all in to Unity3D

Following on from Part 4  (http://www.duanewingett.info/2015/01/29/GameStateEngineForUnity3DPart4SubscribingToEvents.aspx) where we hooked into the events we will now plumb everything we have coded into the unity project its self.

So if we go into Unity and Save the current scene as "Initialize" (save in the scenes folder), the create and save two more scenes "Intro" and "MainMenu". Go to Unity's build Settings Menu and make sure that the only included scenes are...
Scenes/Initialize.unity
Scenes/Intro.unity
Scenes/MainMenu.unity
...and are in that order. We will create a script called "Initialize" and attach it to the  "Initialize" scene. As we want the initialize scene to transition straight to the "Intro" scene, in this "Start" method of whose script we will call the SceneManager's SetGameScene and pass the Intro scene identifier.
public class Initialize : MonoBehaviour {
    // Use this for initialization
    void Start () {
        State.SceneManager.SetGameScene(GameSceneIdentifier.Intro);
    }
}
If you follow the call stack through (Using "Go To Definition" in Visual Studio / "Go To Declaration" in MonoDevelope) until we reach "GameSceneManager.OnGameSceneChange" you will see that the SceneManager raises an event when the scene is allowed to change. Our GameManager already subscribed to that event so lets find that.
/// <summary>
/// Handles the GameSceneChanged event of the gameSceneManager control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="GameSceneChangedEventArgs"/> instance containing the event data.</param>
/// <exception cref="System.NotImplementedException"></exception>
void gameSceneManager_GameSceneChanged(object sender, GameSceneChangedEventArgs e)
{
    throw new System.NotImplementedException();
}
Lets remove the code that throws the NotImplementedException and replace this with a call to a method that will load the level for the value of the GameSceneIdentifier which was passed to this event handler in the event data.
/// <summary>
/// Handles the GameSceneChanged event of the gameSceneManager control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="GameSceneChangedEventArgs"/> instance containing the event data.</param>
/// <exception cref="System.NotImplementedException"></exception>
void gameSceneManager_GameSceneChanged(object sender, GameSceneChangedEventArgs e)
{
    // Load the level using the GameSceneIdentifier from the event data
    LoadLevelByGameSceneIdentifier(e.CurrentSceneIdentifier);
}

LoadLevelByGameSceneIdentifier

The LoadLevelByGameSceneIdentifier method will contain the code to change the game Level or Scene in Unity. We will do that by calling the UnityEngine's "Application.LoadLevel(...)". This method has two overloads; one takes a parameter of type string which identifies the level name, and the other of type integer which identifies the index of the level to be loaded. This index corresponds to the order of the included scenes set in Unity's "Build Settings" dialogue. We will use the overload which takes an integer, so to do this we will first create a levelIndex variable of integer type and populate that from the GameSceneIdentifier that has been passed to this method.
/// <summary>
/// Loads the level by game scene identifier.
/// </summary>
/// <param name="gameSceneIdentifier">The game scene identifier.</param>
private static void LoadLevelByGameSceneIdentifier(GameSceneIdentifier gameSceneIdentifier)
{
    // Get the level index from the GameSceneIdentifier. This must match 
    // the same index as defined in Unity's Build Settings dialogue. 
    // We will also need to explicitly cast the identifier as an int.
    int levelIndex = (int)gameSceneIdentifier;

    // Call through to the Unity Engine's method to load a scene / level 
    // using our levelIndex. 
    Application.LoadLevel(levelIndex);
}
Add a game object into the Intro scene (maybe some text) so you can easily identify the scene has transitioned from the "Initialize" scene and has loaded correctly.

Running Unity up now the game should start in the "Initialize" scene and immediately transition to the "Intro" scene. You may chose to use the Intro scene to show introduction credits while loading assets, etc.

Lets add another script to transition from the "Intro" to "MainMenu" and attach it to the Intro scene (or an object in the scene like the camera)
public class Intro : MonoBehaviour
{
    // Use this for initialization
    void Start()
    {
        StartCoroutine(DelayMethod(5.0f));

        //3 seconds later
        // Change to main menu
        State.GameManager.SceneManager.SetGameScene(GameSceneIdentifier.MainMenu);
    }

    IEnumerator DelayMethod(float delay)
    {
        yield return new WaitForSeconds(delay);
    }
}
Running this up in Unity now should make the game transition immediately to the "Intro" scene and then pause before transitioning to the "MainMenu" scene.

Conclusion

So why all this code just to transition the scene? could we not just have put "Application.LoadLevel(1);" in the "Start" method of the "Initialize" script? Well yes we could, but we'd have to start adding any logic around whether changing the scene is valid in the scene scripts. There may be logic like "is the game saved" that needs to be checked before a scene can change, say for instance when changing from "Playing Game" to "Load a saved game" It is not really the responsibility of the scene to know or care about this logic or any rules about what scene changes are valid.

So we encapsulate all this logic in the GameManager and SceneManager objects and access this through the static State class giving a single point of access. The SceneManager also allows for subscribers to may want to "block" a scene change to set the cancel flag. The SceneManager also provides a call back for the calling code to run if a transition fails.

It's also good coding practice to separate concerns into separate classes to make code re-use and maintenance easier and to help identify bugs when they occur. So these are the reasons why I have gone down this route. I hope some of these articles have helped you. Please feel free to leave any comments or questions, below.

All Parts

Part 1
Part 2
Part 3
Part 4

Full Code

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





Wednesday, February 04, 2015 7:36:34 AM (GMT Standard Time, UTC+00:00)  #    Comments [0] -
C# | Game Scene Manager | Game State Manager | Unity3D
 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
Archive
<April 2015>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789
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: 66
This Year: 13
This Month: 0
This Week: 0
Comments: 52
Themes
Pick a theme:
All Content © 2015, Duane Wingett
DasBlog theme 'Business' created by Christoph De Baene (delarou)