Teilen und Erobern - Mit FSM in Unity

Eine kompetente Architektur spielt eine Schlüsselrolle bei der Entwicklung eines Softwareprodukts. Die häufigsten Probleme mit Leistung, Erweiterbarkeit oder Verständlichkeit liegen in ihrer Abwesenheit. Das Fehlen einer streng definierten Projektstruktur beraubt Entwickler der Fähigkeit, in Abstraktionen zu denken, den von einem Kollegen geschriebenen Code auf einen Blick zu verstehen und vorherzusagen, wo der Fehler auftritt. In einigen Fällen kann eine Person sogar in ihrem eigenen Code verwirrt sein, der mit Entitäten und Komponenten übersättigt ist. Aber fast jeder Programmierer, früher oder später, ob allein oder mithilfe eines cleveren Buches, lernt Lösungen kennen, die unabhängig vom Kontext gut sind. Sie sind so effektiv und vielseitig, dass sie einen Platz finden, um viele Probleme zu lösen, und ... Ja, ich weiß, Sie können nicht weitermachen, alle haben bereits verstanden, dass ich darüber gesprochen habeDesignmuster. Einige beten für sie, andere fanden ihre Fahrräder unter ihnen. Einige behaupten im Interview, sie hätten sie von innen und außen studiert und seien in völlige Nutzlosigkeit geraten. Aber jeder hörte auf die eine oder andere Weise von ihnen. Heute werden wir über eines der Muster sprechen - "Staat" . Genauer gesagt über endliche Zustandsmaschinen. Selbst wenn Sie zu der letzten der oben aufgeführten Gruppen gehören, sind Sie wahrscheinlich auf das folgende Tool gestoßen:





Minimaler Hauptcharakter-Animator im Plattformer
Minimaler Hauptcharakter-Animator im Plattformer

Animatoren in Unity basieren auf Zustandsautomaten. Jede Animation einer Gruppe von Objekten wird als Status dargestellt. Die Bedingungen und die Reihenfolge der Übergänge zwischen ihnen werden im Animator festgelegt, der eine Zustandsmaschine ist. Auch das Thema der Verwendung von Finite-State-Maschinen zur Beschreibung der Arbeitslogik von Objekten mit komplexem Verhalten wurde wiederholt angesprochen. KI-Bots, Kontrolle über die Hauptfigur, das ist alles.





. . , - , . , , , . , . , - :





  • . , , . , 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);
    }
}
      
      



Mit anderen Worten, wenn wir auf eine Schaltfläche klicken (tatsächlich an einen beliebigen CanvasRenderer), senden wir das entsprechende Signal an die Maschine. Beim Übergang zwischen Zuständen können wir verschiedene Canvas auf eine für uns bequeme Weise ein- und ausschalten, die in verwendeten Masken ändern Physics.Raycast



und manchmal sogar Time.timeScale ändern! Egal wie schrecklich und unzivilisiert es auf den ersten Blick erscheinen mag, solange das, was getan Enter



wird, abgesagt wird Exit



, wird es garantiert keine Unannehmlichkeiten verursachen, also machen Sie weiter! Die Hauptsache ist, es nicht zu übertreiben.








All Articles