Comments 16
Спасибо за статьи, было интересно почитать. Есть ли в планах статья про продвижение игры, успехи на GreenLight и т.п.?
0
В чем удобность команд?
Читал про StrangeIOC, там можно создавать цепочки из сигналов и команд. Но у вас такого не видел.
Еще, возможно, когда в команде много программистов, то удобнее, когда каждая операция в своем файле (проще мерж, лучше видно историю изменений).
Но не нравится мне, что надо создавать класс/файл на каждый чих.
Читал про StrangeIOC, там можно создавать цепочки из сигналов и команд. Но у вас такого не видел.
Еще, возможно, когда в команде много программистов, то удобнее, когда каждая операция в своем файле (проще мерж, лучше видно историю изменений).
Но не нравится мне, что надо создавать класс/файл на каждый чих.
0
Можете, пожалуйста, поуточнять? Что имеете ввиду под цепочками? У меня команда может вызывать другую команду — чем не цепочка. Для вас система недостаточно сложная? Просто для данной игры более сложная система не нужна. Вот когда делал Генералы — там было сложнее и команды были иерархическими, у команд были дети, сложная система событий, которая позволяла подписаться до команды, в середине команды, после команды, более сложная валидация. Каждому приложению ровно такая сложность, которая необходима, но не больше.
О да, я забыл, ведь создание файлов — это столь сложная операция. Гораздо удобнее каждый раз рыться в файле на тысячи строк. Поиском? Главное преимущество файлов — значительно легче искать то, что необходимо. Когда у тебя весь необходимый код вмещается на одном экране и другой экран просто не нужен) Один файл на 50-100 строк, файлы структурированы. Можно открыть несколько вкладок. Код, который сейчас не нужен — не путается под ногами. Вот мне необходима функциональность А и Г, а между ними — Б и В. Так я просто не открыл лишнее, а только два файла в разный вкладках — а так метаться постоянно видя лишний код.
И какие причины НЕ создавать файлы? Сплошные плюсы ведь)
О да, я забыл, ведь создание файлов — это столь сложная операция. Гораздо удобнее каждый раз рыться в файле на тысячи строк. Поиском? Главное преимущество файлов — значительно легче искать то, что необходимо. Когда у тебя весь необходимый код вмещается на одном экране и другой экран просто не нужен) Один файл на 50-100 строк, файлы структурированы. Можно открыть несколько вкладок. Код, который сейчас не нужен — не путается под ногами. Вот мне необходима функциональность А и Г, а между ними — Б и В. Так я просто не открыл лишнее, а только два файла в разный вкладках — а так метаться постоянно видя лишний код.
И какие причины НЕ создавать файлы? Сплошные плюсы ведь)
+1
Ну, в двухста файлах в одной папке тоже ничего хорошего. А группировать их не всегда удобно, иногда важно чтобы классы оставались в одном пространстве имен, а после переноса файлов в другую папку IDE будет активно мешать этому.
Лично я предпочитаю создавать отдельный файл на класс если в этом классе более 7 строк кода.
-1
Что имеете ввиду под цепочками?
commandBinder.Bind(GameEvent.HIT).To<DestroyEnemyCommand>().To<UpdateScoreCommand>();
commandBinder.Bind(GameEvent.HIT).InSequence()
.To<CheckLevelClearedCommand>()
.To<EndLevelCommand>()
.To<GameOverCommand>();
injectionBinder.Bind<ICommandBinder>().To<SignalCommandBinder>().ToSingleton(); // сигналы - новые, строго типизированные события.
https://strangeioc.github.io/strangeioc/TheBigStrangeHowTo.html
Это должно проделываться в Composition Root. Вызов одной команды из другой это явно плохая практика.
Кстати, в StrangeIOC команды могут быть асинхронными, в этом случае они конечно упрощают жизнь. Хотя я для этого использовал Promise, которые очень популярные в JS.
файле на тысячи строк
Я не говорил, что надо вообще все в один файл писать. Но создавать файл для каждой функции, еще и в 1-5 строк… это странно для меня. Хотя конечно можно в одном файле написать несколько классов.
Но остается проблема с GC. У вас некоторые команды создавались в каждом update, рано или поздно это может внести свою лепту в понижение fps.
Я не представляю как выглядел мой проект, если бы каждый метод я выносил бы в отдельный класс. Где-то должна быть эта грань.
0
Не знал, что время редактирования комментария столь ограничено. Немного изменил пример.
commandBinder.Bind(GameEvent.HIT).To<DestroyEnemyCommand>().To<UpdateScoreCommand>();
commandBinder.Bind(GameEvent.HIT).To<DestroyEnemyCommand>().Once();
commandBinder.Bind(GameEvent.HIT).InSequence()
.To<CheckLevelClearedCommand>()
.To<EndLevelCommand>()
.To<GameOverCommand>();
commandBinder.Bind<ShipDestroyedSignal>().To<ShipDestroyedCommand>(); // сигналы - новые, строго типизированные события.
0
Вызов одной команды из другой это явно плохая практика
Чем плохая?
Это должно проделываться в Composition Root.
Почему?
Как на меня в данной ситуации это лишнее усложнение, которое не несёт никакого позитива в данном случае.
commandBinder.Bind<ShipDestroyedSignal>().To<ShipDestroyedCommand>(); // сигналы - новые, строго типизированные события.
Я в игре могу подвесится прям на команды как на события. И тоже строготипизированно. Выглядит как-то так:
public void On(ShipDestroyedCommand ev) {
}
Но создавать файл для каждой функции, еще и в 1-5 строк
Почему для каждой? Есть много классов, где несколько методов. Да и все классы очень быстро растут. Нету смысла сейчас экономить файлы, чтобы потом их все-равно выносить. И как тогда файлы, которые содержат два-три класса называть? Другой вопрос — нужно ли такое разбиение и может неоторые классы должны нести чуть больше ответственности?
Я не представляю как выглядел мой проект, если бы каждый метод я выносил бы в отдельный класс
Я не выношу каждый метод в отдельный класс, не понимаю о чем вы говорите? Есть и довольно большие классы. Каждый класс у меня отвечает за то что должен и сейчас это довольно мало кода, но дальше он растет и классы растут. Я не могу понять, что вы предлагаете? Ну вот на практике, код, который вы читали выше — какие два класса объеденить в один?
в StrangeIOC команды могут быть асинхронными, в этом случае они конечно упрощают жизнь
Чем упрощают? Что для вас «асинхронные» в данном контексте?)
0
Почему?
При использование Composition Root, все связи видны как на ладоне. Как минимум это должно упростить жизнь новым программистам на проекте.
Я сейчас читаю книгу «Dependency Injection in .NET». Там об этом написано. Хотя там написано про инициализацию зависимостей, но думаю к сигналам и командам это тоже относится.
Я в игре могу подвесится прям на команды как на события. И тоже строготипизированно. Выглядит как-то так:
В StrangeIoC есть два вида событий: старые событий и новые сигналы. Я это имел ввиду.
Чем упрощают? Что для вас «асинхронные» в данном контексте?)
Команда выполняется в течении нескольких кадров. Проще вызвать другую команду в конце асинхронной команды. Иначе нужен будет или callback или promise.
Чтобы просто не создавать классы размером в 1000 строк, можно использовать шаблон стратегия. Хотя я часто пишу статические классы — утилиты или хелперы.
Суть команд, мне кажется, больше, чем просто декомпозиция большого класса.
0
Шикарная статья :) Спасибо автору
+1
вопрос по классу Command, bool Run и bool IsValid
как обрабатываются различные причины false на клиенте, когда например нужно отобразить разное сообщение пользователю почему та или иная команда не может быть выполнена?
как обрабатываются различные причины false на клиенте, когда например нужно отобразить разное сообщение пользователю почему та или иная команда не может быть выполнена?
0
Изначально у меня было что-то вроде такого:
И тогда каждая команда при ошибке возвращает не false, а код ошибки. Как часть сервера работает прекрасно — можно и узнать все возможные ошибки и сериализуется легко.
Сейчас же я понял, я никогда не проверяю статус, потому что вьюшка не позволяет запустить команду, которая может вернуть false. Правда, статусы самодокументируемые и отладка с ними легче, так что если бы нас в команде было хотя бы двое — обязательно бы их использовали.
public enum Status {
Success,
RoomNotEmpty,
NotEnoughItems,
CrewMemberIsFreezed
}
public abstract class Command
{
public Core Core { get; private set; }
public bool IsValid { get; private set; }
public Status Status { get; private set; }
public Command Execute (Core core)
{
Core = core;
Status = Run();
IsValid = (Status == Status.Success);
return this;
}
protected abstract Status Run ();
}
И тогда каждая команда при ошибке возвращает не false, а код ошибки. Как часть сервера работает прекрасно — можно и узнать все возможные ошибки и сериализуется легко.
protected override Status Run ()
{
if (Room.Building.Type != BuildingType.Empty) {
return Status.RoomNotEmpty;
}
if (Building.Type == BuildingType.Empty) {
return Status.CantBuildEmpty;
}
if (!new Pay(Building.Config.ConstructionCost).Execute(Core).IsValid) {
return Status.NotEnoughResources;
}
Room.Building = Building;
return Status.Success;
}
Сейчас же я понял, я никогда не проверяю статус, потому что вьюшка не позволяет запустить команду, которая может вернуть false. Правда, статусы самодокументируемые и отладка с ними легче, так что если бы нас в команде было хотя бы двое — обязательно бы их использовали.
0
вьюшка не позволяет запустить команду, которая может вернуть false
Это означает, что у вас бизнес-логика дублируется… Нарушение принципа DRY.
0
Вы правы.
Хотя если строго говорить, то валидация в командах у меня излишняя, а не дублируется (нарушением DRY было бы, если бы убрав эту валидацию, у меня перестало бы работать, а так — функционал игры совершенно не изменится). А сейчас эта валидация просто не нужна для корректной работы игры.
Я пишу эту валидацию скорее для более легкого покрытия тестами, допроверки глупых багов в юай и дополнительной декларативности. А еще потому что это что-то двух разных микросервисов, а там код тоже дублируется.
Хотя если строго говорить, то валидация в командах у меня излишняя, а не дублируется (нарушением DRY было бы, если бы убрав эту валидацию, у меня перестало бы работать, а так — функционал игры совершенно не изменится). А сейчас эта валидация просто не нужна для корректной работы игры.
Я пишу эту валидацию скорее для более легкого покрытия тестами, допроверки глупых багов в юай и дополнительной декларативности. А еще потому что это что-то двух разных микросервисов, а там код тоже дублируется.
0
Вот это я понимаю… гхм, пиар своего проекта. В Гринлайте выглядит интригующе, да и вообще задумка интересная. И когда в Steam появится?
0
Only those users with full accounts are able to leave comments. Log in, please.
Пишем игровую логику на C#. Часть 2/2