[Программирование, Разработка игр] Реализация паттерна проектирования (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Будущих студентов курса "Unity Game Developer. Professional" приглашаем посмотреть открытый урок на тему "Продвинутый искусственный интеллект врагов в шутерах".
А сейчас делимся традиционным переводом полезного материала.
В этом туториале мы освоим паттерн проектирования “Команда” (Command) и реализуем его в Unity в рамках системы перемещения игрового объекта.Знакомство с паттерном КомандаЗапросы, приказы и команды: все мы знакомы с ними в реальной жизни; один человек отправляет запрос (или приказ, или команду) другому человеку выполнить (или не выполнять) некоторые задачи, которые ему поручены. В проектировании и разработке программного обеспечения это работает аналогичным образом: запрос одного компонента передается другому для выполнения определенных задач в рамках паттерна Команда.
Определение: Паттерн Команда — это поведенческий паттерн проектирования, в котором запрос преобразуется в объект, который инкапсулирует (содержит) всю информацию, необходимую для выполнения действия или запуска события в более позднее время. Это преобразование в объекты позволяет параметризовать методы различными запросами, задерживать выполнение запроса и/или ставить его в очередь.
Хороший и надежный программный продукт должен основываться на принципе разделения обязанностей. Обычно его можно воплотить, разбив приложение на несколько уровней (или программных компонентов). Часто встречающийся на практике пример — разделение приложения на два уровня: графический интерфейс пользователя (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 PatternsWikipedia Command Design PatternRefactoring GuruGame Programming PatternsDesign Patterns in Game Programming
Узнать подробнее о курсе "Unity Game Developer. Professional".
Посмотреть открытый урок на тему "Продвинутый искусственный интеллект врагов в шутерах".
ЗАБРАТЬ СКИДКУ
===========
Источник:
habr.com
===========
===========
Автор оригинала: Shamim Akhtar
===========Похожие новости:
- [Разработка веб-сайтов, JavaScript, Программирование] Сниппет, расширение для VSCode и CLI. Часть 2
- [Программирование, Kotlin] Проверка на равенство в Kotlin (перевод)
- [Python, Программирование] Ультимативный гайд по поиску утечек памяти в Python
- [Программирование, Проектирование и рефакторинг] Универсальная архитектура приложений
- [Программирование, Разработка мобильных приложений, Разработка под Android, Kotlin] Практическое руководство по использованию Hilt с Kotlin (перевод)
- [Информационная безопасность, Читальный зал, Научно-популярное] Геоблокировка: как ограничивают доступ к информации и кому это нужно
- [Машинное обучение, Гаджеты, Научно-популярное, Звук] Шумно и стрессово? Или шумно и весело? Ваш телефон может слышать разницу (перевод)
- [Программирование, C++] Введение в регулярные выражения в современном C++ (перевод)
- [Программирование, Разработка игр] Семь талантливых стажеров AI@Unity 2020. Часть 2 (перевод)
- [Глобальные системы позиционирования, Программирование, Компиляторы, Lua, Робототехника] Umka и трактор: первый опыт практического применения нового языка
Теги для поиска: #_programmirovanie (Программирование), #_razrabotka_igr (Разработка игр), #_unity, #_gamedev, #_design_pattern, #_blog_kompanii_otus._onlajnobrazovanie (
Блог компании OTUS. Онлайн-образование
), #_programmirovanie (
Программирование
), #_razrabotka_igr (
Разработка игр
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 07:59
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Будущих студентов курса "Unity Game Developer. Professional" приглашаем посмотреть открытый урок на тему "Продвинутый искусственный интеллект врагов в шутерах".
А сейчас делимся традиционным переводом полезного материала. Определение: Паттерн Команда — это поведенческий паттерн проектирования, в котором запрос преобразуется в объект, который инкапсулирует (содержит) всю информацию, необходимую для выполнения действия или запуска события в более позднее время. Это преобразование в объекты позволяет параметризовать методы различными запросами, задерживать выполнение запроса и/или ставить его в очередь.
Диаграмма классов для паттерна проектирования КомандаДля реализации паттерна Команд нам потребуются абстрактный класс Command, конкретные команды (ConcreteCommandN) и классы Invoker, Client и Receiver.CommandВ роли Command обычно выступает интерфейс с одним или двумя методами выполнения (Execute) и отмены (Undo) операции команды. Все классы конкретных команд должны быть производными от этого интерфейса и должны реализовывать фактический Execute и, при необходимости, реализацию Undo. public interface ICommand
{ void Execute(); void ExecuteUndo(); } Создание игрокаТеперь мы создадим игровой объект Player. В этом туториале для представления игрока мы будем использовать объект Capsule. Кликните правой кнопкой мыши в окно Hierarchy и создайте новый игровой объект Capsule. Переименуйте его в Player.Создание скрипта GameManager.csВыберите игровой объект Ground и добавьте новый скриптовый компонент. Назовите скрипт GameManager.cs.Теперь мы реализуем перемещение объекта Player.Для этого мы добавляем public GameObject переменную с именем player. public GameObject mPlayer;
Реализация движений игрокаДля перемещения игрока мы будем использовать клавиши со стрелками (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; } } 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; } public int? myProperty { get; set; }
Nullable типы, являются экземплярами структуры System.Nullable. Тип, допускающий значение NULL, может представлять корректный диапазон значений для своего базового типа значения плюс дополнительное значение NULL. Например, Nullable<Int32>, который произносится как «Nullable of Int32», может быть присвоено любое значение от -2147483648 до 2147483647, а также ему может быть присвоен null. Nullable<bool> может быть присвоено значение true, false или null. Возможность назначать null числовым и логическим типам особенно полезна, когда вы имеете дело с базами данных и другими типами, которые содержат элементы, которым может не быть присвоено значение. Например, логическое поле в базе данных может хранить значения true или false, либо оно может быть еще не определено.
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; } ****
var clickPoint = GetClickPosition(); if (clickPoint != null) { IEnumerator moveto = MoveToInSeconds(_player, clickPoint.Value, 0.5f); StartCoroutine(moveto); } **** 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 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; } Теперь мы реализуем класс 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; } private Invoker mInvoker;
mInvoker = new Invoker();
// Undo
if (Input.GetKeyDown(KeyCode.U)) { mInvoker.Undo(); } 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(); } } 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); } } Узнать подробнее о курсе "Unity Game Developer. Professional".
Посмотреть открытый урок на тему "Продвинутый искусственный интеллект врагов в шутерах". ЗАБРАТЬ СКИДКУ =========== Источник: habr.com =========== =========== Автор оригинала: Shamim Akhtar ===========Похожие новости:
Блог компании OTUS. Онлайн-образование ), #_programmirovanie ( Программирование ), #_razrabotka_igr ( Разработка игр ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 07:59
Часовой пояс: UTC + 5