Pull to refresh

Unity3d. Уроки от Unity 3D Student (B21-B24)

Reading time 10 min
Views 59K
Всем привет.

Ссылки на предыдущие уроки:


Базовый урок 21 — Вычисление расстояния между объектами




В уроке будет показано, как получить расстояние между двумя точками в трехмерной сцене, используя метод Vector3.Distance. Кроме того, в дополнение к оригинальному уроку я расскажу о свойстве Vector3.sqrMagnitude, при помощи которого можно получить более оптимизированный код вычисления расстояния.

При разработке игры часто возникает необходимость узнать расстояние между объектами. Один из самых простых способов это сделать — использовать метод Distance класса Vector3.
Рассмотрим следующую сцену: кубик fallbox, падающий вниз на более большой куб Cube; точечный источник света Point light, находящийся между ними; и, конечно, камера.



Если мы захотим, чтобы источник света загорался только тогда, когда он находится в пределах определенной дистанции от падающего куба, то нужно создать следующий скрипт:
Код на JavaScript:
var box : Transform;
 
function Update () { 
 var dist : float = Vector3.Distance(box.position, transform.position);
 Debug.Log(dist);
 
 if(dist <= 10){
  light.enabled = true;
 }else{
  light.enabled = false;
 }
}

Код на C#:
public Transform box;

private void Update()
{
    float dist = Vector3.Distance(box.position, transform.position);
    Debug.Log(dist);

    if (dist <= 10)    //вместо if/else можно использовать более сокращенную запись: light.enabled = dist <= 10;
    {
        light.enabled = true;
    }
    else
    {
        light.enabled = false;
    }
}

В скрипте мы сперва объявляем переменную box, в которой будем хранить ссылку на компонент Transform нашего падающего ящика. Затем в Update вызываем метод Vector3.Distance, передав в качестве аргументов координаты объектов, между которыми требуется вычислить расстояние. Полученное расстояние заносим в переменную dist и сравниваем его с необходимым значением (10 в нашем примере). Если расстояние не превышает заданное значение — включаем компонент света, в противном случае — выключаем. При помощи метода Debug.Log выводим значение текущего расстояния в консоль.
Добавляем скрипт на объект света Point Light, в поле скрипта Box перетаскиваем fallbox, запускаем сцену. Кубик падает вниз, свет не горит. При расстоянии между светом и кубиком <= 10 — свет включается.



Примечание от переводчика №1: расстояние также можно подсчитать, вычислив разность между координатами объектов, получив в результате объект типа Vector3, а затем обратиться к его свойству magnitude:
Код на C#:
float dist = (box.position - transform.position).magnitude;

Примечание от переводчика №2: использование метода Vector3.Distance или свойства Vector3.magnitude имеет один недостаток. Дело в том, что при расчете расстояния сначала получается квадрат значения расстояния, из которого, соответственно, надо извлечь корень. Извлечение корня является достаточно затратной операцией и при частом вызове для большого числа объектов может привести к падению производительности. В данном случае в качестве оптимизации можно использовать свойство Vector3.sqrMagnitude. Оно вернет квадрат расстояния, что вычисляется быстрее простого расстояния благодаря отсутствию операции извлечения корня. Полученный квадрат расстояния нужно будет сравнить с квадратом заданного значения, т. е. для примера из урока это будет выглядеть так:
Код на C#, укороченный, оптимизированный, с константой вместо «магического» числа:
public Transform box;
private const int cMaxDistance = 10;

private void Update()
{
    float dist = (box.position - transform.position).sqrMagnitude;
    light.enabled = dist <= cMaxDistance * cMaxDistance; //квадрат расстояния сравниваем с квадратом заданного предела!
    Debug.Log(dist);
}

Ссылка на оригинальный урок

Дополнительные материалы:

Ссылка на документацию метода Vector3.Distance
Ссылка на документацию свойства Vector3.sqrMagnitude

Базовый урок 22 — Создание паузы при помощи метода WaitForSeconds




В уроке рассказывается, как сделать паузу (задержку) в игре с использованием метода WaitForSeconds и инструкции yield.

Если вы хотите реализовать паузу между некими игровыми событиями (например, создание игровых объектов через определенные промежутки времени), то необходимо воспользоваться Корутинами (сопрограммами). Корутина — это специальным образом оформленный метод, работающий в основном потоке игры, и обычно вызываемый после метода Update. Корутина, в зависимости от заданных условий, может прервать свое выполнение в определенной точке своего кода, а затем вновь продолжить работу (подробнее о корутинах есть на хабре — прим. переводчика).
Посмотрим на сцену ниже. На ней есть куб, играющий роль земли, источник света, камера и пустой объект под именем spawn point, висящий над землей. Кроме того, в папке Project есть префаб weight — кубик с компонентом rigidbody. Наша цель — периодически создавать кубики из префаба в точке spawn point, после чего они будут падать на землю.



Создать объект легко — нужно воспользоваться уже знакомым методом Instantiate. Однако, если мы просто напишем вот такой скрипт и добавим его к объекту spawn point
Код на JavaScript:
var box : GameObject; //здесь храним ссылку на префаб порождаемого объекта
 
function Update () {
 Instantiate(box, transform.position, transform.rotation); //создаем объект
 }

… то получим примерно следующую картину:



Буквально за пару секунд выполнения у нас создалось слишком много объектов. Это не удивительно, ведь мы поместили создание объекта в метод Update, который вызывается каждый кадр игры, т. е. несколько десятков раз в секунду. Любые попытки как-то вставить задержку в метод Update, чтобы объекты создавались реже, ни к чему хорошему не приведут — это уменьшит FPS игры. Вместо этого нам поможет корутина.
Давайте реализуем скрипт создания объектов следующим образом:
Код на JavaScript:
var box : GameObject;
var readynow : boolean = true;
 
function Update () {
 if(readynow){
  MakeBox(); //в JS просто запускаем корутину
 }
}
 
function MakeBox(){
 readynow=false;
 Instantiate(box, transform.position, transform.rotation);
 yield WaitForSeconds(2); //приостанавливаем выполнение корутины на 2 секунды
 readynow=true;
}

Код на C#:
public GameObject box;
private bool readynow = true;
 
private void Update()
{
    if (readynow)
        StartCoroutine(MakeBox()); //в C# для запуска корутины нужно использовать метод StartCoroutine
}
 
private IEnumerator MakeBox() //Корутина должна возвращать IEnumerator 
{
 readynow = false;
 Instantiate(box, transform.position, transform.rotation);
 yield return new WaitForSeconds(2); //код приостановки корутины немного сложнее, чем в JS
 readynow = true;
}

Итак, в переменный box мы храним ссылку на префаб порождаемого объекта. Булева переменная readynow служит для определения, запускать ли сейчас метод-корутину MakeBox, которая создает объект. Внутри корутины MakeBox мы сперва запрещаем ее вызов (readynow = false), потом создаем объект методом Instantiate, а затем используем связку yield / WaitForSeconds (обратите внимание на различия в коде для JS и в коде для C# — прим. переводчика). Эта связка позволяет приостановить выполнение корутины, в данном случае, на 2 секунды, а потом вернуться обратно и продолжить выполнение, где мы теперь разрешаем вызов корутины (readynow = true). Метод Update при этом не простаивает эти 2 секунды, а продолжает выполняться, только не запускает заново новую корутину, пока не отработает предыдущая.
Напоминаю, что скрипт надо добавить к объекту spawn point, перетащить в поле скрипта Box префаб кубика и запустить сцену. Теперь кубики создаются через каждые 2 секунды.



Ссылка на оригинальный урок

Дополнительные материалы:

Ссылка на документацию класса WaitForSeconds

Базовый урок 23 — Система частиц




В уроке будет показано, как создать простой взрыв, используя систему частиц.
Примечание от переводчика: в оригинальном уроке используется старая версия Unity, в которой работа с системами частиц сильно отличается от текущей версии (4.3.Х). Поскольку рассказывать про старые компоненты уже не имеет смысла, я опишу работу с текущей версией, в следствии чего возникнут расхождения с оригиналом. Если все-таки кому-то нужно будет посмотреть про старую систему частиц — обращайтесь к оригинальному уроку по ссылке в конце перевода.

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



Давайте создадим новую систему частиц: меню GameObject -> Create Other -> Particle System. Добавится новый объект с компонентами Transform и Particle System. В Particle System перечислено множество настроек, изменяя которые можно добиться различных эффектов. Для эффекта взрыва зададим следующие настройки:
В шапке компонента:
Duration = 0.10 — продолжительность генерирования частиц. Поскольку взрыв будет кратковременным, мы можем задать самое маленькое значение
Looping — снимем галочку — флаг зацикливания генерации частиц. Для одиночного взрыва его нужно снять
Start Lifetime = 0.4 — продолжительность жизни частиц. Частицы будут существовать указанное время, после чего исчезнут
Start Speed = 25 — начальная скорость движения частиц
Start Size = от 1 до 3 — начальный размер частиц. Здесь мы внесем разнообразие и зададим не константное значение, а случайное значение между двумя константными. Для это нужно нажать маленький треугольник справа от значения параметра и выбрать в меню пункт «Random between two constants», после чего ввести значения 1 и 3.

На вкладке «Emission»:
Rate = 0 — число генерируемых частиц в секунду или в единицу пройденного расстояния системой частиц. Как ни странно, установим это значение в 0, так как нам необходимо одновременно сгенерировать частицы, а для этого лучше подойдет следующий параметр.
Bursts — позволяет задать массовый выброс частиц, что хорошо подходит для взрыва. Для этого надо нажать на кнопку с плюсиком и в появившейся строке задать значения Time = 0, Particles = 20. Благодаря этому, сразу же после появления объекта системы частиц на сцене будет произведен одновременный выброс 20 частиц.
На вкладке «Shape»:
Shape = Sphere — задает фигуру, определяющую направление распространения частиц. Так как мы хотим распространить частицы взрыва во все стороны — используем сферу.
На вкладке «Renderer»:
Material — необходимо задать материал для отображения частиц. Пока установлен материал по умолчанию, частицы отображаются серыми шариками. Чтобы сделать что-то более интересное, необходимо создать свой материал:

В окне Inspector появиться новый материал. Зададим ему любое имя, затем установим в поле Shader значение Particles/Alpha Blended и добавим текстуру для взрыва (рисунок звездочки):

Теперь этот материал надо добавить в поле Material вкладки Renderer нашей системы частиц (перетащив материал мышью или выбрав его из меню).
На всякий случай, продублирую настройки скриншотом (неиспользованные вкладки вырезаны):


Потестируем наш взрыв. Кликнем по созданной системе частиц в окне Hierarchy. Если все сделано правильно, в разные стороны рассыпался ворох звезд.



Теперь необходимо написать небольшой скрипт и добавить его к нашему взрыву. Это скрипт будет отвечать за уничтожение системы частиц со сцены, после того, как все ее частицы прекратят существование:
Код на JavaScript:
private var ps : ParticleSystem;
 
function Start () {
     ps = GetComponent(ParticleSystem);
}
 
function Update () {
    if(ps)
    {
       if(!ps.IsAlive())
       {
         Destroy(gameObject);
       }
    }
}

Код на C#:
private ParticleSystem ps;
 
private void Start() 
{
    ps = (ParticleSystem)GetComponent(typeof(ParticleSystem));
}
 
private void Update() 
{
    if (ps)
        if (!ps.IsAlive())
            Destroy(gameObject);
}

В скрипте мы сохраняем компонент системы частиц в переменной ps. В методе Update проверяем присутствие компонента на сцене. Если он есть — проверяем, «жив» ли он. Это делается при помощи вызова метода IsAlive — он вернет false, если система частиц больше не генерирует частицы и все ранее сгенерированные частицы исчезли. Если это так — удаляем объект системы частиц.

Все готово, чтобы из взрыва сделать префаб. Нажимаем в окне Project кнопку CreatePrefab, называем префаб starBursts, перетаскиваем на него взрыв из окна Hierarchy и удаляем взрыв со сцены.
Теперь нам только осталось написать скрипт и добавить его к падающему кубику:
Код на JavaScript:
var stars : ParticleSystem;
 
function OnCollisionEnter (col : Collision) {
 Instantiate(stars, transform.position, transform.rotation);
 Destroy(gameObject);
}

Код на C#:
public ParticleSystem stars;

private void OnCollisionEnter(Collision col)
{
    Instantiate(stars, transform.position, transform.rotation);
    Destroy(gameObject);
}


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

Ссылка на оригинальный урок (с использованием старой системы частиц)

Дополнительные материалы:

Документация компонента Particle System

Базовый урок 24 — Работа с циклом «For»




В уроке рассказывается об использовании оператора цикла «For», с помощью которого можно повторять необходимые действия, пока выполняется заданное условие.

При создании игры часто возникает задача выполнить несколько раз подряд некие повторяющиеся действия. Для этого не стоит «копипастить» код, а нужно воспользоваться циклами.
На нашей сцене есть земля, свет, камера и пустой объект creator.



Мы хотим, чтобы из объекта creator с небольшими паузами создались из префаба несколько других объектов. Для этого напишем следующий скрипт:
Код на JavaScript:
var myPrefab : Rigidbody;
var distanceMultiplier : float = 2;
 
function Start(){
 
    var i : int = 0;
    var pos : Vector3 = transform.position;
 
    for(i=0; i<=3; i++){
        Instantiate(myPrefab, Vector3(pos.x+i*distanceMultiplier, pos.y, pos.z), transform.rotation);
        yield WaitForSeconds(0.5);
        Debug.Log("made ball "+i);
 
    }
}

Код на C#:
public Rigidbody myPrefab;
private const float distanceMultiplier = 2f;

private IEnumerator Start()
{
    Vector3 pos = transform.position;
 
    for(int i=0; i<=3; i++)
    {
        Instantiate(myPrefab, new Vector3(pos.x + i * distanceMultiplier, pos.y, pos.z), transform.rotation);
        yield return new WaitForSeconds(0.5f);
        Debug.Log("made ball "+i);
    } 
}


Мы завели переменную для хранения создаваемого объекта myPrefab. В уроке это шарик с компонентом Rigidbody, но это не принципиально. Переменная distanceMultiplier нужна, чтобы разнести в пространстве создаваемые объекты. В методе Start мы сохраняем позицию объекта creator, которая будет служить основой для размещения создаваемых объектов. Затем происходит запуск цикла for.
В первой строке цикла задано условие работы. Цикл будет выполняться, пока истинно условие. Условие состоит из трех частей:
  • i=0 — задаем переменной-индексу начальное значение
  • i<=3 — устанавливаем, собственно, условие — выполнять цикл, пока значение индекса не больше 3
  • i++ — увеличиваем значение индекса на 1

При данной записи цикл отработает 4 раза: пока переменная i равна 0, 1, 2 и 3. Когда i станет равной 4 — произойдет выход из цикла.
Внутри цикла мы выполняем уже знакомые действия. Сперва создаем объект, задав ему позицию, равную позиции объекта creator, за исключением координаты х — к ней мы прибавляем текущее значение индекса, умноженное на константу distanceMultiplier. Затем делаем паузу на полсекунды, чтобы объекты не создавались одновременно. В конце цикла пишем отладочное сообщение в лог.
Добавляем скрипт к объекту creator, в поле скрипта myPrefab переносим префаб сферы с компонентом Rigidbody, запускаем сцену. Четыре сферы, одна за другой, появились из ниоткуда и попадали на землю.



Ссылка на оригинальный урок

Дополнительные материалы:

Ссылка на MSDN о различных циклах
Tags:
Hubs:
+20
Comments 12
Comments Comments 12

Articles