Часто в играх, вне зависимости от жанра, требуется реакция внешнего мира на действия игрока согласно текущему состоянию игры, например, дверь может быть открыта ключом – таким образом “наличие ключа” – это состояние, а открытие двери – это реакция на наличие у игрока ключа. В данной статье мы рассмотрим пример разработки такой системы. Для этого мы сделаем следующее:
- Создадим систему состояний при помощи ScriptableObject
- Напишем кастомный инспектор для редактирования состояния
- Создадим систему реакций
- Соберем простую сцену для наглядной демонстрации работы кода.
Для начала давайте импортируем бесплатный контроллер, а также сделаем иерархию папок.
Создаем систему состояний в Unity 3D
Состояние игры будет выражаться условиями, которые будут представлять собой ScriptableObject, который будет содержать два поля – описание состояния и значение типа bool, отражающее выполнено ли условие или нет. Также стоит помнить, что ScriptableObject сохраняет своё состояние при выключении игры, однако работает это только в редакторе Unity, когда вы запустите билд – состояние сохраняться не будет, поэтому целесообразно определить метод Reset(), который будет сбрасывать состояние условия. Для это расширим класс ScriptableObject.
И создадим класс условий, унаследованный от него.
Создадим объект этого класса в папке “Conditions”.
Как мы можем видеть – мы не можем изменять состояние через инспектор, для этого мы напишем свой инспектор:
- Строка [CustomEditor…] определяет, для какого класса мы пишем инспектор.
- target хранит объект, для которого открыт инспектор
- 12 и 19 – самая главная часть кода, она отвечает непосредственно за значения полей, остальные строки – внешний вид инспектора, не стесняйтесь экспериментировать с ними.
Теперь инспектор для условий выглядит так:
Нам осталось лишь сделать скрипт для выполнения условия, вариантов, как это сделать множество, мы для примера выберем простой – условие будет выполнено, когда игрок зашел в триггер. Создадим класс, который будет проверять, есть ли вообще триггер на данном объекте и который будет реагировать только, если зашедший в триггер имеет определенный тэг.
- Require Component требует наличие коллайдера на объекте, ведь если нет коллайдера, то смысла в триггере нет.
- Затем в методе Awake() проверяется наличие триггера, и если его нет, то в консоль выводится Warning.
- Затем в методе OnTriggerEnter проверяется тэг зашедшего в триггер, и если этот тэг совпадает с одним из тех, что лежит в списке, то вызывается метод OnTriggerEnterWithTag(Collider other), который мы и будем использовать далее.
Теперь создадим скрипт, который будет выполнять условие, если игрок зашел в триггер.
- Скрипт очень простой, он содержит условие, и если кто-то зашёл в триггер, его выполняет.
Создаем систему реакций в Unity 3D
Теперь нам надо сделать то, что будет реагировать на выполнение условий, для этого создадим сначала абстрактный класс IReaction.
- В этом классе будет объявлен один метод – React()
Все реакции будут наследоваться от IReaction, они могут быть разными, например, включение/выключение объекта, включение звука, добавление предмета в инвентарь и тд, для простоты мы покажем пример на изменении позиции объекта.
Остался последний штрих – нечто должно проверять условия и вызывать реакции. Для примера сделаем проверку так же при пересечении триггера.
- Здесь у нас есть три списка: условия, которые должны быть выполнены, условия, которые должны быть не выполнены и реакции.
Демо-сцена в Unity 3D
Теперь соберем простую сцену, она будет содержать дверь, которая будет открываться только в случае, если мы были на кнопке.
Как мы можем видеть – условия сохраняются даже при выключении игры.
Чтобы этого не происходило создадим простой скрипт, который будет сбрасывать состояние в начале игры.
- Чтобы было проще получить доступ ко всем условиям – создадим папку Resources и положим их туда.
Итого, мы написали универсальную систему условий и реакций, а так же написали собственный инспектор для редактирования условий. Данная система очень гибкая и может быть использована почти что в любой игре.