Une architecture compétente joue un rôle clé dans le développement de tout logiciel. Les problèmes de performances, d'extensibilité ou de compréhensibilité les plus courants sont enracinés dans son absence. L'absence d'une structure de projet strictement définie prive les développeurs de la capacité de penser en abstractions, de comprendre d'un coup d'œil le code écrit par un collègue et de prédire où l'erreur se produit. Et dans certains cas, une personne peut être confuse même dans son propre code, sursaturée d'entités et de composants. Mais presque chaque programmeur, tôt ou tard, seul ou à l'aide d'un livre intelligent, se familiarise avec des solutions qui sont bonnes quel que soit le contexte. Ils sont tellement efficaces et polyvalents qu'ils trouvent une place dans la résolution de nombreux problèmes, et ... Oui, je sais, vous ne pouvez pas continuer, tout le monde a déjà compris ce dont je parlaismodèles de conception. Certains prient pour eux, d'autres ont trouvé leurs vélos parmi eux. Certains affirment dans l'interview qu'ils les ont étudiés de fond en comble et ont été pris dans une totale inutilité. Mais tout le monde, d'une manière ou d'une autre, en a entendu parler. Aujourd'hui, nous allons parler de l'un des modèles - "État" . Plus précisément, sur les machines à états finis. Même si vous appartenez au dernier des groupes listés ci-dessus, vous avez probablement rencontré l'outil suivant:
Les animateurs dans Unity sont construits sur des machines à états. Chaque animation d'un groupe d'objets est représentée comme un état. Les conditions et l'ordre des transitions entre eux sont déterminés dans l'animateur, qui est une machine à états. En outre, le sujet de l'utilisation de machines à états finis pour décrire la logique de travail d'objets à comportement complexe a été soulevé à plusieurs reprises. Bots IA, contrôle du personnage principal, c'est tout.
. . , - , . , , , . , . , - :
. , , . , Play . - , . , isPaused, , .
, . , , , .
, , . , , , , , AI.
, . Play, WaitMatch "match_ready", , , "room_left" .
. , , , . , , "" .
. , . . . . , .
- . , . , .
, . :
FSM |
AState |
- public FSM(AState initState) - public void Signal(string name, object data = null) - private void ChangeState(AState newState) |
- void Enter() - void Exit() - AState Signal() |
, 2 :
. . , , . , . Exit
Enter
. , :
public class FSM
{
private AState currentState;
public FSM(AState initState) => ChangeState(initState);
private void ChangeState(AState newState)
{
if (newState == null) return;
currentState?.Exit();
currentState = newState;
currentState.Enter();
}
public void Signal(string name, object arg = null)
{
var result = currentState.Signal(name, arg);
ChangeState(result);
}
}
. , , .
public class AState
{
public virtual void Enter() => null;
public virtual void Exit() => null;
public virtual AState Signal(string name, object arg) => null;
}
public class SLoad : AState
{
public override void Enter()
{
Game.Data.Set("loader_visible",true);
var load = SceneManager.LoadSceneAsync("SceneGameplay");
load.completed+=a=>Game.Fsm.Signal("scene_loaded");
}
public override void Exit()
{
Game.Data.Set("loader_visible",false);
}
public override AState Signal(string name, object arg)
{
if (name == "scene_loaded")
return new SLobby();
return null;
}
}
, , . , , . 3 , . - , - . ! , , . , , ,
public class SMessage : AState
{
private string msgText;
private AState next;
public SMessage(string messageText, AState nextState)
{
msgText = messageText;
btnText = buttonText;
next = nextState;
}
public override void Enter()
{
Game.Data.Set("message_text", msgText);
Game.Data.Set("window_message_visible",true);
}
public override void Exit()
{
Game.Data.Set("window_message_visible",false);
}
public override AState Signal(string name, object arg)
{
if (name == "message_btn_ok")
return next;
return null;
}
}
, c , .
...
case "iap_ok":
return new SMessage("Item purchased! Going back to store.", new SStore());
...
Game.Data
, , , , "". , , UI, . , . , .
public class ButtonFSM : MonoBehaviour, IPointerClickHandler
{
public string key;
public override void OnPointerClick(PointerEventData eventData)
{
Game.Fsm.Signal(key);
}
}
En d'autres termes, lorsque nous cliquons sur un bouton (en fait, vers n'importe quel CanvasRenderer), nous envoyons le signal correspondant à la machine. Lors de la transition entre les états, nous pouvons activer et désactiver différents Canvas de la manière qui nous convient, changer les masques utilisés Physics.Raycast
et même parfois changer Time.timeScale! Aussi horrible et non civilisé que cela puisse paraître à première vue, tant que ce qui est fait Enter
est annulé Exit
, il est garanti qu'il ne causera aucun inconvénient, alors allez-y! L'essentiel est de ne pas en faire trop.