Игра на Unity, с открытым кодом

Черная пятница, черная пятница… надоело. Объявляю свой личный Белый понедельник — за пару ночей написал небольшую игру и выкладываю ее код на всеобщее пользование, со скидкой 90%. Зачем мне это надо? Ну я вижу следующие плюсы — тот самый открытый код для поиска работы (да да, сейчас я нахожусь в активном поиске), почитать в комментариях о своих косяках, наконец то сменить статус на Хабре.

Суть игры


Идея пришла внезапно, и пока она не улетела решил записать а потом и воплотить ее. Представьте холодную, темную камеру в некой тюрьме. В ней сидит, неизвестно как и неизвестно кем, прикованный волшебник, которого каждую ночь мучает всякая нечисть. Из последних сил он создает небольшой файрбол и вдыхает в него подобие жизни.

Познакомьтесь, это Кальцифер, ваш аватар в этой игре.


Именно им вы будете уничтожать вся гадость, которая ползет в сторону нашего закованного бедняги.

Ссыль на код

Пояснения насчет кода


Начнем с GameManager. Он всему голова, именно в нем меняется состоянии игры — Initialization->GameLoop->Win или Lose. Одинок и един, ибо синглтон. Так как игра не сетевая, простая, и без сложных переходов, то было принято решение использовать этот паттерн. Здесь же идет обработка попаданий по игроку (см. ниже Известные проблемы), учет хитпоинтов и проверка на выигрыш\проигрыш. Был бы GodObject, да слишком мало у нас классов, поэтому знает не всё обо всех. На этапе инициализации, создается пул объектов отображающих анимацию урона по игроку и смерти врагов. Для отслеживания состояния хитпоинтов, можно подписаться на UpdateHpWizardDelegate или UpdateHpCalciferDelegate. В нашем случае это делает GUIManager для отображения текущего хп на экране.



К этому времени SpawnManager уже составил список точек спавна врагов



a WaveManager загрузил порядок волн создания врагов. Волны можно настроить двумя способами: прописать в коде игры или загрузить с Json файла. Для редактирования этого Json написан кастомный editor:GameDataEditor



Можно прописать точный номер спавна или указать что можно создаться на любом свободном.

Создание волн сделанно с помощью хитрой корутины:

SpawnWaves
  private IEnumerator SpawnWaves()
        {
            yield return new WaitForSeconds(firstWaveDelay);
            while (currentWave < waves.Count)
            {
                if (!CheckFinishedCurrentWave())
                {
                    var step = waves[currentWave].GetCurrentWaveStep();
                    if (step != null)
                    {
                        yield return new WaitForSeconds(step.delay);
                        var spawners = step.spawners;
                        if (spawners != null)
                        {
                            foreach (var spawn in spawners)
                            {
                                SpawnPoint spawnPoint;
                                if (spawn.index == Consts.INDEX_RANDOM_SPAWN_POINT)
                                {
                                    spawnPoint = SpawnManager.S_Instance.GetRandomEmptySpawnPoint();
                                }
                                else
                                {
                                    spawnPoint = SpawnManager.S_Instance.GetSpawnPointByIndex(spawn.index);
                                }
                                if (spawnPoint != null)
                                {
                                    SpawnManager.S_Instance.SpawnEnemy(spawnPoint, spawn.name);
                                }
                                else
                                {
                                    throw new Exception("Not empty spawn point");
                                }
                            }
                        }
                        Debug.Log(step.text);
                    }
                    waves[currentWave].currentStep++;
                }
                else
                {
                    SelectNextWave();
                }
            }
        }


Вначале проходит firstWaveDelay секунд до начала запуска 1 первой волны. После этого в цикле прогоняют все волны по очереди, вставляя нужную задержку step.delay между шагами волны. Почему в корутине а не например в Update? Да собственно можно и так и эдак, просто тут более наглядно, видно где задержка ( yield return new WaitForSeconds) и не надо городить лишние циклы и проверки.

Давай те глянем что же представляют из себя SpawnPoint. Это MonoBehavior c 2 компонентами: SpawnPoint и CircleCollider2D. В первом, с помощью второго, определяется занят ли спавн каким то врагом. OnDrawGizmos отображает в редакторе Unity расположение спавнов.

OnDrawGizmos

    void OnDrawGizmos()
        {
            if (m_IsDirty)
            {
                Gizmos.color = Color.red;
            }
            else
            {
                Gizmos.color = Color.green;
            }
            Gizmos.DrawSphere(transform.position, 0.3f);
        }




Все враги происходят от базового класса BasicEnemy в котором есть несколько виртуальных методов:

  • ContactWizard() , для взаимодействия с волшебником
  • ContactCalcifer(), для взаимодействия с Кальцифером
  • ShowDeathAnimation(), для показа анимации смерти

Их можно переопределить в потомках, для различной реакции на эти события. Например PoisonEnemy ранит при прикосновении Кальцифера, в отличии от остальных врагов.

PoisonEnemy.ContactCalcifer
 
  public override void ContactCalcifer()
        {
            GameManager.S_Instance.DamageCalcifer(damage);
            base.ContactCalcifer();
        }


Кстати, врагов и многие другие объекты (много и часто создаваемых на сцене) мы не удаляем с помощью Destroy(this), а отправляем обратно в пул объектовObjectPool.Recycle(this). Таким макаром мы неплохо экономим на создании объектов, которое как известно достаточно затратное дело.

Так например анимации заканчиваются вызовом SelfDestroy(), который и возвращает объект анимации обратно в пул.



Движутся же враги с помощью силы пафоса компонента BasicEnemyMoving. В нем нас интересуют два метода: OnEnable()и Move(). OnEnable () вызывается после вытаскивания врага с пула и нужен для поворота врага (если необходимо) в сторону цели.

OnEnable
 
public void OnEnable()
        {
            if (obj!=null&&obj.NeedRotateForDirection) 
            {
                Vector3 moveDirection = transform.position - GameManager.S_Instance.wizardTransform.position;
                if (moveDirection != Vector3.zero)
                {
                    float angle =  Mathf.Atan2(moveDirection.y, moveDirection.x) * Mathf.Rad2Deg-90;
                    transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward); 
                }
            }
        }


Move() же является виртуальным методом, который и движет врага к цели. Его можно переопределить в потомках и сделать особенное движение (с рывками, синусоидальное и т.п.)

Move
 
 public virtual void Move()
        {
            transform.position = Vector3.MoveTowards(transform.position, SpawnManager.S_Instance.TEMP_GOAL.position, speed * Time.deltaTime);
        }


На этом как бы всё.


Известные косяки


  • Синглтоны
  • Отсутствие классов Wizard и Calcifer. Вся логика с их взаимодействием лежит в GameManager
  • Магические числа с углами поворота спрайтов. Да понимаю что надо бы все спрайты подвести к одному углу и уже от него плясать. Но времени не хватило.
  • Отсутствует запись прогресса
  • Кривое объявление префабов врагов в SpawnManager

TODO


  • Смена дня\ночи. Днем отдыхаешь, раскидываешь скиллы и т.п. Ночью жгешь вражин.
  • Новые типы врагов: стреляющие, ранящие Кальцифера, оставляющие слизь/паутину, телепортирующиеся, размножающиеся
  • Прокачка Кальцифера: статы, tower defence, скиллы
  • Несколько концовок. Есть пара задумок насчет нескольких вариантов развития событий, в случаи проигрыша на разных этапах/разной прокачки
  • Освещение. Стены камеры освещаются Кальцифером, по мере его роста, все сильнее и сильнее.
  • Кат сцены.

Анимация огонька взята с powstudios.com
За спрайт волшебника отдельное спасибо милой Kori Tyan
Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 55

    +3
    Бедный Хаул, вот это его жизнь потрепала:)
      0
      Это уже в процессе пришло, изначально в голову был образ Корвина с Амбера.
        0

        Уж больно имя узнаваемое.
        Можно сделать так, чтобы камера переезжала иногда на новое место со сменой декораций и противников.

          0
          Пока по предварительным планам все дело будет происходить в этой камере. А врагов к следующему билду добавлю. Так же думаю можно будет кастомизацию приделать — вместо огонька Мортэ например, дракоша с Мулана или Джинни
      +2
      Почему вместо кватернионов и арктангенса не использовать простр transform.up или transform.right как вектор направления?
        0
        Торможу, как это?
          +2
          transform.up = wizardPosition — transform.position;
            +2
            transform forward/up/right не только геттеры, но и сеттеры
              +1
              Спасибо. Вот они минусы самостоятельного и одиночного изучения Unity.
          –14
          Побольше бы таких статей, а то нормальные уже читать надоело, хочется потрещать.
          Да и действительно, почему не превратить хабр в откровенный сайт по поиску работы!
          Сегодня один написал, завтра другой, после завтра десяток таких статей в день.

          С таким кодом я бы взял только забившийся Кальцифер прочищать.
            0
            Главное, ни слова про политику или Маска.
              +9
              Что не так с кодом?
              почитать в комментариях о своих косяках

              Это озвучено аж до ката, так что если не сложно тыкните меня носом в косяки.
              А насчет работы… Ссылки тут нет, спама тоже, продолжение про эту игру уже составляется. Не только же зубрам писать, новички тоже хотят учиться.
                –13
                Предложение работы и бесплатный рефакторинг хотят новички выкладывая свои наработки о кальцефере. Мне просто ипец как интересно лицезреть подобное на хабре.
                  +6
                  Ясно. Ну тогда вопросов не имею к вам больше. Можете не лицезреть мои посты.
                    –11
                    Меня пугает что это увидят такие же как Вы и завтра уже другие посты затеряются среди подобного контента-кальцефера.
                      +2
                      а на какую платформу вы предлагаете выкладывать свой код под предлогом какой-то цели, чтобы его могли увидеть работодатели в таких отраслях? на какие сайты заходят так много представителей IT компаний, чтобы на них такие статьи были настолько же эффективны (а может и больше)?
                      да, соглашусь с вами, Хабр не то место, чтобы делать из него свалку мелких статей «смотрите как умею, возьмите меня куда-нибудь». а есть ли этому альтернатива? притом не просто куда можно закидывать код и пояснениями в виде статьи, но куда бы еще и заходили искать этот самый код люди, нуждающиеся в таких программистах?
                        +4
                        Вы попросите @bumburum создать хаб «Ищу работу» и отпишитесь от него.
                        +2

                        Рискую прослыть слоупоком, что такое "кальцифера"?

                        • UFO just landed and posted this here
                          0
                          Меня пугает что это увидят такие же как Вы и завтра уже другие посты затеряются среди подобного контента-кальцефера.

                          А мне кажется, что «ищу работу статьи» — скорее положительное явление. Автор старается максимально вложится в материал, а не просто перевести на отъебись или скопипастить новость с другого сайта.
                            –2
                            Привет! Написав это сообщение Вам выпал шанс совершенно бесплатно получить от меня ответ! Я решил Вас осчастливить уникальная возможность и Вам даже не придется ждать следующую черную пятницу. Так вот мой дорогой друг! Здесь много Ёби от чувачка-новичка… Буду благодарен за подробное объяснение и наставление! И да, ведется набор в команду для написания продолжения о нападении Сифилёчка в вечернее время на Дрищьград.

                            Вы реально считаете это положительным контентом? Мне Вас жаль если это так.
                              +2
                              Наверное, вы хотели донести какую-то мысль. Возможно, в голове у вас она даже казалась довольно внятной. Но сформировать в речь вы ее не смогли
                        +3
                        Ну а я новичок в разработке на C#, и МНЕ интересно. А вот многочисленные статьи про Docker-контейнеры, которыми хабр-уже-давно-не-торт засран от и до, уже реально заколебали.

                        Уважаемый Arkebuz, мой Вам совет — создайте свой раздел на samlib.ru, и туда выкладывайте статьи. Потом только киньте ссылку на раздел — добавлю в друзья.

                          0
                          А почему на самлиб? Насколько я помню там же самиздат в основном.
                          +2
                          Прошу прощения, а за то, а ваш комментарий тут тоже нужно оплатить?
                          Вы ведь «бесплатно» старались, тратили время на его создание.
                            0
                            Нет, я в него рекламу потом своего портфолио вставлю и ещё пару ссылок на профиля чуваков которые мне его писать помогали. Я вообще хотел написать пост, типа- я крут и вот пару чуваков на которых стоит обратить внимание, но потом подумал что его могут не опубликовать и решил написать бессмысленный коммент от новичка, который никакой смысловой нагрузки не несет.
                            Но если Вы хотите заплатить, то можете это сделать! Я вообще нуждаюсь в деньгах, поэтому воспользуюсь моментом, и скажу, если кто ещё хочет заплатить то можете и заплатить! Ещё есть старая мебель на балконе могу сфоткать и ссылку приложить, может кому-то нужно. Лучше место чтобы это написать я не знаю. Если у меня не получилось Вас убедить, то можете написать в комментах, как это нужно сделать! Синглтон!
                        +2
                        То есть, я так понимаю, если бы человек убрал упоминание о поиске работы, то вы бы весь этот «хай» не подняли?
                        Ну в любом случае, Ваша логика чем-то напоминает логику «лубочных» старушек у подъезда, вот «проститутка» прошла, вот «наркоман».
                        Я так погляжу у Вас в принципе нет публикаций, Вы напишите свой шедевр, и посмотрите, как люди его оценят, таким образом Вы создадите качественный контент и героически разбавите так Вами называемый «контент-кальцифер».
                        Я думаю если человек делает хоть что-то созидательное, что вроде бы как не разрушает особо работу существующей системы, то не надо сразу пытаться поднять его за это «на вилы».
                        В конце концов объективно, автор сам по тренировался, кто-то дал дельный совет и порадовался тому что помог человеку, кто-то из любопытна прочитает, кто-то найдет эту статью через поисковую систему и заинтересуется разработкой игр на Unity, да мало ли что еще хорошего принесет эта статья.
                        Я вот лично благодаря ей и комментариям к ней узнал, что такое кальцифер, не думаю, что это спасет однажды мне жизнь, но и хуже явно не сделает :)
                        А Вам и вашим единомышленникам я думаю не так сложно, пролистать неинтересную статью, это вопрос долей секунд, учитывая, что превью текста статьи в ленте уложилось в один абзац.
                        +1
                        > Синглтоны

                        Есть мнение что с текущей архитектурой Unity это неизбежное зло. Потому что нет никакого контекста с временем жизни «сцена» и простым доступом за O(1). В результате либо вы на каждый чих будете искать нужный объект через FindObjectOfType (что медленно) либо заводите статическую переменную и запоминаете там на него ссылку.

                        А чтобы добавить жести, у вас нет никаких механизмов задания порядка инициализации этих «важных» объектов. Поэтому либо их приходится создавать программно on-demand (как делаете вы в Singleton.cs) и в результате терять возможность их настроить на сцене (поэтому вам пришлось городить огород вокруг редактирования WaveManager), либо они ставятся на сцену но тогда в процессе инициализации есть риск что с ними начнут пытаться работать до вызова Start/Awake (что у вас потенциально возможно в ObjectPool.cs). И так и так плохо, а сделать хорошо нечем.
                          0
                          Script Execution Order?
                            0
                            В рамках одного геймобжекта — да. Но разве он вам что-то обещает про порядок обработки нескольких геймобжектов?
                              0
                              А иерархия? Сначала инициируются родительские элементы, потом дочерные. Порядок инициализации дочерных между собой не важен. У меня, вроде, все сперва инициируется, а потом запускается игра, потому в порядке выполнения евейков я не уверен, но вроде так они и работают?
                                +1
                                Инициализация происходит не по иерархии, а по порядку создания инстансов в сцене. Т.е. или надо обладать идеальной памятью или использовать что-то иное для инициализации.
                              +1
                              Недавно годный доклад на эту тему опубликовали на youtube. Я сам практикую немного другие вещи (ECS / Entitas), но то что сделали они мне очень понравилось www.youtube.com/watch?v=raQ3iHhE_Kk
                              Очень советую к просмотру, примеры кода так же можно найти по коментам и в блоге (https://blogs.unity3d.com/2017/11/20/making-cool-stuff-with-scriptableobjects/)
                              +1

                              Кстати есть что годное по тебе mvc в unity?

                                +2
                                Используйте Zenject или любой другой контейнер.
                                  +1
                                  Он не инджектит, если используешь GameObject.Instantiate, правильно?
                                  А следовательно — он предлагает послать всю компонентную систему Юнити. Или помимо компонента, который мы вешаем на объект — он еще предлагает создать компонент-контейнер? Я не совсем понимаю, как с этим работать, можете, пожалуйста, подробнее?

                                  Ну вот допустим есть гипотетическая логика стратегии, там содержатся ресурсы, исследования.

                                  У меня есть кнопка «Построить супер-здание»:
                                  — Она должна быть серой, если здание не исследовано или уже построено в любом месте карты
                                  — Она должна быть синей, если здание исследовано, но не хватает ресурсов
                                  — Она должны быть зеленой, если можно построить здание
                                  — Она должна вспыхнуть (создаться эффект с частичками), если закончилось строительство здания
                                  — На ней должен быть написан текст, согласно локализации.

                                  И таких зданий у нас пять.

                                  Сейчас иерархия компонентов выглядит так:

                                  BuildingButtonPrefab
                                  |--- BuildingButtonImage
                                  |    \--- BuildingTitleText
                                  \--- ConstructedParticles


                                  Потом пишем такой моно-бехавиор, присваиваем кнопке и вручную в редакторе перетягиваем все компоненты в соответствующие поля, внутри пишем всякую логику через жопу синглтон. Обратите внимание, что во вьюшке необходимо данные из четырех моделей — локализация, кошелек, список строений и дерево исследований.

                                  class BuildingButtonsList : MonoBehaviour {
                                      public BuildingButtonPrefab BuildingButtonPrefab;
                                      
                                      public void Start () {
                                          foreach (var bType in Game.instance.buildings.list) {
                                              Instantiate(BuildingButtonPrefab, transform);
                                          }
                                      }
                                  }
                                  
                                  class BuildingButtonPrefab : MonoBehaviour {
                                      public Button    BuildingButtonImage;
                                      public Text      BuildingTitleText;
                                      public Particles ConstructedParticles;
                                  
                                      private BuildingTypeEnum type;
                                  
                                      public void SetBuildingType (BuildingTypeEnum type) {
                                          this.type = type;
                                          text.text = Game.instance.localization.get("Buildings." + type);
                                      }
                                  
                                      public void Start () {
                                          Game.instance.onChange += OnSomethingChanged;
                                          Game.instance.onConstructed += OnConstructed;
                                      }
                                  
                                      public void OnSomethingChanged() {
                                          var g = Game.instance;
                                  
                                          if (!g.tree.IsResearched(type)) {
                                              Button.image = GetGreyImage();
                                          }
                                          else if (g.buildings.IsConstructed(type)) {
                                              Button.image = GetGreyImage();
                                          }
                                          else if (!g.wallet.HasResourcesFor(type)) {
                                              Button.image = GetBlueImage();
                                          }
                                          else {
                                              Button.image = GetGreenImage();
                                          }
                                      }
                                  
                                      public void OnConstructed(BuildingTypeEnum type) {
                                          if (type == this.type) {
                                              ConstructedParticles.Launch();
                                          } 
                                      }
                                  }


                                  Я вижу тьму проблем в таком подходе, но Юнити прям толкает на то, чтобы писать именно так. Как это правильно сделать при использовании контейнера?
                                    +3
                                    Стараться не использовать MonoBehaviour :).

                                    Вместо Object.Instantiate — нужно использовать фабрику, и тогда-то зависимости будут прокинуты.

                                    По примеру, больно много уже классов, чтобы быстро пояснить… я только один изменю — BuildingButtonPrefab.
                                    public class BuildingButton : MonoBehaviour {
                                    	[SerializeField] private Button _button;
                                    	[SerializeField] private Text _title;
                                    	[SerializeField] private Particles _particles;
                                    
                                    	public void Listen(Action onClick) { _button.onClick.AddListener(onClick.Invoke); }
                                    
                                    	public void Unlisten() { _button.onClick.RemoveAllListeners(); }
                                    
                                    	public void Initialize(string title) { _title.text = title; }
                                    
                                    	public void Burst() { _particles.Launch(); }
                                    
                                    	public void SetState(ButtonState state) {
                                    		switch (state) {
                                    			case ButtonState.Disabled:
                                    				_button.interactable = false;
                                    				// _button.image = _gray;
                                    				break;
                                    			case ButtonState.Inactive:
                                    				_button.interactable = false;
                                    				// _button.image = _blue;
                                    				break;
                                    			case ButtonState.Active:
                                    				_button.interactable = true;
                                    				// _button.image = _green;
                                    				break;
                                    			default:
                                    				throw new ArgumentOutOfRangeException("state", state, null);
                                    		}
                                    	}
                                    }

                                    Как видно, тут нет синглтонов, как и нет никакой логики изменения состояния, нет самих состояний. Вообще, я руководствуюсь правилом — если это MonoBehaviour то это Вид и он максимально тупой. Все данные которые нужны, кнопка получает через методы (или поля), сама кнопка так же ничего и не спрашивает ни у кого, у ней есть все нужные ей данные для работы. Ее внутренние состояние можно поменять, в теории, только через публичные методы. Можно выделить интерфейс — и таким образом заблокировать доступ к gameObject
                                    Логика изменения поведения кнопки находится в другом классе, он не-MonoBehaviour и контролирует ее состояние обращаясь к моделям или другим контроллерам.
                                      0

                                      Стараться не использовать MonoBehaviour :).
                                      Как бы это не было смешно, но насколько я знаю, это единственный верный путь, если нужно создать проект с более менее сложной архитектурой в Unity.

                                        0
                                        «Не использовать» или «использовать»??
                                          0
                                          WeslomPo
                                          Вообще, я руководствуюсь правилом — если это MonoBehaviour то это Вид и он максимально тупой.

                                          Т.е. не использовать их для чего-то сложного. А выносить всю логику и т.д. в свои классы не наследуемые от MonoBehaviour и связывать с компонентами порожденным от MonoBehaviour через интерфейсы.
                                          Но это все актуально только для более менее сложных проектов. Совсем простые игры проще, быстрее и в целом выгоднее делать не задумываясь обо всем этом, т.к. может получиться проектирование ради проектирования.
                                            +1
                                            Не использовать.

                                            Когда изучаешь Unity, автомагически приобретаешь болезнь MonoBehaviour головного мозга xD. Вот ты захотел решить задачу со списком кнопок — и сразу создал «class BuildingButtonsList: MonoBehaviour» — а зачем? Можно же простой класс использовать, а в нужный момент вызвать метод Initialize либо другой аналог Start, из класса контроллера (вопрос целесообразности существования самого такого класса оставим за скобками).

                                            Когда перестаешь при решении задач думать о том как бы это все в недокомпонентную модель вписать — сразу же приобретаешь свободу в решении задач, становится проще применять разные паттерны проектирования на практике.

                                            Может возникнуть опасение, что этот подход не вяжется с Unity — однако это не так, движок достаточно гибкий. Попробуй Zenject изучить, довольно неплохо помогает прокачаться.
                                          0
                                          По примеру, больно много уже классов, чтобы быстро пояснить… я только один изменю — BuildingButtonPrefab.

                                          Так, ну со вьюшкой понятно. _button, _title, _particles — это все ясно. Но как там иерархией выше?

                                          0. У нас есть что-то вроде BuildingButtonControllerFactory, которая создает BuildingButtonController, которая передает уже все параметры этой нашей BuildingButtonPrefab?
                                          1. Или ее правильно назвать BuildingButtonView?

                                          2. А как BuildingButtonController получает ссылку на BuildingButtonPrefab? И вот смотрите, мне надо создать список кнопок. Ну допустим, это выглядит в новом подходе так:

                                          foreach (var bType in game.buildings.list) {
                                              buttons.Add( buildingButtonControllerFactory.Produce(bType) );
                                          }


                                          3. Мы делаем GameObject.Instantiate(BuildingButtonPrefab) в контроллере BuildingButtonController? (простите за каламбур)
                                          4. А как BuildingButtonController знает в какой трансформ необходимо добавить свой ГеймОбджект?
                                          5. Как вы решаете проблему, что некоторые возможности доступны только наследнику MonoBehaviour? Я сходу не вспомню, что именно, но допустим, если бы Instantiate можно было бы сделать только внутри наследника — тогда как?

                                          6. Какая у вас иерархия файлов? Вы отдельные компоненты храните в одной папке (их фабрику, контроллер, вьюшку и всякие сервисы). Или у вас есть директория со всеми фабриками, директория со всеми контроллерами, директория со всеми вьюшками?

                                          7. Вы создаете три класса вместо одного на каждую сущность — не усложняется подход на больших проектах?
                                            +1
                                            Отвечу только по пункту 7. Остальное пусть лучше WeslomPo разжует ))
                                            Как раз таки именно в больших проектах все это и имеет смысл, т.к. относительный объем этого инфраструктурного когда будет очень малым. А преимущества от вынесения бизнес логики в отдельные классы согласно паттерна MVC будут очень значительны.
                                              +1
                                              0. Этому классу совсем неважно как его создадут и куда поместят, это не его проблемы.
                                              1. Да более подходящее имя.
                                              2. Не важно как :) как удобно. Например из пула объектов.
                                              3. Обычно контроллеры не занимаются такой ерундой (просто получают список всех нужных объектов, либо пул), но можно и так.
                                              4. Это одна из причин почему он этим не занимается.
                                              5. Все что нужно MonoBehaviour — это вид и его проблемы, там же и решаем.
                                              6. Под каждый кусок проекта завожу папку, внутри иерархию из папок Controllers, Views, Enums, Interfaces и т.д., папки исключаю из генерации namespace. Пока так. Но не уверен что это лучшее решение.
                                              7. Распространенное заблуждение :). В крупных проектах долгожителях на первый план выходит поддержка, а не написание. Создавать модули — чуточку сложнее становится, за то поддерживать в десяток раз проще. Плюс разделение логики сильно сокращает время написания. Да я, лично, не люблю прокидывать классы через контейнер (так много всего нужно учесть), но это делается один раз, а потом готовые классы просто дополняются нужным функционалом. Кстати, больше всего ошибок допускается и времени уходит на виды, а контроллеры и модели писать — одно удовольствие, чистая логика.
                                              В целом LeonThundeR прав, если проект мелкий и жить долго не собирается — лучше не морочить голову архитектурой особо — весь запал выжрет — это применимо только если нужно долго поддерживать проект.
                                      +1
                                      мне кажется или я попал на форум геймдев.ру
                                        +4

                                        Вы так говорите, будто это что-то плохое. Почему бы и не разбавить немного потоки статей про нейросети, блокчейны и вебдев?

                                        0
                                        Помогла ли эта статья с поиском работы?
                                          +1
                                          Еще не знаю, выходные же:)
                                          +1

                                          Мне вот тут подумалось — а что если запилить клон этой игрушки на Godot 3.0 и сравнить с оригиналом на Unity по простоте реализации?

                                            0
                                            Попробуй, сравним:)
                                              +1
                                              OK, вот на днях текущий проект до релиза дотащу и попробую пожалуй.
                                                +1
                                                +, мне тоже интересно
                                            +1
                                            Я не пропал, нет:) Продолжение уже составляется, вначале на ГТ (думаю статье без единой строчки кода, только про дизайн, самое там место), потом на хабр. Мне вот интересно, кому то интересно прочитать про Gamelift Amazon-ский(облачная платформа для выделенных игровых серверов) и рабочий пример на юнити с ним?

                                            Only users with full accounts can post comments. Log in, please.