Implementando um padrão de design

Convidamos futuros alunos do curso " Unity Game Developer. Professional " a assistir à aula aberta sobre o tema "Inteligência artificial avançada de inimigos em atiradores".







E agora estamos compartilhando a tradução tradicional de material útil.










Neste tutorial, vamos dominar o padrão de design Command e implementá-lo no Unity como parte de um sistema de movimento de objetos de jogo.





Apresentando o padrão de comando

Pedidos, ordens e comandos : todos estamos familiarizados com eles na vida real; uma pessoa envia uma solicitação (ou ordem ou comando) a outra pessoa para realizar (ou não) algumas das tarefas atribuídas a ela. Em design e desenvolvimento de software, isso funciona de maneira semelhante: uma solicitação de um componente é passada para outro para executar tarefas específicas dentro do padrão Equipe.





: — , , () , . , / .





. , ( ). — : (GUI), , (logic handler), -.





GUI , . , GUI -.





 

UML ​. , , .





Command



, (ConcreteCommandN



) Invoker



, Client



Receiver



.





Command

Command (Execute) (Undo) . Execute , , Undo.





public interface ICommand      
{ 
    void Execute();
    void ExecuteUndo();       
}
      
      



Invoker

Invoker



( Sender



) . , . , . . , . .





Client

(Client) . , (Receiver), . . , .





Receiver ( )

Receiver () — , -. . , , .





Command . , - ( ). .





, , . (immutable),   .





Unity

, Unity . . (Undo), .





, !





3D Unity

3D Unity. CommandDesignPattern



.





Plane, . Hierarchy Plane. «Ground» 20 X 20 z. , .





Player



. Capsule



. Hierarchy Capsule



. Player



.





GameManager.cs

Ground



. GameManager.cs



.





Player



.





public GameObject



player



.





public GameObject mPlayer;
      
      



Player



Hierarchy



Player



.





(Up, Down, Left Right).





. Update



. 1 .





void Update()
{
    Vector3 dir = Vector3.zero;

    if (Input.GetKeyDown(KeyCode.UpArrow))
        dir.z = 1.0f;
    else if (Input.GetKeyDown(KeyCode.DownArrow))
        dir.z = -1.0f;
    else if (Input.GetKeyDown(KeyCode.LeftArrow))
        dir.x = -1.0f;
    else if (Input.GetKeyDown(KeyCode.RightArrow))
        dir.x = 1.0f;

    if (dir != Vector3.zero)
    {
        _player.transform.position += dir;
    }
}
      
      



Play



, . (Up, Down, Left Right), .





 

Player



Ground



, . ?





, Ground



, .





public Vector3? GetClickPosition()
{
    if(Input.GetMouseButtonDown(1))
    {
        RaycastHit hitInfo;
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if(Physics.Raycast(ray, out hitInfo))
        {
            //Debug.Log("Tag = " + hitInfo.collider.gameObject.tag);
            return hitInfo.point;
        }
    }
    return null;
}
      
      



Vector3?





?



C#,





public int? myProperty { get; set; }
      
      



, nullable





Nullable



, System.Nullable



. , NULL



, NULL



. , Nullable<Int32>



, «Nullable of Int32»



, -2147483648 2147483647, null



. Nullable<bool>



true



, false



null



. null



, , , . , true



false



, .





, , MoveTo



. MoveTo



. .





public IEnumerator MoveToInSeconds(GameObject objectToMove, Vector3 end, float seconds)
{
    float elapsedTime = 0;
    Vector3 startingPos = objectToMove.transform.position;
    end.y = startingPos.y;
    while (elapsedTime < seconds)
    {
        objectToMove.transform.position = Vector3.Lerp(startingPos, end, (elapsedTime / seconds));
        elapsedTime += Time.deltaTime;
        yield return null;
    }
    objectToMove.transform.position = end;
}
      
      



, , , .





Update



, .





****
    var clickPoint = GetClickPosition();
    if (clickPoint != null)
    {
        IEnumerator moveto = MoveToInSeconds(_player, clickPoint.Value, 0.5f);
        StartCoroutine(moveto);
    }
****
      
      



Play



, . (Up, Down, Left Right) Ground



, Player



.





(Undo



)? ? .





Unity

Undo



, , .





Undo



— , Unity.





. Command



.





Command

public interface ICommand
{
    void Execute();
    void ExecuteUndo();
}
      
      



Command



. — Execute



, — ExecuteUndo



, . ( , ).





.





CommandMove

public class CommandMove : ICommand
{
    public CommandMove(GameObject obj, Vector3 direction)
    {
        mGameObject = obj;
        mDirection = direction;
    }

    public void Execute()
    {
        mGameObject.transform.position += mDirection;
    }

    public void ExecuteUndo()
    {
        mGameObject.transform.position -= mDirection;
    }

    GameObject mGameObject;
    Vector3 mDirection;
}
      
      



CommandMoveTo

public class CommandMoveTo : ICommand
{
    public CommandMoveTo(GameManager manager, Vector3 startPos, Vector3 destPos)
    {
        mGameManager = manager;
        mDestination = destPos;
        mStartPosition = startPos;
    }

    public void Execute()
    {
        mGameManager.MoveTo(mDestination);
    }

    public void ExecuteUndo()
    {
        mGameManager.MoveTo(mStartPosition);
    }

    GameManager mGameManager;
    Vector3 mDestination;
    Vector3 mStartPosition;
}
      
      



, ExecuteUndo



. , Execute



.





Invoker

Invoker



. , Invoker



— , . , Undo



Last In First Out (LIFO)



.





LIFO? LIFO? Stack



.





C# , LIFO (Last In First Out). . Push()



( ), Pop()



( ) Peek()



.





Invoker



, .





public class Invoker
{
    public Invoker()
    {
        mCommands = new Stack<ICommand>();
    }

    public void Execute(ICommand command)
    {
        if (command != null)
        {
            mCommands.Push(command);
            mCommands.Peek().Execute();
        }
    }

    public void Undo()
    {
        if(mCommands.Count > 0)
        {
            mCommands.Peek().ExecuteUndo();
            mCommands.Pop();
        }
    }

    Stack<ICommand> mCommands;
}
      
      



, Execute



Undo



. Execute



, Push



Execute



. Peek



.





Undo ExecuteUndo



, ( Peek



). Invoker



, Pop



.





Invoker



  . Invoker



GameManager



.





private Invoker mInvoker;
      
      



mInvoker



Start



GameManager.







mInvoker = new Invoker();
      
      



Undo

U



. Update



.





// Undo 
    if (Input.GetKeyDown(KeyCode.U))
    {
        mInvoker.Undo();
    }
      
      



Update







.





void Update()
{
    Vector3 dir = Vector3.zero;

    if (Input.GetKeyDown(KeyCode.UpArrow))
        dir.z = 1.0f;
    else if (Input.GetKeyDown(KeyCode.DownArrow))
        dir.z = -1.0f;
    else if (Input.GetKeyDown(KeyCode.LeftArrow))
        dir.x = -1.0f;
    else if (Input.GetKeyDown(KeyCode.RightArrow))
        dir.x = 1.0f;

    if (dir != Vector3.zero)
    {
        //Using command pattern implementation.
        ICommand move = new CommandMove(mPlayer, dir);
        mInvoker.Execute(move);
    }

    var clickPoint = GetClickPosition();

    //Using command pattern right click moveto.
    if (clickPoint != null)
    {
        CommandMoveTo moveto = new CommandMoveTo(
            this, 
            mPlayer.transform.position, 
            clickPoint.Value);
        mInvoker.Execute(moveto);
    }
    // Undo 
    if (Input.GetKeyDown(KeyCode.U))
    {
        mInvoker.Undo();
    }
}
      
      



Play



, . (Up, Down, Left Right), , «u»





GoF, , - , , , , , .





Unity

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

public class GameManager : MonoBehaviour
{
    public interface ICommand
    {
        void Execute();
        void ExecuteUndo();
    }

    public class CommandMove : ICommand
    {
        public CommandMove(GameObject obj, Vector3 direction)
        {
            mGameObject = obj;
            mDirection = direction;
        }

        public void Execute()
        {
            mGameObject.transform.position += mDirection;
        }

        public void ExecuteUndo()
        {
            mGameObject.transform.position -= mDirection;
        }

        GameObject mGameObject;
        Vector3 mDirection;
    }

    public class Invoker
    {
        public Invoker()
        {
            mCommands = new Stack<ICommand>();
        }

        public void Execute(ICommand command)
        {
            if (command != null)
            {
                mCommands.Push(command);
                mCommands.Peek().Execute();
            }
        }

        public void Undo()
        {
            if (mCommands.Count > 0)
            {
                mCommands.Peek().ExecuteUndo();
                mCommands.Pop();
            }
        }

        Stack<ICommand> mCommands;
    }
    public GameObject mPlayer;
    private Invoker mInvoker;

    public class CommandMoveTo : ICommand
    {
        public CommandMoveTo(GameManager manager, Vector3 startPos, Vector3 destPos)
        {
            mGameManager = manager;
            mDestination = destPos;
            mStartPosition = startPos;
        }

        public void Execute()
        {
            mGameManager.MoveTo(mDestination);
        }

        public void ExecuteUndo()
        {
            mGameManager.MoveTo(mStartPosition);
        }

        GameManager mGameManager;
        Vector3 mDestination;
        Vector3 mStartPosition;
    }

    public IEnumerator MoveToInSeconds(GameObject objectToMove, Vector3 end, float seconds)
    {
        float elapsedTime = 0;
        Vector3 startingPos = objectToMove.transform.position;
        end.y = startingPos.y;
        while (elapsedTime < seconds)
        {
            objectToMove.transform.position = Vector3.Lerp(startingPos, end, (elapsedTime / seconds));
            elapsedTime += Time.deltaTime;
            yield return null;
        }
        objectToMove.transform.position = end;
    }

    public Vector3? GetClickPosition()
    {
        if (Input.GetMouseButtonDown(1))
        {
            RaycastHit hitInfo;
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray, out hitInfo))
            {
                //Debug.Log("Tag = " + hitInfo.collider.gameObject.tag);
                return hitInfo.point;
            }
        }
        return null;
    }



    // Start is called before the first frame update
    void Start()
    {
        mInvoker = new Invoker();
    }

    // Update is called once per frame
    void Update()
    {
        Vector3 dir = Vector3.zero;

        if (Input.GetKeyDown(KeyCode.UpArrow))
            dir.z = 1.0f;
        else if (Input.GetKeyDown(KeyCode.DownArrow))
            dir.z = -1.0f;
        else if (Input.GetKeyDown(KeyCode.LeftArrow))
            dir.x = -1.0f;
        else if (Input.GetKeyDown(KeyCode.RightArrow))
            dir.x = 1.0f;

        if (dir != Vector3.zero)
        {
            //----------------------------------------------------//
            //Using normal implementation.
            //mPlayer.transform.position += dir;
            //----------------------------------------------------//


            //----------------------------------------------------//
            //Using command pattern implementation.
            ICommand move = new CommandMove(mPlayer, dir);
            mInvoker.Execute(move);
            //----------------------------------------------------//
        }

        var clickPoint = GetClickPosition();

        //----------------------------------------------------//
        //Using normal implementation for right click moveto.
        //if (clickPoint != null)
        //{
        //    IEnumerator moveto = MoveToInSeconds(mPlayer, clickPoint.Value, 0.5f);
        //    StartCoroutine(moveto);
        //}
        //----------------------------------------------------//

        //----------------------------------------------------//
        //Using command pattern right click moveto.
        if (clickPoint != null)
        {
            CommandMoveTo moveto = new CommandMoveTo(this, mPlayer.transform.position, clickPoint.Value);
            mInvoker.Execute(moveto);
        }
        //----------------------------------------------------//


        //----------------------------------------------------//
        // Undo 
        if (Input.GetKeyDown(KeyCode.U))
        {
            mInvoker.Undo();
        }
        //----------------------------------------------------//
    }

    public void MoveTo(Vector3 pt)
    {
        IEnumerator moveto = MoveToInSeconds(mPlayer, pt, 0.5f);
        StartCoroutine(moveto);
    }
}
      
      



Wikidepia Design Patterns





Wikipedia Command Design Pattern





Refactoring Guru





Game Programming Patterns





Design Patterns in Game Programming






"Unity Game Developer. Professional".



" ".













All Articles