Uma arquitetura competente desempenha um papel fundamental no desenvolvimento de qualquer produto de software. Os problemas mais comuns de desempenho, extensibilidade ou compreensibilidade têm sua origem na ausência. A falta de uma estrutura de projeto estritamente definida priva os desenvolvedores da capacidade de pensar em abstrações, entender o código escrito por um colega de relance e prever onde o erro ocorre. E, em alguns casos, uma pessoa pode ficar confusa até em seu próprio código, saturado de entidades e componentes. Mas quase todo programador, mais cedo ou mais tarde, sozinho ou com a ajuda de um livro inteligente, se familiariza com soluções que são boas, independentemente do contexto. São tão eficazes e versáteis que encontram lugar na resolução de tantos problemas, e ... Sim, eu sei, não dá para continuar, todos já perceberam do que eu estava falandoPadrões de design. Alguns oram por eles, outros encontram suas bicicletas entre eles. Alguns afirmam na entrevista que os estudaram por dentro e por fora e foram apanhados em completa inutilidade. Mas todos, de uma forma ou de outra, ouviram falar deles. Hoje vamos falar sobre um dos padrões - "Estado" . Mais precisamente, sobre máquinas de estados finitos. Mesmo se você pertencer ao último dos grupos listados acima, provavelmente já encontrou a seguinte ferramenta:
Os animadores no Unity são construídos em máquinas de estado. Cada animação de um grupo de objetos é representada como um estado. As condições e a ordem das transições entre eles são determinadas no animador, que é uma máquina de estados. Além disso, o tópico do uso de máquinas de estado finito para descrever a lógica de trabalho de objetos com comportamento complexo foi levantado repetidamente. Bots de IA, controle do personagem principal, isso é tudo.
. . , - , . , , , . , . , - :
. , , . , 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);
}
}
Em outras palavras, quando clicamos em um botão (na verdade, para qualquer CanvasRenderer), enviamos o sinal correspondente para a máquina. Ao fazer a transição entre estados, podemos ligar e desligar diferentes Canvas de qualquer maneira conveniente para nós, alterar as máscaras usadas Physics.Raycast
e até mesmo alterar Time.timeScale! Não importa o quão horrível e incivilizado possa parecer à primeira vista, desde que o que foi feito Enter
seja cancelado Exit
, é garantido que não causará nenhum transtorno, então vá em frente! O principal é não exagerar.