Есть тут те кто дрочил простейшую стейт-машину на Unity? Я чего-то охуел от того какой это рак. Суть вот в чём. Замутил простой проект в котором с центре сцены куб. У куба есть компонент MonoBehaviour у которго 3 приватных переменных типа Color (понятное дело триколор), но они в [SerializeField] чтоб можно было устанавливать из инспектора.
Суть задачи в том чтоб куб находился постоянно в одном из трёх стояний: белый (элегантный), красный (красивый) или синий (свежий).
При входе в состояние выполняется какая-то логика. Между какими состояниями не важно, просто при переходе в элегантное состояние куб, например, перемещается немного влево.
Дальше состояние обрабатывается в каждом кадре (пердёж в консоль)
При выходе из состояния тоже выполняется логика
Что я нашёл so far. Можно сделать 3 функции OnStateEnter, OnStateExit и OnStateUpdate и делать в них switch/case как полный, блядь, обмудок. Если добавишь ещё состояний 5, то получишь колбасу кода в миллион строк. Это не элегантно, не свежно и не особенно красиво.
Можно нахуярить FSM на миллион строк (в 50 раз больше чем сама задача) и прописывать всё в отдельных файлах. Один большой "бонус" в том что если ты хочешь чтоб твоё состояние "элегантный" знало какой у него цвет, то будь добр сделай из приватных переменных публичные всем на посмешище. Ну и конечно в стейтах не забывай делать GetComponent от того компонента что эти цвета держат.
Отсюда вопрос: эт чего действительно такой рак и нет никакого паттерна попроще для решения довольно тривиальной задачи? Я видел как один дурачок удалял компоненты с GO и приделывал новые. Но мне бы хотелось услышать варианты решения от разумных людей
>>619199 (OP) Некоторые пользуются встроенным в animator конечным автоматом. Состояниям не обязательно присваивать анимации, и можно хуярить только логику. Не идеально, но для простых задач походит.
>>619260 Да, эту схему тоже пробовал. Из плюсов это то что всё визуально и ты в любой момент можешь поставить на паузу и посмотреть в каком стейте объект. Ещё не обязательно самому включать стейты, а достаточно передавать данные об объекте и если параметры стейтов настроены верно, то переключение произойдёт само.
НО! Здесь проблема в том что стейты ничего не знают о том кто их вызвал. Им в параметрах передаётся инстанс аниматора и ещё две ненужные херни. От инстенса аниматора можно получить GO и с него делать GetComponent. Но чтоб получить значения компонента (в моём случае это опять же цвета) их опять же нужно делать публичными. Мне не нужно иметь возможность менять их из других классов
>>619199 (OP) > Можно сделать 3 функции OnStateEnter, OnStateExit и OnStateUpdate и делать в них switch/case как полный, блядь, обмудок. А можно сделать четвёртую функцию SetState(State newState), которая выполняет несколько простых действий: 1. Берет активное состояние объекта. Состояние, допустим, это структ со ссылками на методы. 2. Отписывает если подписана функцию OnStateUpdate от реального Update. 3. Вызывает OnStateExit старого состояния. 4. Меняет состояние. 5. Вызывает OnStateEnter нового состояния. И усё. Никакие ифы и свитчи не нужны. Граф переходов между состояниями описан отдельно и является данными, но не логикой.
>>619335 > Берет активное состояние объекта. Состояние, допустим, это структ со ссылками на методы.
Это значит что у меня на 3 состояния должно быть условно 9 методов прописано в том же классе. Это не сильно отличается от switch/case или if'ов. Те же проблемы. Очень много кода который лучше всего отделить. Реализовать говнокодом проблем нет, у меня он изначально уже готов и работает говнокодом. Идеально если решение будет подходить под 3 пункта:
1. каждый стейт куба это отдельный класс 2. стейт имеет доступ к приватным переменным куба (это исключительно чтоб нельзя было извне менять значения кубу) 3. стейт можно переключать как из куба, так и из самого стейта
Из того что я знаю это не очень возможно хотя бы по той причине что переменные приватны и следовательно один стейт не может знать о существовании другого, и, как итог, на него переключиться. Но я надеюсь что я тупой и что за 60 лет существования геймдева такие тривиальные задачи научились решать красиво
>>619342 >>619335 > Состояние, допустим, это структ со ссылками на методы. Ну тогда делай не структ, а класс. Только тогда у тебя будет: >>619199 (OP) > Я видел как один дурачок удалял компоненты с GO и приделывал новые. Почему? Потому что эти стейты станут именно што полноценными компонентами.
>>619342 > на 3 состояния должно быть условно 9 методов Это фундаментальная проблема простых fsm, никуда от нее не деться без иерархических стейтмашин или деревьев.
>>619371 > читни gameprogrammingpatterns для начала Я то почитал, но было бы отлично если б ты конкретно указал какую идею оттуда упустил
>>619373 > Это фундаментальная проблема простых fsm Меня не беспокоит много методов, меня беспокоит что мой класс становитмя жирным и я бы их отделил
>не деться без иерархических стейтмашин или деревьев Если знаешь как лучше всего гуглить или где конкретно читать в максимально базовом виде, то буду благодарен
[Header("State colors")] [SerializeField] private Color idleColor; [SerializeField] private Color angryColor; [SerializeField] private Color scaredColor;
idleState = new CharacterState(OnEnter: IdleEnter); angryState = new CharacterState(OnEnter: AngryEnter); scaredState = new CharacterState(OnEnter: ScaredEnter);
>>619579 В оппосте я видел что-то про свич и дальше не читал. Я предлагаю сделать по классу для каждого состояния, который дальше уже будет производить актуальные действия
>>619579 Если у тебя увеличится количество состояний или действий, завязанных на состояния, ты охуеешь. Короче: создаёшь общий для всех статусов интерфейс, в классе куба создаёшь поле с этим типом. Создаёшь класс для каждого состояния, которые реализуют интерфейс. Когда нужно поменять состояние, просто создаёшь экземпляр конкретного класса, и берешь из него цвет для рендера. Не надо для каждого перехода в статус отдельный метод создавать
>>619604 > Когда нужно поменять состояние, просто создаёшь экземпляр конкретного класса, и берешь из него цвет для рендера
Что значит беру ИЗ него? То о чём ты говоришь не работает потому что экзеспляр класса состояния не знает переменных того экземпляря который это состояние носит. У меня IdleState не знает в какой цвет ему менять. Или ты где-то пропустил какой-то момент в этой схеме или я чего-то не понимаю
>>619612 А, вот в чем трабла, эт я затупил. Ну классам статусов нужна ссылка на экземпляр объекта. Можно пихать в конструктор, но лучше реализовать, как тут https://m.habr.com/ru/post/281783/ Если я верно понимаю, то ты все равно создаёшь по три метода на каждый стэйт - лучше держать методы в классах стэйтов, а данные брать из модели куба(ну или экземпляра класса, который их вызывает в простом случае).
>обязательно отпишусь В общем я попробовал все варианты что здесь и ни один из них не выглядит хоть как либо нормально. У каждого какая-то кривизна.
Со struct на маленьком проекте получилось хорошо, но когда перешёл в проект побольше, то 5 состояний у меня сожрало очень много строк и выглядело невозможно криво
С MVC получилась огромная мешанина кода где на 3 события мне пришлось делать и отдельные файлы и контроллер для каждого типа персонажа. Каждая модель при инициализации делает GameObject.FindObjectOfType<КлассКонтроллера>() что настоящая задница. Особенно учитывая что жалуется в той статье человек на безобидный, по сравнению с FindObjectOfType, GetComponent. Можно конечно это решить при помощи синглтонов и factory, но это самый настоящий overkill для такой простой задачи.
Тем временем открыл FPS Microgame и Creator Kit и посмотрел как у них реализован AI. И везде они делают enum и switch и прописывают в один длинный файл все события при всех возможных стейтах. Выглядит ужасно
>>620295 Ну а если без Мвк иметь классы статусов и передавать им вызывающий объект (this) из которого они будут тянуть значения, заданные в инспекторе?
Суть задачи в том чтоб куб находился постоянно в одном из трёх стояний: белый (элегантный), красный (красивый) или синий (свежий).
При входе в состояние выполняется какая-то логика. Между какими состояниями не важно, просто при переходе в элегантное состояние куб, например, перемещается немного влево.
Дальше состояние обрабатывается в каждом кадре (пердёж в консоль)
При выходе из состояния тоже выполняется логика
Что я нашёл so far.
Можно сделать 3 функции OnStateEnter, OnStateExit и OnStateUpdate и делать в них switch/case как полный, блядь, обмудок. Если добавишь ещё состояний 5, то получишь колбасу кода в миллион строк. Это не элегантно, не свежно и не особенно красиво.
Можно нахуярить FSM на миллион строк (в 50 раз больше чем сама задача) и прописывать всё в отдельных файлах. Один большой "бонус" в том что если ты хочешь чтоб твоё состояние "элегантный" знало какой у него цвет, то будь добр сделай из приватных переменных публичные всем на посмешище. Ну и конечно в стейтах не забывай делать GetComponent от того компонента что эти цвета держат.
Отсюда вопрос: эт чего действительно такой рак и нет никакого паттерна попроще для решения довольно тривиальной задачи? Я видел как один дурачок удалял компоненты с GO и приделывал новые. Но мне бы хотелось услышать варианты решения от разумных людей