Мобильная 3D игра на Unity3D менее чем за 90 часов

image Приветствую! Сегодня я расскажу вам о своем опыте разработки игры на Unity для платформы Android, менее чем за 90 часов, на примере создания простенького «раннера». В процессе повествования я затрону некоторые детали и ключевые этапы, с описанием всех возможных подводных камней и методов их решения. Данная история описывает процесс создания игры для мобильных платформ, начиная от концепции и заканчивая готовым продуктом. Надеюсь, она вдохновит вас на создание собственного проекта, либо поможет пролить свет на некоторые особенности движка Unity. Без лишних слов, приступим к делу!

Этап-1: концепция


Как правило, начинающие разработчики, наступают на свои первые и самые значимые грабли уже на данном этапе, потому что перед тем, как приступить к созданию чего-либо, неплохо было бы оценить собственные возможности. Просто задайте себе вопрос: хватит ли у вас сил, времени и умений на создание проекта ААА класса? Ответ – нет! Отбросьте эту идею в долгий ящик, и не возвращайтесь к ней до тех пор, пока не реализуете чертову дюжину удачных проектов. К слову, под удачей мы подразумеваем количество установок от 500 тысяч, рейтинг свыше 3,5 по 5-ти бальной шкале и коммерческий успех. Для начала, займитесь более простыми, я бы даже сказал приземленными проектами, вроде аркад в стиле addictive games, сочетающих в себе все необходимые нами критерии «удачного» проекта.

Преимущества стиля addictive games:

  1. Затягивающий, «залипающий» геймплей;
  2. Отсутствие сюжета;
  3. Простое и интуитивно понятное управление, требующее от игрока минимум действий;
  4. Минимальные требования к графике.

Последний пункт стоит особняком, так как львиная доля времени уходит не на написание кода, а на полировку визуальной составляющей игры, которая в нашем случае особой роли не играет. Учитывая вышесказанное, я решил остановиться на 3D-раннере, а именно на поделке в роде знаменитой игры ZIGZAG, только лучше.

Этап-2: создание наброска


Вот мы и подошли к самому главному этапу разработки, и пока с вашего лица еще не сошла разочаровывающая улыбка, позвольте вам напомнить, что набросок – это видение продукта. Создав его, вы фактический утверждаете техническое задание будущей игры, благодаря которому все дальнейшие шаманства и танцы будут исходить именно из этого задания. Степень проработки эскиза определять вам и только вам. В конце концов, этот эскиз вы создаете для себя, а не для галереи искусств. На данном этапе, я просто беру ручку и блокнот, после чего начинаю рисовать, изредка оставляя краткие комментарии и пояснения:

image

Из наброска видно, что игра предназначена для мобильных платформ, и запускается она будет в портретном режиме. Геймплей также бесхитростен: задача игрока заключается в преодолении опасного машрута на предоставленном игрой автомобиле, попутно собирая кристаллы. За каждый собранный кристалл и удачно пройденный поворот, игрок получает вознаграждение в виде очков бонуса. Касание по экрану заставляет изменять направление движения автомобиля по осям X и Z.

Этап-3: создание прототипа


Имея под рукой подробный план действий, можно смело приступать к созданию «мокапа» или прототипа будущей игры. По сути, данный этап – начало работы с Unity, и начинать его следует с настройки окружения. Вот, как это настроено у меня:

image

В левой части экрана расположились редактор Scene и Game. Последний отображает то, как именно игра выглядит на устройствах. В правой части: панели Hierarchy и Inspector, а чуть ниже расположены панели Project и Console.

Этап-3.1: под капотом


Внимание! Ниже будет описан простейший код реализации игры, рассчитанный на новичков, и демонстрирующий то, насколько быстро и просто можно добиться результата в Unity. Финальный код игры реализован на более глубоких познаниях языка, включающий проблемы хранения данных, оптимизации и монетизации проекта, однако, по понятным причинам, в данной статье о них говориться не будет. Все скрипты мы будем писать на C#, а тем, кому это не интересно, предлагаю смело переходить к Этапу-4: визуальный дизайн

Мое прототипирование всегда начинается с болванок, то есть в качестве актеров я всегда использую примитивные элементы, вроде кубов и сфер. Такой подход заметно упрощает процесс разработки, позволяя абстрагироваться от всего, что не связано с механикой игры. На первом шаге мы формируем базовое понимание облика будущей игры, а так как по задумке наша игра создается в изометрическом стиле, первое что нам необходимо проделать, это настроить камеру. Тут мы подходим к одной из ключевой особенности Unity. Дело в том, что можно долго экспериментировать с параметрами настройки камеры, подбирая нужные значения… Но проще просто выставить понравившийся ракурс с помощью панели View, а затем активировать GameObject -> Align With View, после чего ваша камера тотчас же примет необходимые значения. Вот такой вот shortcut от создателей Unity.

image

Итак, сцена готова, но как придать персонажу движение? Для начала, произведем некоторые манипуляции с объектом Sphere, добавив в него такие компоненты как Rigidbody и только что созданный скрипт sphereBehavior. Не забудьте отключить галочку Use Gravity, так как на данном этапе он нам не понадобится.

image

Если вкратце, то компонент Rigidbody позволяет объекту ощутить на себе все прелести физического мира, таких как масса, гравитация, сила тяжести, ускорение и.т.д. Вот почему для нас он так важен! А теперь, чтобы заставить тело двигаться в нужном нам направлении, нам всего лишь нужно слегка изменить параметр velocity, но делать это мы будем при помощи кода. Давайте заставим сферу двигаться по оси Х, для этого внесём изменения в скрипт sphereBehavior:

using UnityEngine;
using System.Collections;

public class sphereBehavior : MonoBehaviour {

   private Rigidbody rb; // Объявление новой переменной Rigidbody
   private float speed = 5f; // Скорость движения объекта
   
   void Start() {
      rb = GetComponent<Rigidbody> (); // Получение доступа к Rigidbody
   }
   void Update() {
      rb.velocity = new Vector3 (speed, 0f,0f);
   }

}

В Unity, тела описывают своё положение и направление, посредством специальных векторов, хранящих значения по осям x, y и z. Изменяя эти значения, мы добиваемся необходимого нам направления или положения конкретного тела. Строка rb.velocity = new Vector3(speed, 0f,0f) задает новое направление телу по оси X, тем самым придавая нашей сфере нужное нам направление.

Если вы сделали всё в точности, как и я, то ваша сфера отправится в бесконечное путешествие по оси X, со скоростью speed.

Теперь давайте заставим нашу сферу изменять свое направление, при каждом клике левой клавиши мыши так, как это реализовано в игре ZIGZAG. Для этого мы вновь вернемся к коду sphereBehavior и изменим его следующим образом:

using UnityEngine;
using System.Collections;

public class sphereBehavior : MonoBehaviour {

   private Rigidbody rb; // Объявление новой переменной Rigidbody
   private bool isMovingRight = true; // переменная, отражающая условное направление объекта
   private float speed = 5f; // Скорость движения объекта

   void Start() {
      rb = GetComponent<Rigidbody> (); // Получение доступа к Rigidbody
   }

   void changeDirection() {
      if (isMovingRight) {
         isMovingRight = false;
      } else {
         isMovingRight = true;
      }
   }

   void Update() {

      if(Input.GetMouseButtonDown(0)) {
         changeDirection();
      }

      if (isMovingRight) {
         rb.velocity = new Vector3 (speed, 0f, 0f);
      } else {
         rb.velocity = new Vector3 (0f, 0f, speed);
      }

   }

}

Условимся, что когда сфера движется по оси X, то это движение называется движением «вправо», а по оси Z – «влево». Таким образом мы легко можем описать направление нашего тела специальной булевой переменной isMovingRight.

if(Input.GetMouseButtonDown(0)) {
   changeDirection();
}

Этот кусочек кода отслеживает нажатие левой клавиши мыши, и если данная клавиша все же была нажата, запускает функцию changeDirection(), с простой логикой: если на момент нажатия левой клавиши мыши, переменная isMovingRight имела значение true, то теперь она стала false и наоборот. Напомню, что булевая переменная позволяет нам ответить на один простой вопрос: истинно ли утверждение о том, что тело движется по оси X, или нет? Иными словами, нажатие на левую клавишу мыши постоянно изменяет значение isMovingRight, то на true(тело движется вправо), то на false(тело движется влево).

Альтернативно, функцию changeDirection() можно записать в одну строку:

void changeDirection() {
      isMovingRight = !isMovingRight;
}


И последнее, что необходимо сделать, это переписать метод направления движения с учетом переменной isMovingRight:

if (isMovingRight) {
   rb.velocity = new Vector3 (speed, 0f, 0f);
} else {
   rb.velocity = new Vector3 (0f, 0f, speed);
}


Если isMovingRight имеет значение true (если сфера действительно движется вправо), тогда значение velocity принимает новый вектор направления rb.velocity = new Vector3 (speed, 0f, 0f); Если isMovingRight имеет значение false, значит тело более не движется вправо, а значит пришло время изменить вектор направления на rb.velocity = new Vector3 (0f, 0f, speed);

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

Круто? Конечно нет! Ведь сфера движется, а мы стоим на месте. Давайте доработаем игру так, чтобы мы могли двигаться вместе со сферой и не упускали её из виду. Для этого нам нужно создать скрипт cameraFollow и прикрепить его к объекту Main Camera:

image

А вот код скрипта cameraFollow:
using UnityEngine;
using System.Collections;

public class cameraFollow : MonoBehaviour {

public GameObject player;
public Vector3 offset;

   void Start () {
      offset = transform.position - player.transform.position;
   }
	
   void Update () {
      transform.position = player.transform.position + offset;
   }

}

Как же осуществить слежение за объектом? Для начала нам нужно рассчитать разницу смещения между объектами Camera и Sphere. Для этого достаточно вычесть от позиции камеры, координаты сферы, а полученную разницу сохранить в переменной offset. Но прежде, необходимо получить доступ к координатам сферы. Для этого нам необходима переменная player, представляющая собой простой GameObject. Так как наша сфера находится в постоянном движении, мы должны синхронизировать координаты камеры с координатами сферы, приплюсовав полученное ранее смещение. Осталось только указать в поле player наш объект слежения, и можно смело любоваться результатом. Просто перетащите объект Sphere в поле Player, скрипта cameraFollow, как это показано на картинке (Main Camera при этом должна оставаться выделенной):

image

Теперь же, давайте подумаем над генерацией дороги, по которой могла бы двигаться наша сфера, ведь сейчас она в буквальном смысле парит в воздухе. Начнем с настройки объекта Cube, представляющий, по нашему мнению, участок пути.

image

Если в вашем списке нет тэга Ground, то его необходимо создать во вкладке Add Tag...

Следующее, что нам предстоит совершить, это создать в корне проекта специальную папку с названием Prefabs, и перетащить в нее наш Cube, прямо из инспектора. Если после этого, имя объекта Cube стало синего цвета, значит вы все сделали правильно.

image

Префабы – это особый тип объектов, позволяющий хранить GameObject, а также все его значения и свойства в одном месте. Префабы позволяют создавать бесконечное множество объекта, а любое его изменение немедленно отражаются на всех его копиях. Иными словами, теперь мы можем вызывать участок пути Cube, прямо из папки Prefabs, столько раз, сколько необходимо.

Теперь давайте создадим пустой GameObject, (щелчок правой кнопки мыши по Hierarchy) переименуем его в RoadContainer и прикрепим к нему только что созданный скрипт roadBehavior:

image

image

А вот и сам код roadBehavior:

using UnityEngine;
using System.Collections;

public class roadBehavior : MonoBehaviour {

   public GameObject road; // Префаб участка пути
   private Vector3 lastpos = new Vector3 (0f,0f,0f); // Координаты установленного префаба

   void Start() {

      for(int i=0; i<10; i++) {
         GameObject _platform = Instantiate (road) as GameObject;
         _platform.transform.position = lastpos + new Vector3 (1f,0f,0f);
         lastpos = _platform.transform.position;
      }

   }

}

Что же тут на самом деле происходит? Как видите, у нас есть переменная, которая позже, вручную будет привязана к нашему префабу Cube, и есть объект Vector3, хранящий координаты последнего установленного префаба (сейчас значения равны нулю).

for(int i=0; i<10; i++) {
   GameObject _platform = Instantiate (road) as GameObject;
   _platform.transform.position = lastpos + new Vector3 (1f,0f,0f);
   lastpos = _platform.transform.position;
}

Этот участок кода выполняет следующее: до тех пор, пока i < 10, мы будем брать префаб, устанавливать его позицию с учетом последней позиции lastpos + позиция с учетом смещения по X, сохранять последнюю позицию. То есть в результате, мы получим 10 префабов Cube установленных в точности друг за другом. Перед проверкой, не забываем назначить переменной road наш объект Cube из папки Prefabs:

image

image

Ок, но что делать дальше? А дальше нам нужно продолжить установку блоков в произвольном порядке. Для этого нам понадобится генератор псевдослучайных чисел random. Подправим скрипт roadBehavior с учетом нововведений:

using UnityEngine;
using System.Collections;

public class roadBehavior : MonoBehaviour {

   public GameObject road; // Префаб участка пути
   private Vector3 lastpos = new Vector3 (0f,0f,0f); // Координаты установленного префаба

   void Start() {

      for(int i=0; i<10; i++) {
         GameObject _platform = Instantiate (road) as GameObject;
         _platform.transform.position = lastpos + new Vector3 (1f,0f,0f);
         lastpos = _platform.transform.position;
      }

      InvokeRepeating ("SpawnPlatform", 1f, 0.2f);

   }

   void SpawnPlatform() {

      int random = Random.Range (0, 2);
      if (random == 0) { // Установить префаб по оси X
         GameObject _platform = Instantiate (road) as GameObject;
         _platform.transform.position = lastpos + new Vector3 (1f,0f,0f);
         lastpos = _platform.transform.position;
      } else { // Установить префаб по оси Z
         GameObject _platform = Instantiate (road) as GameObject;
         _platform.transform.position = lastpos + new Vector3 (0f,0f,1f);
         lastpos = _platform.transform.position;
      }

   }

}

Строчка InvokeRepeating («SpawnPlatform», 1f, 0.2f) предназначена для активации функции SpawnPlatform() спустя 1 секунду после начала игры, и повторного её вызова каждые 0.2 секунды. Что касается самой функции, то тут, как говорится все проще пареной репы! Каждые 0.2 секунды, система загадывает случайное число между цифрами от 0 до 1. Если система загадала 0 – мы устанавливаем новый префаб по оси X, а если 1 – то по оси Z. Вот и вся магия!

image

И наконец, давайте заставим сферу падать каждый раз, когда она сходит с дистанции. Для этого создадим новый скрипт playerFalls и прикрепим его к нашему объекту Sphere:

image

А вот и сам код скрипта playerFalls:

using UnityEngine;
using System.Collections;

public class playerFalls : MonoBehaviour {

   private Rigidbody rb;

   void Start() {

      rb = GetComponent<Rigidbody> ();

   }

   void Update() {

      RaycastHit hit;
      if(Physics.Raycast (transform.position, Vector3.down, out hit, 5f) && hit.transform.gameObject.tag == "Ground") {
         rb.useGravity = false;
      } else {
         rb.useGravity = true;
      }

   }

}

Raycast – специальный луч на подобии лазера, который излучается по направлению к сцене. В случае, если луч отражается от объекта, он возвращает информацию об объекте, с которым столкнулся. И это очень круто, потому что именно так, посредством такого луча, направленного из центра сферы вниз, мы будем проверять, находимся ли мы на платформе Cube или нет (проверяем, имеет ли объект тэг «Ground»). И как только мы покинем регионы дорожного полотна, мы автоматом активируем параметр Gravity нашей сферы (помните, как мы заведомо отключили его в самом начале?), после чего сфера, под воздействием гравитации, рухнет вниз, ха-ха!

Этап-4: визуальный дизайн


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

image

По замыслу, вы управляете автомобилем, несущимся по бескрайним морским просторам, спасаясь от надвигающего катаклизма. Промедление сродни смерти, так как платформы то и дело норовят опрокинуться в морскую пучину, увлекая игрока в бездну позора и разочарования. Плюс ко всему, время от времени, платформы начинают менять цвет, а автомобиль, самопроизвольно увеличивать скорость. Всё это призвано привнести в игру некое подобие «челленджа». Как и было сказано, за каждый удачно пройденный поворот или собранный кристалл, игрок вознаграждается «инкамом» — местным подобием зарплаты. Зарплату в последствии можно обменять в лавке на авто с более высоким «инкамом». Концепция подарила звучное название «Income Racer».

Все ассеты были смоделированы в Blender’е – бесплатном 3D редакторе. В нём же были созданы необходимые текстуры, впоследствии доведенные до приемлемого вида в Photoshop'е. Приятным моментом оказалось то, что Unity легко импортирует 3D модели из Blender’а, без лишней головной боли, делая процесс создания приятным и безболезненным.

Этап-5: полировка


Доводка проекта – те еще грабли, ведь всегда найдется место тому, что можно улучшить, или переделать. Зачастую, случается так, что именно на этапе полировки и доводки, процесс разработки значительно теряет во времени, а то и вовсе заходит в тупик. Причина заключается в том, что вы уже заметно подустали: игра вам кажется однообразной и недостаточно интересной. Иногда, под конец разработки, приходит откровение того, что вы способны на переделку игры с нуля, улучшив её как минимум в два раза! Отбросьте эти мысли и вспомните о плане, о том, с чего все начиналось. Лучше дополнять игру уже после релиза, путем выкатки обновлений, чем затягивать разработку на неопределенный срок. В противном случае, вы рискуете погубить проект, поставив на нём жирный крест. К примеру, на момент написания этих строк, игра даже не имела вступительного экрана. Причиной тому тот факт, что по плану я не мог выйти за рамки в 90 часов, отведенные на процесс разработки. Конечно, можно было бы потратить еще несколько часов на создание вступительного экрана, однако на то он и план, чтобы ему следовать. И это нормально, что некоторые моменты добавляются в игру уже после её релиза.

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

В итоге получилось то, что получилось. На всё про всё было затрачено чуть более 90 часов, что по меркам современного геймдева не так уж и много. По прошествии этого времени, игра была загружена в Play Market и выставлена на всеобщий суд, вот такая история! Если вам понравилась статья, или просто есть о чем поговорить, то добро пожаловать в комментарии. Буду рад ответить на ваши вопросы.

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 22

    +1
    Вот, кстати, 5-й этап самый злой. Знаю по себе. Стоит только начать оптимизировать и добрасывать функционал, который рождается в процессе работы над проектом и все — приехали. Релиза нет, но есть куча реализованных плюшек, а в голове уже готова прорва новых идей. И отказаться от этого соблазна — очень тяжело. Я не про гейм.дев, а в общем. Поэтому ТЗ на проект должно быть строгим и с конкретными целями, без размытых описаний тех или иных составляющих будущего продукта.

    Ну а в целом, спасибо за пост. Продолжайте в том же духе и удаче в игрострое =)
      +1
      Спасибо. Действительно, 5-й этап актуален не только для геймдева. Как вы подметили, важно всегда четко следовать ТЗ, а также уметь вовремя останавливаться.
      +1
      Почему внутри метода
      void changeDirection() {
            if (isMovingRight) {
               isMovingRight = false;
            } else {
               isMovingRight = true;
            }
      }

      пять строчек вместо одной:
      void changeDirection() {
            isMovingRight = !isMovingRight;
      }


      InvokeRepeating ("SpawnPlatform", 1f, 0.2f);

      Странное решение. Разве Вас не смущает то, что платформы будут генериться вне зависимости от положения игрока? Много опасных «а вдруг» при дальнейшей разработке проекта:
      • а вдруг игрок пройдёт первые десять платформ быстрее, чем за секунду
      • а вдруг игрок будет стоять на месте, а платформы всё генерятся и генерятся
      • а вдруг скорость игрока будет больше одной платформы за 0.2 секунды


      Я понимаю, что целью статьи было немного другое, но с такими вещами аккуратнее надо быть в статьях, которые мотивируют/обучают начинающих разработчиков :)
        +1
        Ваши утверждения логичны и понятны, но только не для начинающих разработчиков, которые только знакомятся с C# и геймдевом в целом. Как и было сказано, финальный код игры реализован на более глубоких познаниях языка, в нём предусмотрен и Object Pool, и прекращение генерации платформ в случае схода игрока с намеченного маршрута. В рамках данной статьи я попытался рассказать, как простыми методами и в короткий срок, можно добиться результата в Unity. Отсюда и разъяснение, что называется «на пальцах». Вам большая благодарность за уделенное внимание.
          0
          Думаю, Ваша статья совместно с нашими комментариями, дадут начинающему разработчику понятие того, что нужно учиться делать правильно. И при этом не отпугнут его от увлекательной стези геймдева, а это главное.
          0
          а вдруг игрок пройдёт первые десять платформ быстрее, чем за секунду

          а с какой стати это так может случиться?

          а вдруг игрок будет стоять на месте, а платформы всё генерятся и генерятся

          также как и в первом вопросе. в скрипте написан явный код движения объекта. с чего это вдруг он не будет двигаться?

          а вдруг скорость игрока будет больше одной платформы за 0.2 секунды

          с какой стати скорость игрока будет больще 0.2 если явно указана эта скорость?
            0
            Не нужно быть прямолинейным, думаю Kos-Boss имел в виду тот случай, если разработчик надумает развивать игру, добавить возможность замедления времени, сделать паузу, сделать разные типы шариков с разной скоростью.

            При таких манипуляциях придется полностью перелопачивать весь код. Вспоминать, где ты там скорость захардкодил и другие неприятные вещи.

            Как сказал автор статьи, да, не стоит нагружать начинающих сразу же огромным объемом информации, что Kos-Boss и решил прокомментировать, добавив свое мнение, и это здорово. Начинающий разработчик прочитает статью, посмотрит комментарий и сделает вывод, мол: «понятно, можно написать как в статье, а можно сделать все более гибким, что позволит мне в дальнейшем с легкостью внедрять изменения».

            От себя хотелось бы добавить:
            void Update() {
                  rb.velocity = new Vector3 (speed, 0f,0f);
               }

            Значение speed стоит умножать на Time.deltaTime, чтобы предотвратить разную скорость при разном количестве fps. К примеру, если один человек запустит игру и у него будет 60 fps, а у другого компьютер более слабый, будет допустим 30 fps, то у второго человека машина будет перемещаться в два раза медленнее, по сравнению с первым, потому что у перого машина 60 раз в секунду получает ускорение speed, а у второго игрока только 30 раз в секунду, что в два раза медленнее.

            Все дело в том, что функция Update() выполняется каждый кадр. Умножение любой скоростной переменной на Time.deltaTime позволит сделать её независимой от количества кадров в секунду.

            Time.deltaTime изменяется в зависимости от fps, например если у человека 60 fps, Time.deltaTime образно говоря будет равен 0.25f, а если у человека игра подвисает и идет в 30 fps, то Time.deltaTime будет равен 0.5f (в два раза больше), таким образом у первого игрока 5f * 0.25f (будет выполняться 60 раз в секунду), а у второго 5f * 0.5f (будет выполняться 30 раз в секунду). В результате за реальную секунду у обоих игроков машина сдвинется на абсолютно одинаковое расстояние.

            Time.deltaTime нужно применять в функциях Update() везде, где есть какие-либо переменные, отвечающие за скорость передвижения того или иного объекта.

            P.S. — Нужно будет только увеличить значение speed, иначе после добавления Time.deltaTime скорость машины будет очень медленной.
              0
              Спасибо, именно это я и имел ввиду, когда писал:
              Много опасных «а вдруг» при дальнейшей разработке проекта


              По поводу
              void Update() {
                  rb.velocity = new Vector3 (speed, 0f,0f);
              }

              Тут в каждом кадре устанавливается скорость движения rigidbody, а не выполняется перемещение объекта на определённую величину. То есть от фпс тут будет зависеть то, как часто будет устанавливаться скорость (как часто вызывается Update()), но никак не скорость передвижения объекта. Поэтому умножать на Time.deltaTime в данном случае не нужно.

              Но Вы правильно сделали, что упомянули и разъяснили принцип работы Time.deltaTime, ведь это, наверное, самая распространённая ошибка начинающего Unity-разработчика.
                0
                Спасибо про Time.deltaTime, действительно не придал значение тому, что задается velocity, а не MovePosition или AddForce какой :)
                  0
                  Тут стоит упомянуть начинающим разработчикам, что
                  1) velocity лучше руками не трогать вообще, т.к. это ломает физику объекта. Используйте .AddForce(x, y, z, ForceMode.Force);
                  2) все вычисления физики надо делать в FixedUpdate(), а не Update(). Он для того и предназначен, что не зависит от мощности железа игрока.
                    0
                    1) velocity лучше руками не трогать вообще, т.к. это ломает физику объекта. Используйте .AddForce(x, y, z, ForceMode.Force);

                    Тут зависит от того, с какой целью Вы хотите её менять и какая степень реализма физики Вам необходима. Если реализм важен (настолько, насколько его может обеспечить физика Unity), то использовать мгновенное изменение вектора скорости конечно же не нужно.

                    2) все вычисления физики надо делать в FixedUpdate(), а не Update(). Он для того и предназначен, что не зависит от мощности железа игрока.

                    Да, тут согласен с Вами.

                    Ещё из коварного вспомнилось, что для произвольного перемещения/вращения rigidibody необходимо использовать методы MovePosition/MoveRotation (или параметры position/rotation) самого rigidbody, а не трансформа.
              0
              А можно еще короче, чтобы не писать 2 раза название переменной. Хотя ваш вариант скорее более читабелен.
              void changeDirection() {
                    isMovingRight ^= true;
              }
              
              0
              Вы могли бы подробнее рассказать, на какие этапы сколько времени ушло?
                +1
                На первые два этапа (концепция и создание наброска) я потратил около 5 часов. Это время я провел за изучением приложений, а также за оценкой собственных возможностей. Сам процесс написания кода, начиная от простенькой поделки до более-менее приемлемой игры, отнял у меня 36 часов работы. В это время я занимался игрой с самого утра до поздней ночи, делая небольшие перерывы на прием пищи и кофе-брейки. Остальное время ушло на создание моделей, текстур и визуальной составляющей игры (еще 36 часов). Чуть более 12 часов ушли на доводку — поиски оптимального значения скорости, механика «инкама» и прочее, и еще несколько часов на создание иконки и баннеров, необходимых для публикации в плеймаркет. Если считать по дням, то разработку я начал вечером 8 марта, а уже 14-го, приложение было опубликовано.
              • UFO just landed and posted this here
                  0
                  Если вы про графическое оформление GUI — то этот этап я оставляю практический на самый конец. А так, GUI присутствует в игре с самого начала и наращивается по мере развития игры (используются стандартные компоненты Unity).
                  • UFO just landed and posted this here
                    0
                    Если интересно, у нас это обычно делается следующим образом. Программист пишет логику самой игры, по ходу дела привязывая к ней необходимые элементы пользовательского интерфейса (на заглушках). Затем человек, ответственный за GUI, вместо заглушек подставляет необходимые графические элементы. Если необходимы какие-то дополнительные эффекты, для которых необходимо доработать логику, то за дело опять берётся программист.

                    Возможно, это не самый лучший подход и, в случае больших проектов в больших компаниях, всё необходимо планировать заранее. Но для маленькой студии с постоянной внутренней обратной связью это хороший и вполне достаточный вариант :)
                    0
                    del
                      0
                      Круто! За такой срок сделали. Респект.
                      0
                      Большое спасибо за туториал! Респект!
                      Я могу ошибаться, но вот в этой части кода мы «переписываем» гравитацию (ось У) и поэтому шар не упадет.

                      void Update() {
                          rb.velocity = new Vector3 (speed, 0f,0f);
                      }


                      Я бы изменил вот так:

                      void Update() {
                          rb.velocity = new Vector3(speed, rb.velocity.y, 0f);
                      }


                      А в целом очень даже ничего!

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