Для одной незамысловатой игры мне понадобилось реализовать поведение ИИ с базовым функционалом: патрулирование, преследование и ведение боя. Сама по себе задача простая, однако, типов локаций было два и с разными уровнями абстракций. В одном случае действие проходило в замкнутых помещениях, а в другом - посреди городских улиц. На малых пространствах генерировалась сетка навигации, но в большой локации для сохранения производительности был использован поиск пути по графу.  Все виды поведений уже были написаны, а логика одинакова во всех локациях. Для ИИ не имело значения какой там используется поиск пути. Главное получить дорогу до цели и выполнить свою задачу! Для себя я выделил два решения. Первое заключалось в адаптации поведения под местность, например с помощью паттерна стратегия. Но в этом случае пришлось бы для каждого типа навигации писать дополнительную логику. Второе же решение предполагало унификацию данных поиска пути. При таком подходе ИИ не нужно было дополнять излишней логикой, а всю работу на себя брали поисковики! Основные объекты:  IPath<TPoint> (данные о пути) IPathProvider<TPoint> (поисковик или объект предоставляющий путь) IPathResponse<TPoint> (содержащий путь ответ, получаемый от поисковика) IPathRequestToken<TPoint> (токен для формирования ответа) Определим базовый интерфейс. В нем будет текущая точка, к которой движется бот, коллекция точек всего пути и единственный метод, при вызове которого мы узнаем можно ли следовать дальше по пути. Обобщения используются для того, чтобы не писать для каждого типа точек отдельную реализацию, ведь нам не важно Vector3 или Vector2 будет, может вообще другая структура данных понадобится. Хоть изначально IPath предназначался для простого представления данных о пути, однако в процессе написания кода лично я не хотел бы вручную контролировать смену точек, индексов, какие-то проверки на null, к тому же логика у всех ИИ в данном случае будет одинакова, а разница заключается лишь в функции перехода. Поэтому было добавлено определение метода Continue. Сделаем реализацию пути — пустого. Но для чего? Разве нельзя представлять пустой путь как null? И это возможный вариант, но он добавит нам ручной работы для проверок на существование данных, в то время как реализация в виде конкретного объекта просто не позволит боту передвигаться, т.к. все время будет возвращать отрицательный результат. Добавим стандартную реализацию для пути: Делегат Func<TPoint, TPoint, bool> ContinueFunc — нужен для проверки текущей цели (точки, к которой мы двигаемся). Если бот подойдет к цели, то ее логично будет сменить на следующую точку в пути. Этот делегат передается извне. Перечислитель IEnumerator<TPoint> PointsEnumerator — нужен для ручного обхода по коллекции точек. В конструкторе Path хорошо бы убедиться, что коллекция точек существует. При ее отсутствии нужно выбрасывать исключение: ранее было оговорено о нецелесообразности проверок на null в логике ботов, да и к тому же исключение позволит нам сразу видеть причину некорректного поведения. Имея на руках данные IPath уже можно начинать абстрагироваться от конкретных алгоритмов поиска путей. Но я посчитал это неполноценным решением. И причиной являются алгоритмы с асинхронным/многопоточным выполнением, где путь не может быть сразу возвращен при запросе. Да и к тому же нам все еще нужно знать к кому обращаться за поиском пути :) Для решения проблемы обращения к конкретным объектам, в моем случае это город и комнаты которые имеют данные о сцене, я решил обернуть их в провайдер. IPathProvider<TPoint> — интерфейс, сообщающий нам о том, что у объекта можно запросить путь нужного нам типа. Один объект может реализовывать несколько вариантов этого интерфейса. Определение поисковика: Определение ответа на запрос: IPathResponse<TPoint> содержит в себе путь Path и флаг Ready, сигнализирующий о завершении поиска пути провайдером. При асинхронном/многопоточном вычислении флаг не сразу может быть со значением true. Синхронная реализация ответа выглядит следующим образом: В данном случае не требуется ждать завершения поиска пути, так как он уже найден. Также в конструкторе нужно убедиться что путь существует и в ином случае выдать исключение. Для асинхронной реализации я решил добавить токен через который можно будет передать найденный путь. Это нужно для того, чтобы объект IPathResponse не протаскивался по разным частям программы и его положение было легко определить. Токен: По своему определению он действительно повторяет определение IPathResponse. По этой причине вы можете и не пользоваться токеном, а передавать и работать только с IPathResponse. Но как я уже объяснял выше, добавление данной прослойки помогает избежать протаскивания ответа через разные слои программы. Ответ: Реализация класса асинхронного/многопоточного ответа подходит и для синхронных вычислений. Здесь нет установки значений, только обращение к значениям токена и один статический метод для удобства создания объектов ответа и токена. По сути ответ передается тому, кто запросил путь, а у поисковика остается токен, через который он может передать значения по готовности. Теперь у нас есть все, чтобы абстрагироваться от конкретных алгоритмов при этом не меняя логику ИИ! К тому же мы избавились от надобности знать способ выполнения алгоритма: синхронный он или нет нас не интересует благодаря IPathResponse. В классе ИИ я определил поля для ответа и провайдера, а методе Update содержится логика запроса и следования: Функция для для перехода по точкам: Ну и пример поисковика: Посмотреть исходники можно здесь. Также там есть пара алгоритмов из игрушки.  C#, Automation, Unity