|
Unity Мультиплатформенный инструмент для разработки игр |
25.10.2020, 16:35
|
#1
|
Гигант индустрии
Регистрация: 13.09.2008
Сообщений: 2,893
Написано 1,185 полезных сообщений (для 3,298 пользователей)
|
LeoECS
Привет всем. Случайно вспомнил про булку, зашел и оказывается тут еще есть активность.
Вступление:
В некоторых темах увидел тред где Crystal с "процедурными" знаниями Блитза в очередной раз пытается сделать свой фаллаут, но только уже на Юнити и как ему сложно. Увидел как Кирпи4 спрашивает, как новичку влиться в геймдев. И вспомнилось былое время, когда у меня тоже не было знаний, но было много вопросов и как сложно было раскуривать ооп.
Но спасение есть. Когда-то давно на конференции Юнити представили Entitas фреймворк с ECS для Юнити и после этого эта тема всё больше и больше форсится, а многие мелкие студии уже давно перешли на ECS так как по уровню сложности это примерно как визуальное программирование и любой новичок крайне быстро может во всем разобраться и влиться в геймдевчик.
Самих ECS множество со своими плюсами, минусами и интеграцией в Юнити ( DOTS/uECS). Но я напишу тут пример кода на LeoECS так как он максимально простой, позволяет делать "как хочешь" в отличии от DOTS/uECS, но при этом достаточно быстрый, изначально не привязан к Юнити и без кодогенерации как тот же Entitas.
Что к чему:
Краткая суть почему ECS это хорошо заключается в том, что практически не нужно уметь кодить, не нужно знать тонну паттернов и прочих сложностей. Все просто, как и в визуальном программировании, но только там соединяем ноды, а тут печатаем кусочки кода.
Так же использование ECS идеально подходит для небольших игр на мобилки типа Match3, но и большие тоже можно писать.
Сам ECS из себя представляет:
World - "мир" в котором всё крутится
System - системы, которые обрабатывают данные
Entity - фактически контейнер, который содержит в себе компоненты
Component - сам компонент представляющий собой структуру в которой храниться несколько значений.
*собственно Entity и Component это тоже самое, что и в Юнити GameObject и Component. Но только в Юнити свернули не туда
Ещё ECS можно представить просто как конвейер ( World) по которому движутся коробки ( Entity) в которых что-то лежит ( Component), а рядом стоящие работники ( System) берут эти коробки ( Entity), что-то делают с ними или c их содержимым ( Component), а затем или возвращают коробку ( Entity) обратно на конвейер ( World) или вовсе "выбрасывают в мусорку".
Особенность всего этого в том, что System, Entity и Component ничего не знают друг о друге и вся магия происходит благодаря System.EcsFilter<T, ..>, который берет все Entity в текущем "мире", сортирует их по наличию запрашиваемых компонентов на них и возвращает пользователю грубо говоря массив с Entity.
Ресурсы:
Для начала нам нужно скачать ECS и два модуля к нему для связи с Юнити (это кстати единственная сложность в использовании стороннего ECS).
LeoECS: https://github.com/Leopotam/ecs
(тут так же есть краткая документация, ссылки на другие модули, ссылки на примеры кода и на некоторые игры сделанные на этом ECS)
Unity integration: https://github.com/Leopotam/ecs-unityintegration
(это нужно просто для дебага ECS в редакторе Юнити)
Service Locator: https://github.com/Leopotam/globals
(это фактически синглтон для вызова ECS из классов MonoBehaviour)
Простой пример:
После добавления ресурсов с гитхаба в Юнити нужно нажать правой кнопной мышки например на папку Assets и выбрать " Create > LeoECS > Create Startup template". Это создаст MonoBehaviour класс с инициализацией ECS.
Данный юнити-компонент нужно добавить на игровой объект в сцене.
using Leopotam.Ecs;
using LeopotamGroup.Globals;
using UnityEngine;
namespace Example {
internal class EcsStartup : MonoBehaviour {
private EcsWorld world = default;
private EcsSystems systems = default;
// Это нужно для интеграции с Юнити так как сам ECS изначально
// никак не связан с ним.
// В небольшом комьюнити по LeoECS принято использовать этот шаблон где:
// GameData используется для реалтайм-данных так как System, Entity и Component
// ничего не знают друг о друге.
// [System.Serializable]
// public class GameData {
// public int shootCount = 0;
// public int collisionsCount = 0;
// }
[SerializeField] private GameData gameData = new GameData();
// SceneData наследуется от MonoBehaviour и содержит в себе ссылки
// на объекты сцены. Например это камера и какие-то
// заранее созданные объекты. Я в этом примере заранее создал
// игрока и сделал ссылку на его игровой объект.
// public class SceneData : MonoBehaviour {
// ссылка на объект игрока, который уже находится на сцене
// public PlayerView PlayerView;
// //public Camera mainCamera;
// //...
// }
[SerializeField] private SceneData sceneData = default;
// Config наследуется от ScriptableObject и служит для хранения
// всяких настроек, ссылок на префабы, которые в какой-то момент
// нужно будет создать. Так же в нем можно хранить ссылки на
// другие ScriptableObject с конфигами.
// Именно этот класс сугубо только для удобства. Данные можно
// так же классическим способом хранить и на MonoBehaviour игрока, врага.
//[CreateAssetMenu]
// public class Config : ScriptableObject {
// Данные игрока, которые можно изменять в реальном времени
// и которые сохраняться после выхода из игрового режима в редакторе
// public float PlayerMoveSpeed = 1f;
// public float PlayerRotateSpeed = 50f;
// [Space]
// public ProjectileView ProjectilePrefab = default;
// public float ProjectileStartSpeed;
// }
[SerializeField] private Config config = default;
private void Start() {
world = new EcsWorld();
systems = new EcsSystems(world);
// Это пак Service Locator, который является аналогом синглтона.
// Для примера сохраним ссылку на реалтайм-данные
Service<GameData>.Set(gameData);
// Это пак Unity integration, который нужнен для дебага ECS в сцене.
// Так же именно это создаёт некоторое количество аллокаций так как
// для наглядности при добавлении Entity или компонентов изменяет
// имя скрытых игровых объектов, которые отображаются в дебаге.
// Скрин в аттаче
#if UNITY_EDITOR
Leopotam.Ecs.UnityIntegration.EcsWorldObserver.Create(world);
Leopotam.Ecs.UnityIntegration.EcsSystemsObserver.Create(systems);
#endif
// Это хендл для систем. Тут в список добавляются системы.
systems
// Сам процесс добавления систем. Если не добавлять систему или если
// что-то закомментить, то у нас просто пропадёт часть функционала
// без каких либо ошибок. Один из серьезных плюсов.
.Add(new PlayerInitSystem())
.Add(new PlayerInputSystem())
.Add(new PlayerMoveSystem())
.Add(new PlayerRotateSystem())
// Удаляет все экземпляры указанного компонента со всех Entity и со
// всех тут указанных систем.
// Этот метод специфичен и иногда при неправильном добавлении
// систем создаёт проблемы. Так как если сначала в список
// добавить систему с обработкой компонента, а ниже добавить
// систему с генерацией компонента, то естественно на следующем
// цикле этот компонент уже будет удален и система с обработкой
// компонента уже не будет работать.
.OneFrame<PlayerInputEvent>()
// Это Dependency injection данных во все системы в текущем EcsSystems
.Inject(gameData)
.Inject(sceneData)
.Inject(config)
// Инициализация EcsSystems
.Init();
}
// Тут вызывается цикл EcsSystems. Если нужно использовать FixedUpdate,
// то нужно создать еще одну систему и по аналогии вызвать.
// Важно помнить, что Entity с компонентами хранятся в EcsWorld и добавление
// новых или удаление EcsSystems повлияет только на функционал.
private void Update() {
systems?.Run();
}
// Очистка системы и "мира".
// При добавлении новых EcsSystems в мир нужно будет не много переделать
// код дабы сначала удалить все EcsSystems, а потом и сам "мир".
private void OnDestroy() {
if (systems != null) {
systems.Destroy();
systems = null;
world.Destroy();
world = null;
}
}
}
}
В идеале система должна выполнять минимально количество кода. Из-за этого количество систем может быть и 10 и 100 и соответственно лучше сразу называть их понятным для себя образом.
Первая система называется у нас PlayerInitSystem. В ней мы создаем Entity и связываем с игроком на сцене.
using Leopotam.Ecs;
using UnityEngine;
internal class PlayerInitSystem : IEcsInitSystem {
// Тот самый Dependency injection.
// Для EcsWorld выполняется автоматически, а все остальное нужно добавлять
// руками через .Inject(..)
private readonly EcsWorld ecsWorld = default;
private readonly SceneData sceneData = default;
// IEcsInitSystem.Init для вызова на старте игры или при вызове EcsSystem.Init()
public void Init() {
// Получаем ссылку на PlayerView
var view = sceneData.PlayerView;
// Создаем новый Entity. Все Entity находятся в пуле. Если свободный Entity
// уже есть, то тогда берется из пула
var entity = ecsWorld.NewEntity();
// Добавляем в Entity компонент PlayerRef и сохраняем ссылку на PlayerView
// из которого можно будет получить ссылку на transform и другие компоненты
entity.Get<PlayerRef>().View = view;
// В сам PlayerView тоже сохраняем ссылку на Entity
view.Entity = entity;
}
}
public class PlayerView : MonoBehaviour {
public EcsEntity Entity;
// Фактически PlayerView не нужен, достаточно только в ECS-компоненте
// сохранить ссылку на transform игрока.
// Но при таком подходе потом при вызове любого MonoBehaviour события
// допустим OnCollisionEnter можно будет сразу добавить нужный компонент
// на Entity игрока
// private void OnCollisionEnter(Collision other) {
// Entity.Get<PlayerCollideEvent>();
// }
}
internal struct PlayerRef {
public PlayerView View;
}
Далее у нас система PlayerInputSystem в которой обрабатываем Input. Вообще нужно делать по другому, но для примера будет достаточно.
using Leopotam.Ecs;
using UnityEngine;
internal class PlayerInputSystem : IEcsRunSystem {
// Тут в EcsFilter<T> мы указываем с каким компонентом нам нужны Entity.
// Компонентов можно задать больше
// Так же можно использовать EcsFilter<T, ..>.Exclude<T, ..> для исключения
// определенных компонентов из результата
private readonly EcsFilter<PlayerRef> filter = default;
// IEcsRunSystem.Run этот метод вызывается в каждом цикле
public void Run() {
// В данном случае это нужно только для того, что бы не вызывался
// лишний раз Input и создание Vector2 если вдруг у нас пустой фильтр
if(filter.IsEmpty())
return;
var h = Input.GetAxisRaw("Horizontal");
var v = Input.GetAxisRaw("Vertical");
var direction = new Vector2(h, v).normalized;
// Проходим по всем элементам. Тут за foreach можно не переживать
// Код с Input'ом можно перенести и сюда, но это будут лишние
// вычисления если у нас например в фильтре будет не 1, а 1000 Entity
if (direction.sqrMagnitude > 0f) {
foreach (var i in filter) {
// Получаем ссылку на Entity данной итерации и добавляем
// компонент, который заканчивается на Event. Таким образом
// мы только для себя обозначаем, что этот компонент должен
// быть удален после использования всеми системами.
// В данном случае он будет удален в конце главного цикла
// через метод .OneFrame<PlayerInputEvent>()
filter.GetEntity(i).Get<PlayerInputEvent>().Value = direction;
}
}
}
}
internal struct PlayerInputEvent {
public Vector2 Value;
}
Система PlayerMoveSystem будет двигать все Entity с определенным количеством компонентов
internal class PlayerMoveSystem : IEcsRunSystem {
// Как видим тут мы фильтруем по наличию компонентов PlayerRef
// и PlayerInputEvent, которые в данном примере должны быть только
// у Entity игрока.
private readonly EcsFilter<PlayerRef, PlayerInputEvent> filter = default;
// DI конфига
private readonly Config config = default;
public void Run() {
// Тут уже не нужны никакие доп. вычисления типа Input как в прошлой системе
foreach (var i in filter) {
// Обратите внимание на "ref". Так как у нас компоненты
// являются "struct", мы должны получать ссылку на них
// именно через ref иначе мы получим копию, которая
// ничего не изменит и удалится сразу же после выхода
// из этого метода.
// Через Get1, Get2, GetN мы получаем компонент из Entity.
// Нумерация компонентов такая же как в EcsFilter
ref var playerRef = ref filter.Get1(i);
ref var inputEvent = ref filter.Get2(i);
// Получаем ссылку на transform игрока через PlayerView,
// который до этого сохранили в компоненте PlayerRef
// Так же получаем из конфига скорость игрока,
// которую можно менять в реальном времени.
// Если у нас будет 1000 игроков в фильтре,
// то скорость изменится для всех.
var tr = playerRef.View.transform;
tr.position += tr.forward * inputEvent.Value.y * config.PlayerMoveSpeed * Time.deltaTime;
}
}
}
Следующая система фактически копи-паст предыдущей и может возникнуть вопрос нафига этот лишний код? А ответ тот же, что и был выше - если отключить систему или если закоментить её подключение в коде, то в игре просто пропадёт функционал, который содержится в системе и никаких ошибок не будет.
internal class PlayerRotateSystem : IEcsRunSystem {
private readonly EcsFilter<PlayerRef, PlayerInputEvent> filter = default;
private readonly Config config = default;
public void Run() {
foreach (var i in filter) {
ref var playerRef = ref filter.Get1(i);
ref var inputEvent = ref filter.Get2(i);
// Все тоже самое, но только мы тут разворачиваем игрока
// Конечно можно объединить в одну, но тогда мы теряем
// ту самую гибкость ECS
var tr = playerRef.View.transform;
tr.Rotate(Vector3.up, inputEvent.Value.x * config.PlayerRotateSpeed * Time.deltaTime);
}
}
}
|
(Offline)
|
|
Эти 3 пользователя(ей) сказали Спасибо Nex за это полезное сообщение:
|
|
25.10.2020, 16:36
|
#2
|
Гигант индустрии
Регистрация: 13.09.2008
Сообщений: 2,893
Написано 1,185 полезных сообщений (для 3,298 пользователей)
|
Ответ: LeoECS
А сейчас добавим возможность пострелять. Для этого создайте на сцене некоторое количество объектов с коллайдерами, которые будут являться препятствием для пули.
Так же нужно создать системы и добавить их в список в EcsStartup классе.
.Add(new PlayerInputShootSystem())
.Add(new ProjectileCreateSystem())
.Add(new ProjectileMoveSystem())
.Add(new ProjectileDestroyEventSystem())
Система PlayerInputShootSystem будет создавать Entity с компонентом PlayerShootEvent и некоторым данными для создания пули.
Код очень похож на предыдущую систему где создавался компонент с событие движения игрока.
Конечно системы можно унифицировать дабы они двигали сразу все Entity допустим с компонентом Move, но в этом случае мы потеряем гибкость и при отключении этой системы перестанут двигаться и игрок и пули.
internal class PlayerInputShootSystem : IEcsRunSystem {
// Тут для пример вместе с EcsFilter<T> есть еще и .Exclude<T> специально
// что бы пули не могли создаваться если игрок движется или
// вращается (если на Entity игрока есть компонент PlayerInputEvent)
private readonly EcsFilter<PlayerRef>.Exclude<PlayerInputEvent> filter = default;
private readonly EcsWorld ecsWorld = default;
private readonly Config config = default;
private readonly GameData gameData = default;
public void Run() {
if (filter.IsEmpty())
return;
if (!Input.GetKeyDown(KeyCode.Space))
return;
foreach (var i in filter) {
ref var playerRef = ref filter.Get1(i);
var tr = playerRef.View.transform;
ecsWorld.NewEntity().Get<PlayerShootEvent>() = new PlayerShootEvent() {
Position = tr.position,
Angle = tr.rotation.eulerAngles.y,
Speed = config.ProjectileStartSpeed
};
// Как помним GameData класс был у нас для временных реалтайм-данных.
// В данном случае при выстреле увеличиваем переменную и
// для примера можно где-то её отрисовать в UI или можно
// посмотреть её значение в редакторе так как у нас
// этот класс сериализован
gameData.shootCount++;
}
}
}
// Данный компонент хоть и заканчивает на "Event", но добавлять
// его в .OneFrame<T> не будем потому-что он создается вместе
// с отдельным Entity. В этом случае лучше удалить руками
// сам Entity, а вместе с ним удалится и этот компонент
internal struct PlayerShootEvent {
public Vector3 Position;
public float Angle;
public float Speed;
}
В системе ProjectileCreateSystem создаем префаб и Entity пули. Так же обязательно удаляем Entity с событием PlayerShootEvent, которое сигнализирует о необходимости создания пули. Иначе пули будут создаваться в каждом цикле * на количество Entity в фильтре.
internal class ProjectileCreateSystem : IEcsRunSystem {
// Получаем все Entity с PlayerShootEvent
private readonly EcsFilter<PlayerShootEvent> filter = default;
private readonly EcsWorld ecsWorld = default;
private readonly Config config = default;
public void Run() {
foreach (var i in filter) {
ref var eventData = ref filter.Get1(i);
var position = eventData.Position;
position.y = 0.5f;
var rotation = Quaternion.Euler(0, eventData.Angle, 0);
// Так же создаем и связываем игровой объект и Entity.
var entity = ecsWorld.NewEntity();
ref var projectileRef = ref entity.Get<ProjectileRef>();
// На Entity пули добавляем постоянный компонент,
// который будет хранить постоянную скорость пули
entity.Get<ProjectileMove>().Value = eventData.Speed;
var view = UnityEngine.Object.Instantiate(config.ProjectilePrefab, position, rotation);
view.Entity = entity;
projectileRef.View = view;
// И обязательно удаляем текущий Entity, который содержит
// компонент-событие для создания пули
filter.GetEntity(i).Destroy();
}
}
}
// ref - компонент для Entity пуль
internal struct ProjectileRef {
public ProjectileView View;
}
// View - компонент для пули. Так же тут показан пример как из MonoBehaviour
// взаимодействовать с Entity игрового объекта
public class ProjectileView : MonoBehaviour {
public EcsEntity Entity;
// Если пуля столкнулась с чем-то кроме игрока (PlayerView),
// то добавляем на Entity пули компонент-событие ProjectileDestroyEvent
// благодаря которому эта пуля будет удалена в последующей системе
private void OnTriggerEnter(Collider other) {
if (other.GetComponent<PlayerView>())
return;
// Просто пример как использовать это.
// В данном случае мы получаем ссылку на реалтайм-данные и увеличиваем
// значение переменной, которая будет показывать количество
// столкновений пуль за игру
// Можно так же получать ссылку на мир EcsWorld и создавать Entity
Service<GameData>.Get().collisionsCount++;
// Для примера добавлен параметр в который записывается
// игровой объект с которым произошло столкновение
Entity.Get<ProjectileDestroyEvent>() = new ProjectileDestroyEvent() {
Value = other.gameObject
};
}
}
В данной небольшой системе мы просто двигаем все пули с постоянной скоростью, которая сохранена в компоненте ProjectileMove. Опять же это сохранение скорости пули только как пример. Данные скорости пули можно так же брать из Config, как и в системе с движением игрока.
internal class ProjectileMoveSystem : IEcsRunSystem {
private readonly EcsFilter<ProjectileRef, ProjectileMove> filter = default;
public void Run() {
foreach (var i in filter) {
// Помним что "ref" нужен обязательно при получении компонентов,
// которые являются "struct"
ref var projectileRef = ref filter.Get1(i);
ref var projectileMove = ref filter.Get2(i);
// Непосредственно двигаем игровой объект на сцене
var step = projectileMove.Value * Time.deltaTime;
var transform = projectileRef.View.transform;
transform.position += transform.forward * step;
}
}
}
internal struct ProjectileMove {
public float Value;
}
А в этой последней системе ProjectileDestroyEventSystem просто удаляем и игровой объект и Entity. Сам Entity не удалится с "мира", а только пометится как удаленный и в любой момент будет использован заново.
internal class ProjectileDestroyEventSystem : IEcsRunSystem {
private readonly EcsFilter<ProjectileRef, ProjectileDestroyEvent> filter = default;
public void Run() {
foreach (var i in filter) {
ref var projectileRef = ref filter.Get1(i);
ref var destroyData = ref filter.Get2(i);
// Тут просто для примера выводим к консоль название
// игрового объекта с которым столкнулась пуля
Debug.Log(destroyData.Value);
// Удаление gameObject пули на сцене и Entity из "мира"
Object.Destroy(projectileRef.View.gameObject);
filter.GetEntity(i).Destroy();
}
}
}
internal struct ProjectileDestroyEvent {
public GameObject Value;
}
Всё. Выглядит это конечно более сложнее, чем классический код и синхронизация с игровыми объектами на сцене тоже такое себе. Но за то мы получаем максимально разграниченный функционал, который не зависит друг от друга. Так же это всё легко тестится и расширяется хоть до уровня ММО-игры.
|
(Offline)
|
|
Эти 3 пользователя(ей) сказали Спасибо Nex за это полезное сообщение:
|
|
25.10.2020, 16:49
|
#3
|
Быдлокодер
Регистрация: 05.07.2009
Адрес: Проспит
Сообщений: 5,024
Написано 2,313 полезных сообщений (для 5,350 пользователей)
|
Ответ: LeoECS
Сообщение от Nex
Краткая суть почему ECS это хорошо заключается в том, что практически не нужно уметь кодить, не нужно знать тонну паттернов и прочих сложностей. Все просто, как и в визуальном программировании, но только там соединяем ноды, а тут печатаем кусочки кода.
|
Сообщение от Nex
Выглядит это конечно более сложнее, чем классический код и синхронизация с игровыми объектами на сцене тоже такое себе. Но за то мы получаем максимально разграниченный функционал, который не зависит друг от друга. Так же это всё легко тестится и расширяется хоть до уровня ММО-игры.
|
Так ECS легко или сложно?
Сообщение от Nex
Crystal с "процедурными" знаниями Блитза в очередной раз пытается сделать свой фаллаут
|
Это «покемоны».
|
(Offline)
|
|
25.10.2020, 17:20
|
#4
|
[object Object]
Регистрация: 01.08.2008
Адрес: В России
Сообщений: 4,361
Написано 2,473 полезных сообщений (для 6,857 пользователей)
|
Ответ: LeoECS
Сообщение от Arton
Так ECS легко или сложно?
|
Блиц был про "легко начать, но тяжело закончить".
Тут наоборот.
Это сложно или легко? [риторический вопрос]
__________________
Retry, Abort, Ignore? █
Intel Core i7-9700 4.70 Ghz; 64Gb; Nvidia RTX 4090 3070
AMD Ryzen 7 3800X 4.3Ghz; 64Gb; Nvidia 1070Ti
AMD Ryzen 7 1700X 3.4Ghz; 8Gb; AMD RX 570
AMD Athlon II 2.6Ghz; 8Gb; Nvidia GTX 750 Ti
|
(Offline)
|
|
Сообщение было полезно следующим пользователям:
|
|
26.10.2020, 01:29
|
#5
|
Терабайт исходников
Регистрация: 05.07.2007
Сообщений: 5,196
Написано 1,721 полезных сообщений (для 5,374 пользователей)
|
Ответ: LeoECS
Когда мне надоест программировать фоллауты, я возьму UE с его нодами.
Смысла перехода на LeoECS не вижу имхо. Так же можно было предложить
на огре по кодить, по тому что "это другое", а смысл? ХЗ.
Нужны более лёгкие варианты? - UE.
Сообщение от Arton
Так ECS легко или сложно?
Это «покемоны».
|
Не совсем. Этот проект делается под впечатлением
от покемонов и фоллаутов с частицей героев меча и магии.
Так что да всё же это очередной фоллаут.
Прошлый мой фоллаут кстати 10 лет назад успешно создавался,
был уже набор механик собрав которые в одну кучу можно
было бы вывалить демо уровень игры. Там и управление персонажем,
и поиск пути, и инвентарь, и редактор уровня, и загрузка с сохранением.
Проект был закрыт по двум причинам:
1. Незаинтересованность аудитории.
2. Смена сферы интересов (мотоцикл, бабы, бухло, рокнролл).
Щас я откатил интересы на 10 лет назад, и проект успешно
производится потихонечку, не смотря на отсутствие заинтересованности
в нём кем либо кроме меня. Я бы даже сказал, что это тот самый фоллаут,
который я делал 10 лет назад, но пересмотренный под другим углом.
Будет всё то, что планировалось и плюсом покемоны,
но сюжет выкинут в мусорку, и придуман новый.
Хотя можно что-то из старого проекта впендюрить в этот по сюжету,
у меня как раз несколько фракций будет, вот Бимелов из "Выжженной Земли"
можно и добавить. Главный плюс нового фоллаута в том, что он совмещает
в себе реалтайм игру с пошаговыми боями, а вариант 10 лет назад
должен был быть чисто реалтаймом, без пошаговости, как фоллаут тактикс.
Короче новый вариант ближе к первым двум фоллаутам, но с боями как
в четвёртых героях.
|
(Offline)
|
|
Эти 2 пользователя(ей) сказали Спасибо Crystal за это полезное сообщение:
|
|
26.10.2020, 04:07
|
#6
|
Разработчик
Регистрация: 12.01.2011
Адрес: Moscow
Сообщений: 424
Написано 70 полезных сообщений (для 103 пользователей)
|
Ответ: LeoECS
ECS конечно тема модная, но пока не изучал.
https://habr.com/ru/post/358108/
|
(Offline)
|
|
26.10.2020, 08:30
|
#7
|
Гигант индустрии
Регистрация: 13.09.2008
Сообщений: 2,893
Написано 1,185 полезных сообщений (для 3,298 пользователей)
|
Ответ: LeoECS
Сообщение от Crystal
Этот проект делается под впечатлением
от покемонов и фоллаутов
|
Я почему и написал про фаллаут. Помню даже какие-то видосы где у избы крыша пропадала при наведении на неё камеры.
Сообщение от Crystal
Щас я откатил интересы на 10 лет назад, и проект успешно
производится потихонечку, не смотря на отсутствие заинтересованности
в нём кем либо кроме меня.
|
Поэтому уже с 2013 года все делают на мобилки, добавляя не много бесполезного визуального доната и редкой рекламы.
На мобилках даже бессмысленное нечто под названием "гиперказуалки" с модельками а-ля 2002 год хавают за обе щеки.
То есть будет и респект от игроков и может даже какой-то бабос.
Сообщение от Crystal
Когда мне надоест программировать фоллауты, я возьму UE с его нодами.
|
Кстати есть тоже самое на Unity и называется оно Bolt. Летом его выкупили и сделали бесплатным.
Но я всё же рекомендую попробовать LeoECS так как с этим очень просто кодить и фактически без ООП. По простоте получается тот же Блитз.
|
(Offline)
|
|
26.10.2020, 11:58
|
#8
|
Терабайт исходников
Регистрация: 05.07.2007
Сообщений: 5,196
Написано 1,721 полезных сообщений (для 5,374 пользователей)
|
Ответ: LeoECS
Сообщение от Nex
Я почему и написал про фаллаут. Помню даже какие-то видосы где у избы крыша пропадала при наведении на неё камеры.
Поэтому уже с 2013 года все делают на мобилки, добавляя не много бесполезного визуального доната и редкой рекламы.
На мобилках даже бессмысленное нечто под названием "гиперказуалки" с модельками а-ля 2002 год хавают за обе щеки.
То есть будет и респект от игроков и может даже какой-то бабос.
Кстати есть тоже самое на Unity и называется оно Bolt. Летом его выкупили и сделали бесплатным.
Но я всё же рекомендую попробовать LeoECS так как с этим очень просто кодить и фактически без ООП. По простоте получается тот же Блитз.
|
По видосу, это 2009 год, там был прототип редактора карты,
а так же сохранение, и загрузка.
Дальше это всё переехало на хорс:
А после я наработки по фоллауту использовал для прототипа RTS на хорсе с физиксом:
Про игры на мобилки. Если мне удастся дотащить игру до релиза,
если появится фанбаза, если я стану отечественным Кодзимой,
то выпущу версию для мобилок. Если первый вариант не сработает,
выпущу ремейк своего Unreal Racinga на мобилки с рекламой:
|
(Offline)
|
|
Эти 2 пользователя(ей) сказали Спасибо Crystal за это полезное сообщение:
|
|
26.10.2020, 16:08
|
#9
|
[object Object]
Регистрация: 01.08.2008
Адрес: В России
Сообщений: 4,361
Написано 2,473 полезных сообщений (для 6,857 пользователей)
|
Ответ: LeoECS
Сообщение от Crystal
Смысла перехода на LeoECS не вижу имхо. Так же можно было предложить
на огре по кодить, по тому что "это другое", а смысл? ХЗ.
|
Ты часто апеллируешь к тому, что у тебя нет тех или иных проблем (отсылка к теме, где тебе советовали как кодить более эффективно).
Но всё вот это "излишнее" и непонятное это профилактика тех проблем, которые возникнут, когда твой проект уже будет содержать огроменное количество связанных сущностей и изменение чего-то одного повлечёт за собой целый каскад правок.
Не сочти за призыв к действию, просто пытаюсь рассказать что, почему и зачем.
__________________
Retry, Abort, Ignore? █
Intel Core i7-9700 4.70 Ghz; 64Gb; Nvidia RTX 4090 3070
AMD Ryzen 7 3800X 4.3Ghz; 64Gb; Nvidia 1070Ti
AMD Ryzen 7 1700X 3.4Ghz; 8Gb; AMD RX 570
AMD Athlon II 2.6Ghz; 8Gb; Nvidia GTX 750 Ti
|
(Offline)
|
|
26.10.2020, 17:53
|
#10
|
Терабайт исходников
Регистрация: 05.07.2007
Сообщений: 5,196
Написано 1,721 полезных сообщений (для 5,374 пользователей)
|
Ответ: LeoECS
Сообщение от Randomize
Ты часто апеллируешь к тому, что у тебя нет тех или иных проблем (отсылка к теме, где тебе советовали как кодить более эффективно).
Но всё вот это "излишнее" и непонятное это профилактика тех проблем, которые возникнут, когда твой проект уже будет содержать огроменное количество связанных сущностей и изменение чего-то одного повлечёт за собой целый каскад правок.
Не сочти за призыв к действию, просто пытаюсь рассказать что, почему и зачем.
|
Процитирую сам себя: Код существует для того, чтобы его переписывать.
Имхо любой программист смотря на свой старый код, видит что можно было по другому, и лучше.
Но а мои спотыкания, это неопытность. Мой первый проект на юнити,
первый на сишарпе, да ещё и чуть ли не триплэй уровня девяностых,
и после 9-10 лет паузы в геймдеве. Сам понимаешь, не так то просто
с разбегу влететь в это дело. В любом случае уже почти 10к строк кода,
переноситься на другую платформу разработки (или недоплатформу) я уже не буду.
Ну и собственно сложности существуют для того, чтобы их преодолевать.
Я не просто разрабатываю, я обучаюсь.
|
(Offline)
|
|
26.10.2020, 23:56
|
#11
|
[object Object]
Регистрация: 01.08.2008
Адрес: В России
Сообщений: 4,361
Написано 2,473 полезных сообщений (для 6,857 пользователей)
|
Ответ: LeoECS
Сообщение от Crystal
Процитирую сам себя: Код существует для того, чтобы его переписывать.
Имхо любой программист смотря на свой старый код, видит что можно было по другому, и лучше.
|
В первых 2х постах описывается вариант на тему того, как избежать подобного.
Методика "Всё круто, но переделывай" хорошо катит для детей и бессмертных.
__________________
Retry, Abort, Ignore? █
Intel Core i7-9700 4.70 Ghz; 64Gb; Nvidia RTX 4090 3070
AMD Ryzen 7 3800X 4.3Ghz; 64Gb; Nvidia 1070Ti
AMD Ryzen 7 1700X 3.4Ghz; 8Gb; AMD RX 570
AMD Athlon II 2.6Ghz; 8Gb; Nvidia GTX 750 Ti
|
(Offline)
|
|
Эти 2 пользователя(ей) сказали Спасибо Randomize за это полезное сообщение:
|
|
28.10.2020, 16:21
|
#12
|
Ференька
Регистрация: 26.01.2007
Адрес: улица Пушкина дом Колотушкина
Сообщений: 10,742
Написано 5,461 полезных сообщений (для 15,675 пользователей)
|
Ответ: LeoECS
Сообщение от Crystal
Процитирую сам себя: Код существует для того, чтобы его переписывать.
Имхо любой программист смотря на свой старый код, видит что можно было по другому, и лучше.
|
Всё так, но бизнес с этим не очень согласен.
Да и программист, который хочет кушац за счёт своих писулек тоже.
__________________
Мои проекты:
Анальное Рабство
Зелёный Слоник
Дмитрий Маслов*
Различие**
Клюква**
* — в стадии разработки
** — в стадии проектирования
Для проектов в стадии проектирования приведены кодовые имена
|
(Offline)
|
|
Сообщение было полезно следующим пользователям:
|
|
Ваши права в разделе
|
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения
HTML код Выкл.
|
|
|
Часовой пояс GMT +4, время: 23:53.
|