Implémentation d'un modèle de conception

Nous invitons les futurs étudiants du cours " Unity Game Developer. Professionnel " à regarder la leçon ouverte sur le thème "Intelligence artificielle avancée des ennemis dans les tireurs".







Et maintenant, nous partageons la traduction traditionnelle de matériel utile.










Dans ce didacticiel, nous maîtriserons le modèle de conception Command et l'implémenterons dans Unity dans le cadre d'un système de mouvement d'objet de jeu.





Présentation du modèle de commande

Demandes, commandes et commandes : nous les connaissons tous dans la vraie vie; une personne envoie une demande (ou un ordre, ou une commande) à une autre personne pour exécuter (ou ne pas effectuer) certaines des tâches qui lui sont assignées. Dans la conception et le développement de logiciels, cela fonctionne de la même manière: une demande d'un composant est transmise à un autre pour effectuer des tâches spécifiques dans le modèle d'équipe.





: — , , () , . , / .





. , ( ). — : (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