- PVSM.RU - https://www.pvsm.ru -

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

Всем привет.

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

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


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

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

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

Если мы захотим, чтобы источник света загорался только тогда, когда он находится в пределах определенной дистанции от падающего куба, то нужно создать следующий скрипт:
Код на 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 — свет включается.

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

Примечание от переводчика №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);
}

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

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

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

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


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

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

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

Создать объект легко — нужно воспользоваться уже знакомым методом Instantiate. Однако, если мы просто напишем вот такой скрипт и добавим его к объекту spawn point
Код на JavaScript:

var box : GameObject; //здесь храним ссылку на префаб порождаемого объекта
 
function Update () {
 Instantiate(box, transform.position, transform.rotation); //создаем объект
 }

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

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

Буквально за пару секунд выполнения у нас создалось слишком много объектов. Это не удивительно, ведь мы поместили создание объекта в метод 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 секунды.

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

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

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

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

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


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

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

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

Давайте создадим новую систему частиц: меню 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.
Unity3d. Уроки от Unity 3D Student (B21 B24)
На вкладке «Emission»:
Rate = 0 — число генерируемых частиц, как ни странно, установим в 0. Пояснение в следующем параметре
Bursts — позволяет задать массовый выброс частиц, что хорошо подходит для взрыва. Для этого надо нажать на кнопку с плюсиком и в появившейся строки задать значения Time = 0, Particles = 20. Благодаря этому, сразу после появления объекта системы частиц на сцене будет произведен одновременный выброс 20 частиц.
На вкладке «Shape»:
Shape = Sphere — задает фигуру, определяющую направление распространения частиц. Так как мы хотим распространить частицы взрыва во все стороны — используем сферу.
На вкладке «Renderer»:
Material — необходимо задать материал для отображения частиц. Пока установлен материал по умолчанию, частицы отображаются серыми шариками. Чтобы сделать что-то более интересное, необходимо создать свой материал:
Unity3d. Уроки от Unity 3D Student (B21 B24)
В окне Inspector появиться новый материал. Зададим ему любое имя, затем установим в поле Shader значение Particles/Alpha Blended и добавим текстуру для взрыва (рисунок звездочки):
Unity3d. Уроки от Unity 3D Student (B21 B24)
Теперь этот материал надо добавить в поле Material вкладки Renderer нашей системы частиц (перетащив материал мышью или выбрав его из меню).
На всякий случай, продублирую настройки скриншотом (неиспользованные вкладки вырезаны):
Unity3d. Уроки от Unity 3D Student (B21 B24)

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

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

Теперь необходимо написать небольшой скрипт и добавить его к нашему взрыву. Это скрипт будет отвечать за уничтожение системы частиц со сцены, после того, как все ее частицы прекратят существование:
Код на 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 скрипта необходимо поместить префаб системы частиц нашего взрыва.
Запускаем, любуемся эффектным уничтожением кубика.

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

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

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

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


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

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

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

Мы хотим, чтобы из объекта 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, запускаем сцену. Четыре сферы, одна за другой, появились из ниоткуда и попадали на землю.

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

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

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

Ссылка на MSDN о различных циклах [15]

Автор: Charoplet

Источник [16]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/unity3d/59469

Ссылки в тексте:

[1] Уроки B00-B03: http://habrahabr.ru/post/141362/

[2] Уроки B04-B08: http://habrahabr.ru/post/142845/

[3] Уроки B09-B12: http://habrahabr.ru/post/145565/

[4] Уроки B13-B16: http://habrahabr.ru/post/149721/

[5] Уроки B17-B20: http://habrahabr.ru/post/219193/

[6] Ссылка на оригинальный урок: http://www.unity3dstudent.com/2010/09/beginner-b21-finding-distance-between-vector3-points/

[7] Ссылка на документацию метода Vector3.Distance: http://docs.unity3d.com/Documentation/ScriptReference/Vector3.Distance.html

[8] Ссылка на документацию свойства Vector3.sqrMagnitude: http://docs.unity3d.com/Documentation/ScriptReference/Vector3-sqrMagnitude.html

[9] есть на хабре: http://habrahabr.ru/post/216185/

[10] Ссылка на оригинальный урок: http://www.unity3dstudent.com/2010/09/beginner-b22-pausing-scripts-with-waitforseconds/

[11] Ссылка на документацию класса WaitForSeconds: http://docs.unity3d.com/Documentation/ScriptReference/WaitForSeconds.html

[12] Ссылка на оригинальный урок (с использованием старой системы частиц): http://www.unity3dstudent.com/2010/10/beginner-b23-particle-systems/

[13] Документация компонента Particle System: http://docs.unity3d.com/Documentation/Components/class-ParticleSystem.html

[14] Ссылка на оригинальный урок: http://www.unity3dstudent.com/2010/10/beginner-b24-for-loops/

[15] Ссылка на MSDN о различных циклах: http://msdn.microsoft.com/ru-ru/library/f0e10e56(v=vs.90).aspx

[16] Источник: http://habrahabr.ru/post/221545/