Паттерн состояния / поведения в C# в Unity. Рассказываю на примерах, как пользоваться
Урок по Unity, посвященный практическим примерам реализации паттерна проектирования под названием состояние или поведение.
Урок по Unity3D, однако именно эта тема подойдет для всех начинающих разработчиков, программирующих на C#.
___
Лавка Разработчика в других соц. сетях, добавляйтесь!
gamedevlavka - вконтактик
t.me/gamedevlavka - телеграм канал Лавки Разработчика
t.me/gamedevtavern - ламповый чат
/ discord - дискорд сервер
Пікірлер: 95
Наконец-то появился нормальный канал с действительно правильным контентом! Паттерны и архитектура на юнити! А не то что эти ваши, сделаем игру за 10 минут!
Огромное спасибо, ты находка, надеюсь еще будут видео про патерны (фабрика, наблюдатель и тд), желаю удачи в своем ремесле и на ютубе
Да, это видео не совсем из ряда "посмотрел, переписал и применяешь" тут нужно додумать, все равно углубиться и понять как это все работает, но не смотря на это, это наверное лучшее что есть на русском. Благодарю, и благодарю что продолжаешь делать видео.
Необходимость дописывать код Player при создании нового состояния - грубое нарушение принципа OCP. Методов SetBehavior/StateName/() не должно быть здесь, состояния должны изменяться внутри метода Update каждого состояния. В словаре тоже не вижу смысла, зачем он? Плюс, все, что касается состояний надо вынести из класса Player в класс PlayerBehaviorHandler, для соблюдения принципа SRP. Урок хороший, слушать очень приятно, спасибо!
@user-mb9bl3gr9i
Жыл бұрын
Крюков в помощь
@MARKEDEATH
Жыл бұрын
откройте форточку
@Spaceman68ok
Жыл бұрын
🤓
Отличный урок, планирую просмотреть все Ваши видео прежде чем пойти на собеседование)) Удачи в разработках
Отличный урок. Спать ибо за труд 🔥✨
Теперь я выучил новый паттерн! Спасибо большое
ОЧЕНЬ КАЧЕСТВЕННЫЕ ВИДЕО!!!!! УДАЧИ ТЕБЕ ДРУГ!!!! И БОЛЬШОЕ СПАСИБО!!!!!!!
Очень хороший канал, для тех, кто хочет устроиться на работу
@gamedevlavka
3 жыл бұрын
В точку!
Спасибо, путь благословит тебя Бог на твоем пути.
Лучшая реализация state что я видел) Беру на вооружение)
Это гениально, огромное спасибо!
Спасибо! Классный урок!
Хорошие видео, как новичку все понятно (новичку в Unity, не в ооп). Честно, тебя приятно слушать и смотреть, с удовольствием бы устроил кросс вечер за просмотром видео. Но их пока мало, нужно исправлять ситуацию)
@gamedevlavka
3 жыл бұрын
Спасибо) было бы больше времени, видео выходили бы чаще) к сожалению, пока не могу позволить себе :(
@PS-vj6jz
3 жыл бұрын
@@gamedevlavka потихоньку полегоньку)
Слов нет) Супер урок!
Для опытного разработчика все ясно и понятно. А вот для новичка не раскрыты сами поведения. Если будет время и желание приводите больше примеров, хоть и продолжительность роликов увеличится. Рекламу вставляйте что ли что бы мотивация была)
Скорее всего не хватит интерфейса, т.к поведение персонажа будет более сложным чем выводить в консоль, ведь будут зависимости с аниматорами, нав агентами и общими другими данными поведения, так что если и решите использовать полиморфизм то полезнее будут абстрактные классы, ну и композиционная составляющая
Очень круто мужик!
Спасибо все ясно .
Спасибо, полезно!
Спасибо тебе большое
3:22 Оговорочка по фрейду
Очень круто видео! Люблю твой канал. Если можно поясни пожалуйста. В моем понимании это три состояния в апдейте выполняют какую то движением, следовательно они вероятно имеют доступ ко всяким вещам которые относятся к МоноБехэйвор, так вот стоит их наследовать, прописыват логику там в апдейте и добавлять как компоненты к плееру, либо сразу их закидавать через сериалайзФилд. Или вообще так делать не стоит и можно передавать в них в методе СетБехеивор собственный МоноБехэйвор?
@gamedevlavka
2 жыл бұрын
Все верно, если тебе изнутри состояния нужно манипулировать трансформом, или игровым объектом, или зечем-то понадобился монобех, то это все просто передается в конструктор, и тогда состояние может им манипулировать.
Восхитительный контент, буквально закрыл все пробелы на джуна. Переписал проекты для портфолио, ранее утопал в синглтонах. Скоро иду на собеседование, спасибо вам большое. Удачи и развития каналу.
@gamedevlavka
3 жыл бұрын
Удачи на собеседовании!
@user-cm9hc5kn4i
2 жыл бұрын
Как успехи? )
Супер
И ещё, по поводу методов SetState ближе к концу можно реализовать интереснее, что бы вызывать один метод, с разными состояниями, задавая их условно через enum или же посидеть подумать, как реализовать прикольнее...
Спасибо за урок. Но вот нубский вопрос возникает. Как быть с анимациями? Должны ли мы внутри этих классов подключать еще и StateMachineBehaviour ?
Аллах, храни этого пасана
Все это хорошо, только не понятно а как теперь все соединить, например мне нужно получить данные из скрипта который висит на камере или изменять например параметры во время выполнения логики как мне до них добраться если все скрипты не монобехи?
Добрый вечер, я реализовал FSM у персонажа, состояние между собой переключаются,все работает как надо,но возник вопрос... У меня состояние jump, которые никак не могу реализовать,по сути Джамп должен вызываться в любой момент игры,поэтому я хочу вынести Джамп под отдельный компонент,потому что не знаю как можно реализовать состояние,у меня не будет механик по типу двойного прыжка и лазание по стенам... Как ты думаешь лучше подумать и реализовать в машине или лучше вынести его? Заранее спасибо...
По поводу проверки на наличие класса внутри интерфейса, разве нельзя сделать запись что то типо: this.PlayerBehavior?. Update(); Вопрос тип спрашивает, содержится ли в поле что нибудь... Я знаю, что это работает с классами, а с интерфейсами не пробовал. Почему задаю вопрос, потому что можно спросить в if неявно наличие экземпляра Например: if(Class) { выполнение при наличии класса } А с интерфейсами так не работает
Когда люди указывают тип в обощении - они обычно ожидают получить этот тип на выходе а не тип на уровень выше
блин, классно! Слушай а если не секрет, сколько ты лет уже занимаешься c# и юнити ?)
@gamedevlavka
3 жыл бұрын
C# 6 лет, unity 5 лет, в данный момент
Можете пожалуйста кто-то объяснить, зачем создавать отдельный метод GetBehaviour ? Можно и без него обойтись, допустим как в методе SetIdleBehaviour: private void SetIdleBehaviour() { SetBehaviour(_behavioursMap[typeof(PlayerIdleBehaviour)]); }
А в чем разница, если по итогу у тебя update забит if всеми состояниями?
Мне кажется, вы не очень корректный пример привели, и в данном случае, когда мы сами выбираем куда переключиться, данный паттерн является все же стратегией, состояния это типа как с балансом - мы хотим потратить деньги и уже от условий задаются состояния: 1) хватает - тратятся 2) не хватает - уведомляет 3) есть возможность в минус уходить - оформляется заем и т.д
@gamedevlavka
2 жыл бұрын
Да, вы абсолютно правы. Это уже обсуждалось в одном из комментариев
Здраствуйте, сделал небольшой рекфатрониг, что скажете? Кажется этот варинат более привлекательным: public class Player : MonoBehaviour { private Dictionary _behavioursMap; private IPlayerBehaviour _behaviourCurrent; private void Start() { InitBehaviours(); SetBehaviourByDefault(); } private void InitBehaviours() { _behavioursMap = new Dictionary(); _behavioursMap[typeof(PlayerBehaviourActive)] = new PlayerBehaviourActive(); _behavioursMap[typeof(PlayerBehaviourAggressive)] = new PlayerBehaviourAggressive(); _behavioursMap[typeof(PlayerBehaviourIdle)] = new PlayerBehaviourIdle(); _behavioursMap[typeof(PlayerBehaviourDie)] = new PlayerBehaviourDie(); } private void SetBehaviour(IPlayerBehaviour newBehaviour) { if (_behaviourCurrent != null) _behaviourCurrent.Exit(); _behaviourCurrent = newBehaviour; _behaviourCurrent.Enter(); } private void SetBehaviourByDefault() { SetNewBehaviour(); } private void Update() { if (_behaviourCurrent != null) _behaviourCurrent.Update(); } public void SetNewBehaviour() where T : IPlayerBehaviour { var type = typeof(T); SetBehaviour(_behavioursMap[type]); } }
А можно вопрос, допустим, есть состояния Walk, Run и Attack. Не совсем понятно, а как сочетать Run + Attack или Walk+ Attack?
@gamedevlavka
2 жыл бұрын
В случае возможности атаковать на ходу, так и прописывается в состоянии Walk, или Run. Прям внутри этих состояний. Но я бы для них сделал некий базовый класс, чтобы код не дублировать
Получается саму визуализацию (движение игрока, скорость, прыжки) мы реализовываем через компонент состояния? Например мне нужно чтобы PlayerBehaviorActive заставлял персонажа двигаться. Мне нужно привязать к нему Rigidbody2d, а значит сначала поместить этот скрипт на объект персонажа?
@gamedevlavka
2 жыл бұрын
На игроке будет Rigidbody, и его ссылку передаешь внутрь поведения
@nepochat
2 жыл бұрын
@@gamedevlavka понял, а каким методом передавать ссылку?
@gamedevlavka
2 жыл бұрын
Через конструктор
Можете пожалуйста объяснить откуда в коде взялось в методе SetBehaviour "behaviourCurrent = newBehavior"? Вот этот нью бехэйвиор - откуда? Это же не опечатка и при этом не объект класса Behaviour, но и красным не подчеркивает
@dronsan-unity7302
Жыл бұрын
В параметрах метода написан, когда используешь этот метод, ты в него должен прокинуть этот элемент
@olsney
Жыл бұрын
@@dronsan-unity7302 точно) пропустил этот момент) спасибо
Вы на полном серьезе создаете сначала Dictionary потому что состояний может быть много, но после вы создаете 3 метода которые будут отвечать за эти состояния, хотя сама их логика должна находиться в классах что унаследовали от интрфейса? А что если у меня 10 состояний под одни только анимации? Я что еще 10 методов написать должен чтобы патерн state работал? А разве он и не создан чтобы децентрализовать код и уменшить количество if и методов
@cerf14506
8 ай бұрын
Изаините что так грубо в начале комментария написал
Пару строчек кода с паттерном стратегия и немного абстракции - верная замена приведенному паттерну. Не вижу в нем необходимости, по крайней мере в данном примере
@gamedevlavka
3 жыл бұрын
Шаблоны стратегия и состояние (поведение) очень похожи. Существуют для разных задач: стратегия отвечает на вопрос "Как делать что-то при определенной стратегии", а состояние - "Что делать в определенном состоянии". Ключевое отличие в том, что паттерн состояния может сам переключать состояние своего "владельца", то есть например: когда закончится атака, состояние атаки переключит персонажа в состояние спокойствие. Стратегия так не работает. Согласен, переключение состояний "изнутри" не показано в примере, я решил, что это долго, и не стал этого делать, показал лишь идею самого паттерна, для чего он нужен.
@sadamskech6351
2 жыл бұрын
@@gamedevlavka Вообще не так это нужно реализовывать состояния для игрока. Нужно использовать State Machine Behaviours!!!
@andrey_aka_skif
Жыл бұрын
@@gamedevlavka без состояний, которые знают о других состояниях, паттерн де-факто вырождается в Стратегию. Да, Банда Четырех не конкретизирует, кто именно должен определять переход. Но они пишут, что правильнее и гибче позволить конкретным состояниям управлять переключением. На Рефакторинг-Гуру так и вовсе называют Состояние надстройкой над Стратегией. В том смысле, что это и есть Стратегия, только конкретные "стратегии" знают друг о друге. И ещё одно занятное наблюдение. Методы Start() и Exit() не являются обязательными для паттерна, но фигурируют в большинстве роликов. Кстати, нет в планах ролика об иерархических FSM или Pushdown automaton?
Довольно неплохое видео. Лишнего вообще ничего нет. Мне понравилось. Спасибо) Слушайте, не хотите взаимно добавить наши каналы в разделе на ютуб канале "Каналы". Думаю это даст взаимное продвижение роликов и развитие общества разработчиков. Ваша аудитория может заметить мой канал, а моя аудитория - ваш!
Очень круто! Но зачем вы везде пишете this? Это же не обязательно
@gamedevlavka
2 жыл бұрын
Я постоянно экспериментирую с кодстилем. Пытаюсь поймать баланс: 1. Максимально высокая стабильность 2. Максимально удобный процесс написания. 3. Кодстайл должен не отличаться от кодстайла юнити. Собственно, поэтому где-то есть this, где-то нет. Где-то еще какие-то необычность. Я не сторонник строгого следования кодстайлу Майкрософт. P.S. от this, кстати, я отказался)
Охуенно! Похоже на ECS
как красиво иначе хреначил все через enum в одном скрипте
в чём прикол делать словарь, если ты потом для каждого элемента создаёшь личную функцию?
вместо того, чтобы создавать кучу методов для каждого состояния, метод взятия, задания состояния по взятому..., да ещё и заводить словарь, можно было обойтись таким методом: public void SetBehavior() where T : IPlayerBehaviour { if (currentBehaviour != null) currentBehaviour.Exit(); currentBehaviour = Activator.CreateInstance(); currentBehaviour.Enter(); }
@gamedevlavka
2 жыл бұрын
Плохой вариант. Потому что часто внутрь поведения нужно передавать параметры + создание инстанса через активатор - ресурсоемкий процесс, + лишний алокейт памяти, а если внутри префаб какой создаётся, то вообще беда.
@asaki1k
2 жыл бұрын
@@gamedevlavka ок, видимо, я не совсем понял данный паттерн
И зачем ты там использовал this?
@gamedevlavka
2 жыл бұрын
Все просто: думал, что удобно. Оказалось, и без this удобно
@kingofbattleonline
2 жыл бұрын
@@gamedevlavka ссылка this используют как объект взять самого себя.
@kingofbattleonline
2 жыл бұрын
@@gamedevlavka и тем не менее, паттерн хорошо реализовали.
Очень сложная реализация стэйтмашины
Правильно behavioUr, а не behavior
@gamedevlavka
Жыл бұрын
Первый вариант британский, второй американский другой разницы между этими двумя словами не имеется
@justcore
Жыл бұрын
@@gamedevlavka Но в Unity куда не посмотри, везде пишут именно Behaviour. Думаю, лучше придерживаться этого для избежания путаницы
Послание новичкам: Это видео не для новичков. Инфа хорошая, но объяснений практически ноль.
Ахаха)) мля, ну ты жесткий конечно. Так усложнить statemashine///