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);
}
}
Wikipedia Command Design Pattern
Design Patterns in Game Programming
"Unity Game Developer. Professional".
" ".