Привет! Добро пожаловать на второй день моих 100 дней VR!

В последний раз мы остановились на учебнике Unity Roll a ball. Я многое узнал о том, как программировать в Unity и как использовать некоторые из их существующих API. Что еще более важно, я научился ориентироваться и использовать редактор Unity!

Редактор определенно отличается от веб-разработки или разработки для мобильных устройств, где вы создаете все с помощью кода, но редактор определенно упрощает работу!

Сегодня во 2-й день я начал смотреть 2-й урок Unity, Космический шутер Unity, я много узнал о Unity из предыдущего урока, может быть, даже достаточно, чтобы начать разрабатывать свою простую игру (с МНОГО помощи от stackoverflow), но вместо этого я решил укрепить свои основы, пройдя еще 1-2 урока.

Я не продвинулся так далеко, как в первый день из-за своей работы, но я закончил первый раздел:

Настройка игры, плеер и камера

Настройка проекта

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

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

Я видел это, когда вы создавали новый проект в Unity. Если вы нажмете на эту вкладку, вы увидите проект, вы можете скачать его там (или из магазина ресурсов Unity)

А затем, когда вы создаете новый проект, просто не забудьте добавить пакет активов в ресурс:

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

Пока все хорошо, пока ничего сложного!

Игровой объект Player

Теперь, когда я настроил проект, я перешел к созданию игрового объекта Player.

Это ничем не отличается от того, что я узнал в предыдущем уроке. Только на этот раз вместо создания 3D-сферы я перетаскиваю предоставленный мне существующий актив модели.

Классный корабль!

Несколько интересных вещей, которые стоит отметить в этом видео

Mesh Collider, Mesh Filter и Mesh Renderer

Глядя на компоненты корабля, мы видим, что есть Mesh Filter и Mesh Renderer.

Из простого поиска Google:

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

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

После этого, если вы хотите увидеть свою модель, вам понадобится Mesh Renderer, который берет вашу сетку и накладывает на нее материал. Материалом является кожа корабля, который вы видите выше.

Наконец, у нас есть компонент Mesh Collider, который мы добавляем и который принимает сетку. Это позволяет нам создать коллайдер, точно соответствующий форме нашего корабля.

Важно подумать о компромиссе между игровым процессом и производительностью, на который мы должны пойти.

Более мелкозернистый коллайдер => Unity выполняет больше работы => Более низкая производительность. Однако, с другой стороны, мы можем не захотеть создавать кубический коллайдер над нашим кораблем.

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

В игре с более тонким контролем, такой как этот космический шутер, это было бы плохо.

Простой способ изменить значения Game Transform

Также в другой заметке, не связанной с этим, я нашел отличный трюк для редактора.

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

А затем оттуда перетащите мышь вверх и вниз, чтобы изменить значения. Это намного проще, чем просто вводить значения вручную!

Камера и освещение

В следующем видео мы немного поиграли с камерой и освещением.

Камера

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

Освещение

В более поздней версии Unity проект уже идет с Directional Light в иерархии, но эти источники света, как и следовало ожидать, освещают вашу игру!

Направленный свет — это просто свет, исходящий из определенного направления.

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

Добавление фона

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

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

Оказывается, если вы перетащите файл .tif в квадрат, он автоматически создаст для вас материал, который вы сможете использовать.

Шейдеры

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

К счастью, нам не нужно учиться делать что-либо из этого (пока), и мы можем просто использовать существующие, которые нам предоставляет Unity. Ура фреймворкам!

В видео мы применили эффект Unlit Texture Shader, чтобы наше фоновое изображение выглядело ярче.

Перемещение корабля

Итак, теперь, когда у нас есть настройка среды, мы, наконец, начали кодировать.

using UnityEngine;
using System.Collections;
[System.Serializable]
public class Boundary
{
    public float xMin, xMax, zMin, zMax;
}
public class PlayerController : MonoBehaviour
{
    public float speed;
    public float tilt;
    public Boundary boundary;
    void FixedUpdate ()
    {
        float moveHorizontal = Input.GetAxis ("Horizontal");
        float moveVertical = Input.GetAxis ("Vertical");
        Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
        rigidbody.velocity = movement * speed;
        rigidbody.position = new Vector3 
        (
            Mathf.Clamp (rigidbody.position.x, boundary.xMin, boundary.xMax), 
            0.0f, 
            Mathf.Clamp (rigidbody.position.z, boundary.zMin, boundary.zMax)
        );
        rigidbody.rotation = Quaternion.Euler (0.0f, 0.0f, rigidbody.velocity.x * -tilt);
    }
}

Давайте разберем этот код.

Жесткое тело

Самое главное сейчас то, что в Unity 5.X этот код на самом деле не будет работать. Это потому, что мы больше не получаем наш компонент RigidBody, как сейчас.

Мы должны сделать что-то похожее на то, что мы сделали с обучающим роликом, и сохранить экземпляр нашего RigidBody в функции Start(), например так:

using UnityEngine;
using System.Collections;
[System.Serializable]
public class Boundary
{
    public float xMin, xMax, zMin, zMax;
}
public class PlayerController : MonoBehaviour
{
    public float speed;
    public float tilt;
    public Boundary boundary;
    public RigidBody rigidbody;
    void Start()
    {
        Rigidbody = GetComponent<RigidBody>();
    }
    void FixedUpdate ()
    {
        float moveHorizontal = Input.GetAxis ("Horizontal");
        float moveVertical = Input.GetAxis ("Vertical");
        Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
        rigidbody.velocity = movement * speed;
        rigidbody.position = new Vector3 
        (
            Mathf.Clamp (rigidbody.position.x, boundary.xMin, boundary.xMax), 
            0.0f, 
            Mathf.Clamp (rigidbody.position.z, boundary.zMin, boundary.zMax)
        );
        rigidbody.rotation = Quaternion.Euler (0.0f, 0.0f, rigidbody.velocity.x * -tilt);
    }
}

Теперь нам нужно использовать GetComponent‹RigidBody› для доступа к компонентам нашего GameObject.

Граница

Первое, что вы можете заметить, это наличие еще одного класса: Boundary. Он имеет [System.Serializable] поверх него.

[System.Serializable]
public class Boundary
{
    public float xMin, xMax, zMin, zMax;
}

Что означает сериализуемость, так это то, что вы сообщаете Unity, что если вы сделаете класс общедоступной переменной, он выставит переменные в классе для использования в инспекторе.

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

Движение

Чтобы переместить корабль, мы применили что-то очень похожее на то, что мы делали в первый день с обучением мячу.

Мы видим, что мы по-прежнему используем ту же функцию FixedUpdate() и точно так же получаем входные данные о движении игроков.

Новая интересная часть — использование большей части библиотеки Unity.

Unity поставляется с собственной математической библиотекой Mathf. Библиотека предоставляет нам отличные игровые расчеты.

rigidbody.position = new Vector3 
    (
        Mathf.Clamp (rigidbody.position.x, boundary.xMin, boundary.xMax), 
        0.0f, 
        Mathf.Clamp (rigidbody.position.z, boundary.zMin, boundary.zMax)
    );

В этом случае мы используем Mathf.Clamp, чтобы помочь установить границу позиции, чтобы, если наша позиция превышает минимум, она оставалась на минимуме, а если она превосходила максимум, она оставалась на максимуме.

Создание снимков

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

  • Мы создаем нашу пулю так же, как мы создаем наш фон.
  • Мы создаем объект Quad, а затем используем файл .tif для создания материала (или просто используем существующий) и прикрепляем материал.
  • Мы применили к нему несколько шейдеров. Мы можем оптимизировать нашу производительность, используя мобильные шейдеры вместо обычных. Мобильные шейдеры предлагают меньше элементов управления, но в результате они менее требовательны к вычислительным ресурсам.

Наконец, у нас есть код, который перемещает нашу пулю вперед:

using UnityEngine;
using System.Collections;
public class Mover : MonoBehaviour
{
    public float speed;
    void Start ()
    {
        rigidbody.velocity = transform.forward * speed;
    }
}

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

Стрельба выстрелами

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

В видео было показано, что мы создали новый «спаунер» GameObject.

Спаунер — это просто пустой GameObject, местоположение которого мы используем для создания новых пуль.

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

В нашем скрипте мы добавили общедоступную переменную как для префаба пули, так и для местоположения точки появления.

using UnityEngine;
using System.Collections;
[System.Serializable]
public class Boundary
{
    public float xMin, xMax, zMin, zMax;
}
public class PlayerController : MonoBehaviour
{
    public float speed;
    public float tilt;
    public Boundary boundary;
    public GameObject shot;
    public Transform shotSpawn;
    public float fireRate;
    private float nextFire;
    void Update ()
    {
        if (Input.GetButton("Fire1") && Time.time > nextFire)
        {
            nextFire = Time.time + fireRate;
            Instantiate(shot, shotSpawn.position, shotSpawn.rotation);
        }
    }
    void FixedUpdate ()
    {
        float moveHorizontal = Input.GetAxis ("Horizontal");
        float moveVertical = Input.GetAxis ("Vertical");
        Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
        rigidbody.velocity = movement * speed;
        rigidbody.position = new Vector3 
        (
            Mathf.Clamp (rigidbody.position.x, boundary.xMin, boundary.xMax), 
            0.0f, 
            Mathf.Clamp (rigidbody.position.z, boundary.zMin, boundary.zMax)
        );
        rigidbody.rotation = Quaternion.Euler (0.0f, 0.0f, rigidbody.velocity.x * -tilt);
    }
}

Часть кода, которая нас действительно интересует, находится в нашей новой функции Update():

void Update ()
    {
        if (Input.GetButton("Fire1") && Time.time > nextFire)
        {
            nextFire = Time.time + fireRate;
            Instantiate(shot, shotSpawn.position, shotSpawn.rotation);
        }
    }

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

Самое интересное, как мы создаем нашу пулю. Это делается в Unity с помощью функции Instantiate.

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

И это все!

Вывод

Во второй день я закончил первую часть туториала по Space Shooter. Опять же, большая часть обучения на самом деле выполняется вне кода с помощью редактора.

Хорошая новость заключается в том, что я начинаю видеть похожие вещи с использованием GameObjects, материалов и префабов, чтобы назвать несколько примеров.

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

Я надеюсь, что увижу шаблоны и использую их для создания своей игры.

Моя цель — узнать достаточно, что доступно в Unity, чтобы я мог расшириться и начать писать свой собственный код!

Примечание: вау, эти статьи длинные! На самом деле мне интересно, что занимает больше времени, просмотр видео, изучение и понимание того, что происходит, или написание этих объяснений!

Посетите оригинальный День 2

Вернуться к 100 Days of Unity VR Development Table of Contents