Поиск на сайте: Расширенный поиск


Новые программы oszone.net Читать ленту новостей RSS
CheckBootSpeed - это диагностический пакет на основе скриптов PowerShell, создающий отчет о скорости загрузки Windows 7 ...
Вы когда-нибудь хотели создать установочный диск Windows, который бы автоматически установил систему, не задавая вопросо...
Если после установки Windows XP у вас перестала загружаться Windows Vista или Windows 7, вам необходимо восстановить заг...
Программа подготовки документов и ведения учетных и отчетных данных по командировкам. Используются формы, утвержденные п...
Red Button – это мощная утилита для оптимизации и очистки всех актуальных клиентских версий операционной системы Windows...

Разработка вашей первой игры с помощью Unity и C#. Часть 2

Текущий рейтинг: 4.67 (проголосовало 6)
 Посетителей: 3827 | Просмотров: 5571 (сегодня 0)  Шрифт: - +

В первой статье я рассказал о некоторых основах и об архитектуре Unity. В этой статье я намерен исследовать работу с двухмерной графикой в Unity, которая опирается на поддержку двухмерной графики (далее для краткости 2D) в Unity, добавленную в версии 4.3. Вы могли работать с такой графикой и в Unity до версии 4.3, но этот процесс был весьма тяжелый без стороннего инструментария. Я хочу просто перетаскивать изображения в свою сцену, после чего она должна появляться в ней и работать, как ожидалось. Это как раз часть того, что предлагается в Unity 4.3, и в этой статье я рассмотрю другие возможности, разрабатывая базовую двухмерную игру-аркаду, что позволит вам освоить некоторые значимые концепции Unity.

2D в Unity

Чтобы получить поддержку 2D в Unity при создании нового проекта, выберите 2D из раскрывающегося списка в диалоге создания нового проекта. После этого все свойства проекта по умолчанию подстраиваются под 2D (можно увидеть в Edit | Project Settings | Editor), и любые изображения, импортируемые в ваш проект, становятся спрайтами, а не просто текстурами. (Об этом я расскажу подробнее в следующем разделе.) Кроме того, представление сцены переключается по умолчанию в режим 2D. На самом деле в этом режиме просто предоставляется вспомогательная кнопка, которая фиксирует две оси при разработке сцены, но никак не влияет на саму игру. Вы можете щелкнуть ее в любой момент, чтобы войти в режим 2D или выйти из него. 2D-игра в Unity реально остается 3D-средой — просто ваша работа ограничивается осями X и Y. На рис. 1 и 2 показаны варианты с выбранным режимом 2D и без него. Я выделил камеру, чтобы вы видели контуры области просмотра камеры, но она смотрит в пространство как фигура прямоугольной формы.

*
Рис. 1. Режим 2D выбран — камера имеет фокус

*
Рис. 2. Режим 2D не выбран — камера имеет фокус

2D-игра в Unity реально остается 3D-средой — просто ваша работа ограничивается осями X и Y.

Выделенная камера настроена как прямоугольная (orthographic camera) — это один из двух режимов камеры в Unity. Этот тип камеры, широко применяемый в 2D, не масштабирует объекты, отстоящие дальше от ваших глаз, т. е. нет глубины от позиции камеры. Другой тип камеры — перспектива (perspective), которая показывает объекты так, как их видят ваши глаза, с глубиной. Причин использовать каждый из типов камеры много, но, как правило, следует выбирать перспективу, когда требуется визуальная глубина, если только вы не хотите соответственно масштабировать свои объекты. Вы можете сменить тип камеры, просто выбрав камеру и изменив тип проекции. Советую опробовать это и посмотреть, как изменяется область просмотра вашей камеры, когда вы начинаете смещать объекты дальше от глаз по оси Z. Режим по умолчанию можно сменить в любой момент, что повлияет только на изображения, которые будут потом импортироваться в ваш проект.

Когда по умолчанию выбран режим 3D, изображения распознаются как тип Texture.

Если у вас есть существующий проект в Unity или если вы не уверены, выбран ли режим 2D в диалоге проекта, то можете задать свойствам проекта значения по умолчанию для 2D через Edit | Project Settings | Editor; иначе вам придется вручную задавать тип каждого импортируемого вами 2D-изображения, что несколько утомительно при большом количестве изображений.

Все дело в спрайте

Когда по умолчанию выбран режим 3D, изображения распознаются как тип Texture. Перетащить текстуру в сцену нельзя; текстура должна быть применена к какому-то объекту. Создавать 2D-игры таким путем вовсе не забавно. Я хочу просто перетаскивать изображения, чтобы они появлялись в моей сцене. Однако, если режим по умолчанию — 2D, все становится проще. Теперь, когда я перетаскиваю изображение в Unity, оно распознается как тип Sprite.

Это позволяет перетаскивать в Unity все изображения, образующие графику игры, а затем из Unity в сцену. Если какие-то изображения выглядят мелкими, то вместо их масштабирования везде, где они встречаются, вы можете просто уменьшить значение Pixels To Units. Это очень распространенная операция в Unity как для 2D, так и для 3D и обычно более производительная, чем масштабирование объектов через свойство scale преобразования.

Отпуская объекты, вы наверняка заметите, что один объект оказывается поверх другого. Unity на внутреннем уровне создает серию вершин, даже для 2D-изображений, поэтому порядок рисования может различаться в разных частях изображений. Всегда лучше явно указывать z-порядок своих изображений. Это делается одним из трех методов, перечисленных в порядке, в котором Unity рисует ваши спрайты.

  1. Вы задаете свойство Sorting Layer в Sprite Renderer.
  2. Вы задаете свойство Order in layer в Sprite Renderer.
  3. Вы задаете значение позиции по оси Z в Transform.

Уровень сортировки имеет высший приоритет, за ним следует порядок в уровне, а затем z-значение в преобразовании.

Уровни сортировки рисуются в порядке определения. Когда вы добавляете другие уровни (в Edit | Project Settings | Tags and Layers), Unity сначала рисует любой объект, который обнаруживается в уровне Default (затем учитывается Order in Layer, потом — значение позиции по оси Z в Transform), далее Background, Platforms и т. д. Поэтому вы можете легко поправить перекрытие объектов, присвоив их уровню Platforms, а объекту, который должен быть поверх, указав Order in Layer со значением 1; тогда он будет нарисован после всего, что имеет Order in Layer, равный 0.

Распространенная функциональность

На рис. 3 показан уровень, содержащий некоторые изображения платформ и фона, которые были созданы перетаскиванием и заданием уровней сортировки (sorting layers).

*
Рис. 3. Игровой уровень

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

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

void Update()
{
  // Returns -1 to 1
  var horizontal = Input.GetAxis("Horizontal");
  // Возвращает true или false. Событие щелчка левой кнопки
  // мыши или касания на мобильном устройстве вызывает
  // срабатывание этого.
  var firing = Input.GetButtonDown("Fire1");
}

Если вы проверите Edit | Project Settings | Input, то увидите варианты ввода по умолчанию (Unity предоставляет целый набор таких вариантов в каждом новом проекте) и сможете задать новые. На рис. 4 показаны умолчания для считывания перемещения по горизонтали. Параметры left и right представляют клавиши-стрелки «влево» и «вправо», но заметьте, что для перемещения по горизонтали используются «a» и «d». Их можно сопоставить со вводом от джойстика. Вы можете добавить новые значения или изменить умолчания. Поле Sensitivity контролирует, насколько быстро Unity будет проходить от 0 до 1 или –1. При нажатии стрелки вправо первый кадр может получить значение .01, а затем возможно довольно быстрое ускорение до 1, хотя скорость можно подстроить так, чтобы игровой персонаж мгновенно получал горизонтальную скорость. Вскоре я покажу код для применения этих значений к вашим игровым объектам. Для чтения этих значений необходимость в компоненте GameObject на самом деле нет; вы просто используете ключевое слово Input в своем коде для доступа к функциональности, обеспечивающей чтение ввода. Как общее правило, ввод следует считывать в функции Update в противоположность FixedUpdate, чтобы избежать потери событий ввода.

*
Рис. 4. Значения по умолчанию для ввода перемещений по горизонтали

Линейное перемещение Вещи нужно иметь возможность перемещать. Если это игра, где экран прокручивается сверху вниз (top-down game), то гравитация обычно не играет важной роли. Если же это аркада (platformer), гравитация, наоборот, крайне важна. В любом случае обнаружение коллизии объектов весьма критично. Вот базовые правила. Компонент Rigidbody2D или RigidBody (используется для 3D), добавленный к игровому объекту, автоматически придаст ему массу, сделает его поддерживающим гравитацию и способным реагировать на прикладываемые к нему силы. Согласно Википедии: «В физике абсолютно твердое тело (rigid body) является идеализацией твердого тела (solid body), в котором пренебрегается деформацией. Другими словами, расстояние между любыми двумя данными точками абсолютно твердого тела остается постоянным во времени независимо от действующих на него внешних сил». Тот же принцип применяется в играх. Добавление абсолютно твердого тела позволяет вам делает вызовы наподобие показанных на рис. 5.

Рис. 5. Добавление перемещения и скорости

void FixedUpdate()
{
  // Значение от -1 до 1 для перемещения по горизонтали
  float moveHorizontal = Input.GetAxis("Horizontal");
  // Vector просто дает вам значение x,y,z, например 1,0,0
  // для макс. смещения вправо, 0,1,0 для макс. смещения вверх.
  // Сохраняйте текущее Y-значение, которое увеличивается
  // для каждого интервала из-за гравитации.
  var movement = new Vector3(moveHorizontal *
   _moveSpeed, rigidbody.velocity.y, 0);
  rigidbody.velocity = movement;
  if (Input.GetButtonDown("Fire1"))
  {
    rigidbody.AddForce(0, _jumpForce, 0);
  }
}

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

Обнаружение коллизий Объект получает массу от своего компонента RigidBody, но, кроме того, вы должны сообщить Unity, как обрабатывать коллизии с этим объектом. Здесь размер и форма ваших изображений или моделей не имеют значения, хотя масштабирование все же влияет на физику самого объекта. Важны размер и форма компонента коллайдера (collider component), который представляет собой просто определенную область вокруг или внутри объекта, для которого нужно, чтобы Unity обнаруживала соприкосновение с другим объектом. Это обеспечивает поддержку таких ситуаций, когда вы входите в область неактивного зомби или когда при приближении к горе на вас скатывается валун.

Коллайдеры бывают самых разнообразных форм. Коллайдер для 2D может быть окружностью, границей (гранью), многоугольником или прямоугольником. Прямоугольные коллайдеры хороши для объектов с формой наподобие квадратов или прямоугольников, или когда вы просто хотите обнаруживать коллизии в квадратной области. Подумайте о платформе, на которой вы стоите, — это хороший пример прямоугольного коллайдера. Простое добавление этого компонента к вашему игровому объекту позволяет задействовать преимущества физических столкновений. На рис. 6 я добавил персонажу коллайдер в виде окружности и абсолютно твердое тело, а к платформе — прямоугольный коллайдер. Когда я запускаю игру в Editor, игрок немедленно падает на платформу и останавливается. Никакого кода не требуется.

*
Рис. 6. Добавление коллайдеров

Вы можете перемещать и изменять размер области, охватываемой коллайдером, изменяя свойства компонента коллайдера. По умолчанию объекты с коллайдерами не проходят друг сквозь друга (кроме триггеров, о которых я расскажу в следующем подразделе). Поддержка коллизий требует наличия коллайдеров в обоих игровых объектах и, по крайней мере, один объект должен иметь компонент RigidBody, если только это не триггер.

Объект получает массу от своего компонента RigidBody, но, кроме того, вы должны сообщить Unity, как обрабатывать коллизии с этим объектом.

Если я хочу, чтобы Unity вызывала мой код, когда впервые происходит этот случай коллизии, то просто добавляю следующий код в игровой объект через компонент скрипта (о нем мы говорили в предыдущей статье):

void OnCollisionEnter2D(Collision2D collision)
{
  // Если вы хотите проверять, с кем вы столкнулись,
  // то, как правило, должны использовать теги, а не имена
  if (collision.gameObject.tag == "Platform")
  {
    // Отыгрываем звуки шагов или приземления
  }
}

Триггеры Иногда нужно обнаруживать коллизию, но безо всякой физики. Возьмите такую ситуацию, когда вы подбираете в игре какой-то ценный предмет. Вы же не хотите, чтобы монеты разлетелись перед игроком, когда он подойдет к ним; вам нужно, чтобы монеты были собраны и никак не мешали перемещению игрока. В этом случае используется коллайдер, который называется триггером. Это не более чем коллайдер с помеченным флажком IsTrigger в его свойствах. Тем самым физика отключается, и Unity будет лишь вызывать ваш код, когда объект A (содержащий коллайдер) попадет в область объекта B (тоже содержащего коллайдер). В данном случае в коде применяется метод OnTriggerEnter2D вместо OnCollisionEnter2D:

void OnTriggerEnter2D(Collider2D collider)
{
  // Если игрок попадает на триггер
  if (collider.gameObject.tag == "Player")
  {
    // О контроллере игры расскажу чуть позже
    GameController.Score++;
    // Не вызывайте Destroy(this); поскольку this - это
    // компонент скрипта в игровом объекте, вы используете
    // this.gameObject или просто gameObject, чтобы
    // уничтожить объект
    Destroy(gameObject);
  }
}

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

Одна вещь, о которую часто спотыкаются разработчики-новички, — поведение абсолютно жестких тел при добавлении к ним коллайдеров. Если в моем объекте есть коллайдер, имеющий форму окружности, и я помещаю этот объект на наклонную плоскость (как указывается формой ее коллайдера), он начинает катиться (рис. 7). Это моделирует то, что вы видите в физическом мире, когда колесо попадает на склон. Я не использую прямоугольный коллайдер для своего персонажа из-за того, что у этого коллайдера есть границы, которые могут задевать границы других коллайдеров, что будет приводить к перемещению с рывками. Коллайдер с формой окружности обеспечивает более плавное перемещение. Однако, когда плавное вращение неприемлемо, вы можете использовать параметр Fixed Angle в компоненте Rigidbody.

*
Рис. 7. Применение коллайдера с формой окружности для плавного перемещения

Звук Чтобы слышать звук, нужен компонент Audio Listener, который по умолчанию существует в любой камере. Для проигрывания звука просто добавьте компонент Audio Source к игровому объекту и укажите аудиоклип. Unity поддерживает большинство основных форматов звука и будет кодировать более длинные клипы в MP3. Если у вас множество источников звука с клипами, назначенными в Unity Editor, учитывайте, что все они будут загружены в период выполнения. Вместо этого вы можете загружать звук через код, размещенный в специальной папке ресурсов и уничтожать его, когда он больше не нужен.

Импортировав звук в свой проект, я сохранил его как WAV-файл, который представляет собой несжатое аудио. Unity перекодирует более длинный звук для оптимизации, поэтому всегда используйте звук максимального качества, которым вы располагаете. Это особенно верно для коротких файлов вроде звуковых эффектов, которые Unity не станет кодировать. Я также добавил компонент Audio Source к своей главной камере, хотя мог бы добавить его к любому игровому объекту. Затем я назначил аудиоклип Adventure этому компоненту Audio Source и сбросил флажок Loop, поэтому он постоянно проигрывается как петля. Три простых шага, и теперь в игре есть фоновая музыка.

GUI/элементы HUD (Heads-Up Display) Система GUI может охватывать многие вещи в игре. Она может включать систему меню, индикатор здоровья, текущий счет, арсенал оружия и др. Как правило, систему GUI вы постоянно видите на экране независимо от того, куда смотрит камера (хотя это не обязательно). GUI-функционал в Unity в настоящее время полностью перерабатывается, и в Unity 4.6 появится новая система uGUI. Поскольку эта версия еще не вышла, мы рассмотрим здесь лишь некоторую базовую функциональность, но, если вас интересуют подробности о новой системе GUI, загляните в мой блог на channel9 по ссылке channel9.msdn.com/Blogs/AdamTuliper.

Чтобы вывести на экран простой текстовый дисплей (например, score: 0), я открыл Game Object | Create Other | GUI Text. Этой команды в Unity 4.6 больше нет, поэтому советую посмотреть видеоролик по uGUI, о котором я упоминал. В версии 4.6 по-прежнему можно добавить компонент GUI Text к игровому объекту, щелкнув кнопку Add Component; он просто убран из меню Editor. В существующей (устаревшей) системе GUI в Unity вы не можете увидеть свои GUI-объекты в представлении сцены — только в представлении Game, что превращает создание разметки в несколько странное занятие. Если хотите, используйте чисто программный подход к настройке своего GUI, и для этого есть класс GUILayout, позволяющий автоматически отслеживать виджеты. Но я предпочитаю систему GUI, где можно легко работать, используя мышь; вот почему я нахожу гораздо совершеннее систему uGUI. (До uGUI лидером в этой области был довольно основательный сторонний продукт — NGUI, который и задействовали как исходную кодовую базу для uGUI.)

Самый простой способ обновлять текстовый дисплей — найти и назначить в Editor ссылку на игровой объект с этим GUIText и обрабатывать его подобно метке в .NET, обновляя его свойство text:

void UpdateScore()
{
  var score = GameObject.Find("Score").GetComponent<GUIText>();
  score.text = "Score: 0";
}

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

Отслеживание счета Это несложная задача. У вас просто есть класс, предоставляющий открытый метод или свойство, с помощью которого задается счет. В играх обычно есть объект Game Controller, действующий как организатор игры. Game Controller может отвечать за инициацию сохранений игры, загрузку, учет текущего счета и др. В этом примере у меня есть класс, предоставляющий переменную score, как показано на рис. 8. Я назначаю этот компонент пустому игровому объекту, чтобы он был доступен при загрузке сцены. Когда счет обновляется, происходит обновление и GUI. Переменная _scoreText назначена в Unity Editor. Просто перетащите любой игровой объект с GUIText на это поле или используйте виджет поиска, где данный компонент скриптов предоставляет переменную Score Text в редакторе.

Рис. 8. Создание переменной _scoreText

public class GameController : MonoBehaviour
{
  private int _score;
  // Перетащите игровой объект GuiText на это предоставляемое
  // в Editor поле или ищите его при запуске, как на рис. 12
  [SerializeField]
  private GUIText _scoreText;
  void Start()
  {
    if (_scoreText == null)
    {
      Debug.LogError("Missing the GuiText reference. ");
    }
  }
  public int Score
  {
    get { return _score; }
    set
    {
      _score = value;
        // Обновляем счет на экране
      _scoreText.text = string.Format("Score: {0}", _score);
    }
  }
}

После этого я могу просто обновить (в данном примере) код триггера гриба для увеличения счета при каждом подборе ценного предмета:

void OnTriggerEnter2D(Collider2D collider)
{
  if (collider.gameObject.tag == "Player")
  {
    GameController.Score++;
    Destroy(gameObject);
  }
}
Автор: Адам Тьюлипер  •  Иcточник: msdn.microsoft.com  •  Опубликована: 11.09.2015
Нашли ошибку в тексте? Сообщите о ней автору: выделите мышкой и нажмите CTRL + ENTER
Теги:   unity.


Оценить статью:
Вверх
Комментарии посетителей
Комментарии отключены. С вопросами по статьям обращайтесь в форум.