[ JS / C# ] StateManager

Cette section est destinée aux scripts partagés par la communauté. Chaque post est destiné à un script. Suivez bien les recommandations.
Avatar de l’utilisateur
spedy
Messages : 290
Inscription : 16 Nov 2010 16:32
Localisation : Aix en Provence

[ JS / C# ] StateManager

Message par spedy » 29 Août 2013 14:36

NOM DU SCRIPT : StateManager ( Frontcontroller pattern )

-AUTEUR(S): Spedy

-DESCRIPTION : Une des questions que l'on se pose au début d'un projet est son architecture ! En me baladant sur le site d'un autre utilisateur du forum ZeroKcm je suis tombé sur un de ces tuto http://www.zerokcm.fr/wp/question-darchitecture/ ( oui oui il y en a qui clique sur les liens des signatures. Sa vision ce rapprochant un peut de ce que j'utilise en As3 j'ai voulu en donné ma vision.

Il s'agit donc d'un stateManager.
Chaque state est définie pas un ensemble de Script ( Monobehaviour ).
En demandant au manager d'aller à une state celui si détruit les Scripts de la state précédente et instancie ceux de la nouvelle state.
Si un script de la state précédente ce trouve aussi dans la nouvelle state, il n'est pas touché par le script.

On peut ainsi avoir des states comme ceci :
State Menu ( ScriptMenu, ScriptFooter )
State Jeu ( ScriptJeu, ScriptFooter )
State Pause ( ScriptJeu, ScriptPause, ScriptFooter )
State Score ( ScriptScore, ScriptFooter )

Je poste donc ce script pour vous donnez ma vision, mais aussi connaitre votre avis sur ce type d'architecture et bien-sur sur ma façon de la mettre en place.

-UTILISATION :
Il suffi de mettre le script StateManager sur la scène.
Dans celui si les states sont défini dans la fonction init().

Code : Tout sélectionner

var sEnd : List.<System.Type> = new List.<System.Type>( );
	sEnd.Add( StateEnd );
	sEnd.Add( StateFooter );

registerState( State.MENU, sMenu );
-SCRIPT ( Voici les sources ( avec des states d'exemple ) en C#, la version js ce trouve sur mon gitHub dans ma signature )

StateProcess ( classe Abstrait pour les State )

Code : Tout sélectionner

using UnityEngine;
using System.Collections;
 
public abstract class StateProcess : MonoBehaviour
{
    public virtual void Exit() {}
}
StateManage ( le script )

Code : Tout sélectionner

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class StateManager : MonoBehaviour {

	public enum State
	{
		NULL,
		MENU,
		GAME,
		END
	}

	public static StateManager instance;
	public State defaultState;

	protected State _state;
	protected State _prevState;

	protected Hashtable _hStates = new Hashtable();

	// Use this for initialization ( this is a singleton )
	void Awake() {
		if( instance == null )
			instance = this;
		else
			Debug.LogError( "Cannot instantiate StateManager twice !" );

		init( );
	}

	/**
	* Initialisation of states
	*
	* @param
	* @return 
	*/
	public void init( ){
		List<System.Type> sMenu = new List<System.Type>( );
			sMenu.Add( typeof( StateMenu ) );
			sMenu.Add( typeof( StateFooter ) );
		
		List<System.Type> sGame = new List<System.Type>( );
			sGame.Add( typeof( StateGame ) );

		List<System.Type> sEnd = new List<System.Type>( );
			sEnd.Add( typeof( StateEnd ) );
			sEnd.Add( typeof( StateFooter ) );

		registerState( State.MENU, sMenu );
		registerState( State.GAME, sGame );
		registerState( State.END, sEnd );

		state = defaultState;
	}

	/**
	* Register a state
	* 
	* @public
	* @param 	eName 		: State cade name  ( enum State )
	* @param 	lProcess 	: List of process ( List<System.Type> )
	* @return 	true if success 	( bool )
	*/
	public bool registerState( State eName, List<System.Type> lProcess ){
		if( _hStates.ContainsKey( eName ) )
			Debug.LogError( "registerState ::: A State with the name " + eName + "is already registered" );

		_hStates.Add( eName, lProcess );
		return true;
	}

	/**
	* function registerState
	* 
	* @public
	* @param 	eName 		: State cade name  ( enum State )
	* @return 	list of process 	( List<System.Type> )
	*/
	public List<System.Type> getStateContent( State eName ) {
		return ( List<System.Type> ) _hStates[ eName ];
	}

	/**
	* UnRegister a state
	*
	* @param 	eName 		: State cade name  ( enum State )
	* @return 	true if success 	( bool )
	*/
	public bool unRegisterStater( State eName ){
		if( !_hStates.ContainsKey( eName ) )
			Debug.LogError( "getStateContent ::: The State " + eName + " is not registered" );

		_hStates.Remove( eName );
		return true;
	}



	public State state
	{
		get
		{
			return _state;
		}
		set
		{
			if( !_hStates.ContainsKey( value ) )
				Debug.LogError( "The state " + value + " is unkown" );

			if( _state == value )
				Debug.LogError( "The state " + value + " is already the current state" );

			_execState( value );
		}
	}



	//----------o Private

					/**
					* Store the last state before drawing the new state
					*
					* @param 	eName 	: State to draw 	( enum State )
					* @return
					*/
					private void _execState( State eName ){
						_prevState = _state;
						_state = eName;
						_drawState( );
					}


					/**
					* Drawning the DisplayList of the current state
					*
					* @param
					* @return
					*/
					private void _drawState(){
						List<System.Type> v = ( List<System.Type> ) _hStates[ _state ];
						int i;
						StateProcess comp;

						List<System.Type> vTmp = ( List<System.Type> ) _hStates[ _prevState ];
						if( vTmp != null )
						{
							
							i = vTmp.Count;

							while( --i >= 0 )
							{
								if( v.Contains( vTmp[ i ] ) )
									continue;

								comp = ( StateProcess ) GetComponent( vTmp[ i ] );
								comp.Exit( );
								Destroy(  comp );
							}
						}

						i = v.Count;
						while( --i >= 0 )
						{
							comp = ( StateProcess ) GetComponent( v[ i ] );
							if( comp == null )
								gameObject.AddComponent( v[ i ] );
						}
					}
}
Les states pour l'exemples ( StateEnd / StateGame / StateMenu / StateFooter )
- StateEnd

Code : Tout sélectionner

using UnityEngine;
using System.Collections;

public class StateEnd : StateProcess {

	void OnGUI()
    {
        if (GUI.Button(new Rect(50, 50, 200, 80), "MENU"))
        {
            StateManager.instance.state = StateManager.State.MENU;
        }
    }
}
- StateGame

Code : Tout sélectionner

using UnityEngine;
using System.Collections;

public class StateGame : StateProcess {

	void OnGUI()
    {
        if (GUI.Button(new Rect(50, 50, 200, 80), "END"))
        {
            StateManager.instance.state = StateManager.State.END;
        }
    }
}
- StateMenu

Code : Tout sélectionner

using UnityEngine;
using System.Collections;

public class StateMenu : StateProcess {

	void OnGUI()
    {
        if (GUI.Button(new Rect(50, 50, 200, 80), "Play"))
        {
            StateManager.instance.state = StateManager.State.GAME;
        }
 
        if (GUI.Button(new Rect(50, 150, 200, 80), "Quit"))
        {
            StateManager.instance.state = StateManager.State.END;
        }
 
    }
}
- StateFooter

Code : Tout sélectionner

using UnityEngine;
using System.Collections;

public class StateFooter : StateProcess {

	void OnGUI() 
	{
		if (GUI.Button(new Rect(50, 500, 200, 80), "FOOTER"))
        {
            print( "footer" );
        }
	}
}

Répondre

Revenir vers « Scripts »