Введение Приветствую вас, дорогие читатели.
В этой статье пойдет речь о реализации простого менеджера асинхронно выполняемых задач для разработки.
Юнити3д .
Этот менеджер основан на так называемом Сопрограмма , которые присутствуют в движке.
Как обычно, прежде чем описывать реализацию и вдаваться в подробности, необходимо понять, что мы делаем и зачем нам это нужно.
Давайте рассмотрим простой пример, с которым, думаю, многие сталкивались при разработке игр.
У нас есть некий персонаж, который должен выполнить ряд действий: пойти в точку А, взять предмет, переместиться в точку Б, поставить предмет. Как видите, это нормальная последовательность.
В коде это можно реализовать по-разному, например самый примитивный вариант в одном Обновлять с условиями проверки.
Однако все усложняется, если таких персонажей у нас много и действий у них тоже довольно много.
Мне бы хотелось, чтобы наш код мог сообщать о таком персонаже, последовательно выполнять ряд действий и сообщать мне, когда вы закончите, пока я перехожу к другим вещам.
В этом случае асинхронный подход будет полезен.
На данный момент существует множество различных систем (в том числе для Единство ), которые позволяют это сделать, например, UniRx (реактивное асинхронное программирование).
Но все подобные вещи достаточно сложны для понимания и освоения начинающими разработчиками, поэтому попробуем использовать то, что нам предоставляет сам движок, а именно Сопрограмма .
Примечание : описанный выше пример — лишь один из многих; кроме этого есть множество областей, где можно описать подобную ситуацию: последовательная (или параллельная) загрузка ресурсов по сети, инициализация взаимозависимых подсистем, анимация в UI и т.д.
Выполнение
Прежде чем писать код и углубляться в C#, давайте посмотрим на архитектуру и терминологию.Итак, выше я написал для примера действия персонажа, которые он должен совершать.
В рамках системы, которая будет описана в этой статье, данное действие представляет собой определенное задание, которое должен выполнить персонаж.
Если обобщить это понятие, то задача — это любое действие, которое может выполнить любой объект игровой логики.
Задача должна подчиняться следующим правилам:
- Его можно запустить
- Вы можете подписаться на событие завершения
- Это можно принудительно остановить
Почему Subscribe возвращает ITask? Это лишь повышает удобство за счет возможности создания конструкции типа:public interface ITask { void Start(); ITask Subscribe(Action completeCallback); void Stop(); }
ITask myTask;
myTask.Subscribe(() => Debug.Log(“Task Complete”)).
Start();
Интерфейс задачи создан, но ему не хватает одной важной вещи — приоритета выполнения.
Для чего это? Представим себе ситуацию, когда мы поставили персонажу задачи и по логике вещей возникает ситуация, что он должен остановить все свои задания и выполнить другое, важное для игрового процесса.
В этом случае нам необходимо полностью остановить текущую цепочку и выполнить новую задачу.
Описанный пример — лишь один из нескольких вариантов поведения; Кроме того, приоритеты могут быть следующими:
- Обычный приоритет, каждая новая задача помещается в конец очереди.
- Высший приоритет, новая задача помещается в начало очереди.
- Приоритет с принудительной остановкой текущих задач
public enum TaskPriorityEnum
{
Default,
High,
Interrupt
}
public interface ITask
{
TaskPriorityEnum Priority { get; }
void Start();
ITask Subscribe(Action feedback);
void Stop();
}
Итак, с общим пониманием задачи мы определились, теперь нужна конкретная реализация.
Как описано выше, эта система будет использовать Сопрограмма .
Сопрограмма В простом смысле — это сопрограмма (если переводить буквально), которая выполняется основным потоком, но не блокируя его.
Благодаря использованию итераторов (IEnumerator) эта сопрограмма возвращается в каждом кадре, если внутри него происходит вызов.
доходность .
Давайте реализуем класс Task, который будет реализовывать интерфейс ITask. public class Task : ITask
{
public TaskPriorityEnum Priority
{
get
{
return _taskPriority;
}
}
private TaskPriorityEnum _taskPriority = TaskPriorityEnum.Default;
private Action _feedback;
private MonoBehaviour _coroutineHost;
private Coroutine _coroutine;
private IEnumerator _taskAction;
public static Task Create(IEnumerator taskAction, TaskPriorityEnum priority = TaskPriorityEnum.Default)
{
return new Task(taskAction, priority);
}
public Task(IEnumerator taskAction, TaskPriorityEnum priority = TaskPriorityEnum.Default)
{
_coroutineHost = TaskManager.CoroutineHost;
_taskPriority = priority;
_taskAction = taskAction;
}
public void Start()
{
if (_coroutine == null)
{
_coroutine = _coroutineHost.StartCoroutine(RunTask());
}
}
public void Stop()
{
if (_coroutine != null)
{
_coroutineHost.StopCoroutine(_coroutine);
_coroutine = null;
}
}
public ITask Subscribe(Action feedback)
{
_feedback += feedback;
return this;
}
private IEnumerator RunTask()
{
yield return _taskAction;
CallSubscribe();
}
private void CallSubscribe()
{
if (_feedback != null)
{
_feedback();
}
}
}
Несколько уточнений по коду:
- Статический метод Create необходим для удобства записи в виде:
Task.Create(.
).
Subscribe(.
).
Start()
- _coroutineHost это ссылка на экземпляр любого МоноПоведение объект, от имени которого будет запускаться задача (он же Сопрограмма ).
Ссылку можно передать, например, через статическую переменную
- В методе Подписаться подписчики добавляются с помощью +=, так как их может быть несколько (и это нам понадобится позже)
Итак, мы описали интерфейс задачи и сделали реализацию реализующего его класса, но для полноценной системы этого недостаточно; нам нужен менеджер, который будет контролировать выполнение задач по цепочке с соблюдением приоритетов.
Поскольку любая игра может иметь множество подсистем, которым может потребоваться собственный диспетчер задач, мы реализуем его как обычный класс, экземпляры которого будут создаваться и храниться всеми, кому это необходимо.
Реализация класса диспетчера задач.
public class TaskManager
{
public ITask CurrentTask
{
get
{
return _currentTask;
}
}
private ITask _currentTask;
private List<ITask> _tasks = new List<ITask>();
Теги: #unity3d #coroutine #асинхронные задачи #Разработка игр #Разработка игр #unity
-
Каковы Риски, Связанные С Hr-Аутсорсингом?
19 Oct, 24 -
Технокубок 2017-2018
19 Oct, 24 -
Введение В Reactiveui: Коллекции
19 Oct, 24 -
О «Туркине»
19 Oct, 24