Menu
CoddyTech

Transizioni

Fa parte della sezione Architettura dei giochi in Unity del percorso Unity di Coddy. Lezione 30 di 50.

Chi decide quando cambia lo stato? Affidare la decisione al proprietario riporta tutte le regole in un unico Update, come faceva lo switch. La risposta abituale è che ogni stato conosce la propria via d'uscita: nel suo Tick controlla la condizione e chiede alla macchina di passare allo stato successivo. Questa bomba aspetta che il giocatore si avvicini, poi accende la miccia e torna ad aspettare se il giocatore scappa:

public class Bomb : MonoBehaviour
{
    public Transform player;
    public StateMachine machine = new StateMachine();
    public IState idle;
    public IState fuse;

    void Start()
    {
        idle = new IdleState(this);
        fuse = new FuseState(this);
        machine.ChangeState(idle);
    }

    void Update() { machine.Tick(); }

    public bool PlayerNear()
    {
        return Vector3.Distance(transform.position, player.position) < 3f;
    }
}

public class IdleState : IState
{
    private Bomb bomb;
    public IdleState(Bomb bomb) { this.bomb = bomb; }

    public void Enter() { }
    public void Exit() { }

    public void Tick()
    {
        if (bomb.PlayerNear()) bomb.machine.ChangeState(bomb.fuse);
    }
}

public class FuseState : IState
{
    private Bomb bomb;
    private float timer;
    public FuseState(Bomb bomb) { this.bomb = bomb; }

    public void Enter() { timer = 2f; }
    public void Exit() { }

    public void Tick()
    {
        if (!bomb.PlayerNear())
        {
            bomb.machine.ChangeState(bomb.idle);
            return;
        }
        timer -= Time.deltaTime;
        if (timer <= 0f)
        {
            Debug.Log("Boom!");
            Object.Destroy(bomb.gameObject);
        }
    }
}

Ogni stato riceve la bomba nel proprio costruttore e tramite essa accede alla macchina, al controllo sul giocatore e agli altri stati. La bomba crea ogni stato una sola volta, in Start, così uno stato non ne crea un altro con new: due stati che si creassero a vicenda nei propri costruttori entrerebbero in un ciclo finché lo stack non trabocca. L'Update della bomba si limita ad aggiornare la macchina, mentre le regole sono negli stati a cui appartengono: la miccia di 2 secondi è una responsabilità esclusiva di FuseState.

ChangeState esegue subito Exit di FuseState ed Enter di IdleState, ma il resto di Tick di FuseState continuerebbe comunque a essere eseguito dopo la chiamata. return lo interrompe; altrimenti la miccia brucerebbe un altro frame anche se lo stato è già terminato. Destroy è scritto Object.Destroy: un MonoBehaviour lo eredita dalla classe Object di Unity, mentre una classe semplice deve specificare il nome della classe.

challenge icon

Sfida

Medio

Una dionea scatta contro chiunque le si avvicini. Flytrap.cs è pronto: crea la macchina e i tre stati (i campi pubblici open, snap e chew), avvia Open e fa avanzare la macchina a ogni frame, e PlayerInReach() indica se il giocatore è a meno di 2 unità di distanza sul terreno. In FlytrapStates.cs ogni Enter è già scritto (i colori, i timer, il log Chomp!). Scrivi le transizioni, nel Tick di ogni stato:

  • Open: quando il giocatore è a portata, la trappola scatta.
  • Snap dura 0.5 secondi. Poi la trappola mastica se il giocatore è ancora a portata, e si riapre se è riuscito a scappare.
  • Chew dura 3 secondi, chiunque ci sia nei dintorni; poi la trappola si riapre.

Usa le frecce per muovere il Player. Premi Play, avvicinati alla trappola, poi prova a schivarla e infine premi Check.

Provalo tu

using UnityEngine;

public class OpenState : IState
{
    private Flytrap trap;

    public OpenState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        trap.Paint(new Color(0.3f, 0.8f, 0.2f));
    }

    public void Tick()
    {
        // Scatta quando il giocatore è a portata
    }

    public void Exit()
    {
    }
}

public class SnapState : IState
{
    private Flytrap trap;
    private float timer;

    public SnapState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        timer = 0f;
        trap.Paint(new Color(1f, 0.55f, 0.1f));
    }

    public void Tick()
    {
        // Dopo 0.5 secondi: mastica se il giocatore è ancora a portata, altrimenti riapriti
    }

    public void Exit()
    {
    }
}

public class ChewState : IState
{
    private Flytrap trap;
    private float timer;

    public ChewState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        timer = 3f;
        trap.Paint(new Color(0.5f, 0.05f, 0.1f));
        Debug.Log("Chomp!");
    }

    public void Tick()
    {
        // Dopo 3 secondi: riapriti
    }

    public void Exit()
    {
    }
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Architettura dei giochi in Unity