Как стать автором
Обновить

Второе практическое задание с сайта unity3dstudent.com

Время на прочтение 7 мин
Количество просмотров 6.9K
Продолжая разбор задачек с unity3dstudent.com, рассмотрим вторую из них. Вот разбор первой задачи.
image
Ссылка на оригинальное задание: www.unity3dstudent.com/2010/07/challenge-c02-beginner

Суть: нужно создать сцену, в которой по нажатию пробела в поле зрения игрока начнёт падать ящик, в итоге запуская другой ящик в сторону мишени. После попадания мишень должна исчезнуть с каким-нибудь звуком. Запуск ящика – на основе рычага: один ящик уже лежит с одной стороны, ещё один падает на другую сторону рычага.

UPD: третья задача



Часть 1: Сцена.


Сначала стандартно создадим плоскость, источник света, и расположим камеру так, чтобы было «всё хорошо видно» (об этом есть в разборе предыдущей задачи).

Теперь добавим мишень: создадим куб и растянем его до размеров, подобающих порядочной мишени. Пододвинем мишень так, чтобы она была перпендикулярна оси OX (направления осей можно посмотреть справа сверху во вкладке Scene). Сразу же набросаем в любом редакторе простенькую текстуру, отдалённо напоминающую мишень (сделаем её квадратной) и сохраним в папку Materials на панели Project (предварительно создав эту папку):



Теперь добавим в проект (опять же в папку Materials) материал targetMaterial и перетащим в его поле Texture (в панели Inspector) только что добавленную текстуру. Материал перетащим на мишень (назовём её объект в панели Hierarchy “target”).

Теперь займёмся рычагом-катапультой. Добавим в сцену куб, назовём его plank1 и растянем его «в доску»:



Теперь добавим к plank1 компонент Hinge Joint (Component->Physics->Hinge Joint) – в переводе означает «шарнир». На скриншоте выше он уже добавлен – появился оранжевый вектор. Он показывает, вокруг какой оси будет вращаться объект. Сейчас это направление совпадает с осью OX. Можно для проверки слегка повернуть доску относительно OX и запустить сцену.

Итак, нас не устраивает ось вращения – ведь нужно, чтобы при резком повороте доски ящик с одного её края полетел в направлении мишени. В панели Inspector находим компонент Hinge Joint для plank1 и находим группу полей Axis. Это и есть ось вращения. По умолчанию установлены следующие значения:
X: 1
Y: 0
Z: 0

Это и показывает сонаправленность оси OX. Нам подойдёт ось, параллельная OZ. Установим в поле Z единицу, в остальные – нули. Заметим, как поменял направление оранжевый вектор из центра plank1 на сцене.



С рычагом почти закончили. Сейчас проблема в том, что не хватает упора для бросаемого кубика. Добавим его: добавляем куб (назовём его plank2), сжимаем (см. скриншот ниже) и передвигаем так, чтобы он слегка пересекался с plank1, образуя сцепление (пока что лишь виртуальное):





Добавим plank2 компонент Rigidbody (к plank1 этот компонент уже добавился автоматически при добавлении Hinge Joint). «Сообщим» физическому движку о том, что plank1 и plank2 скреплены. Объекту plank1 добавим компонент Fixed Joint (Component->Physics->Fixed Joint). В поле Connected Body в панели Inspector сейчас стоит значение None (Rigidbody). Перетащим на его место plank2 из панели иерархии.

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



Добавим ему компонент Rigidbody. Если запустить сцену сейчас, когда рычаг опускается, куб падает на его конец (опускающийся). Конструкция готова к «выстрелу», осталось подготовить противовес, падение которого будет отправлять в полёт только что добавленный куб.

Добавим в проект префаб, назовём его dropcube. Создадим новый куб в сцене и перетянем его из панели иерархии на префаб. Добавим префабу компонент Rigidbody, а сам куб из сцены удалим. Массу префаба установим равной 30 (для расположения объектов на созданной мной сцене это значение позволяет запускаемому кубу долететь до мишени).

Теперь добавим в сцену пустой объект (Game Object->Create Empty) с названием dropper. Поместим его над дальним от камеры концом рычага в поле зрения камеры (это можно проверить, выбрав объект Main Camera и выбрав Game Object -> Align View to Selected) – оттуда позже будут появляться кубы (такие же, как dropcube):



Часть 2: Скрипты.


Займёмся скриптовой частью.

Во-первых, мишень должна исчезнуть после попадания в неё куба. Из прошлой задачи (ССЫЛКА) возьмём скрипт Selfdestroyer (я для всех задач буду использовать один проект, так что даже добавлять новый скрипт не пришлось – всё уже есть в панели Project) и прикрепим его к объекту мишени. Вот полный код этого скрипта (совсем небольшой):
using UnityEngine;
using System.Collections;

public class Selfdestroyer : MonoBehaviour 
{
	void OnCollisionEnter(Collision obj)
	{
		Destroy(gameObject);
	}
}


Теперь добавим в проект C#-скрипт Boxdropper. Он должен по нажатию на пробел создавать в точке нахождения объекта, к которому прикреплён, куб (точнее, создавать Instance префаба dropcube).

Раз требуется только реакция на нажатие клавиш – оставляем метод Update, метод Start (если он был автоматически сгенерирован) удаляем. Для осуществления задуманного нам, как минимум, потребуется передать в скрипт префаб. Добавим public переменную типа GameObject в класс:
public GameObject cubePrefab;

Изменим метод Update:
void Update () 
{
	if (Input.GetButtonDown("Jump"))
	{
		GameObject.Instantiate(cubePrefab, transform.position, transform.rotation);		
	}
}


Пара замечаний по поводу этого кода:

— во-первых, вместо Input.GetButtonDown(«Jump») можно использовать Input.GetKeyDown(KeyCode.Space). Второй вариант привязан строго к коду клавиши пробел, а первый — к клавише, которая в настройках управления игры исполняет роль клавиши прыжка (по умолчанию – пробел). Эти настройки можно увидеть, собрав проект и запустив получившийся исполняемый файл (если это окажется непонятно, могу расписать процесс поподробнее).

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

— здесь вызывается перегруженная функция Instantiate, имеющая 3 параметра, тогда как есть вариант и с одним: Instantiate(Object original). Последняя будет задавать параметры position и rotation (второй и третий параметр соответственно в вызове Instantiate с тремя параметрами) такими же, как у переданного фактическим параметром объекта. Важно помнить, что эти параметры берутся не из соответствующих значений для объекта, к которому прикреплён скрипт!

Теперь перетащим скрипт Boxdropper из панели Project на объект dropper в панели иерархии.
Что ж, задание почти выполнено. Если запустить сцену и нажать на пробел, упадёт куб и маленький куб (который уже лежал на плече рычага) полетит в мишень. Осталась одна незначительная нестыковка с формулировкой: нет звука при уничтожении мишени.

В первую очередь нам нужен сам звук, то есть файл с каким-нибудь звуком. Я взял первый попавшийся звук, имеющий хоть какое-то отношение к звуку взрыва, из одного из своих прошлых проектов. Добавляем звук в проект (у меня он назван onDestroySound).

Небольшое лирическое отступление о звуке в Unity. Звук здесь бывает моно и 3D (3D Sound). Фактически это показывает 1 или 2 канала в аудио. Для эффектов, имеющих достаточно чёткое пространственное положение (взрыв, звук удара и т.п.) рекомендуется использовать моно-звук, так как для его реальная громкость (слышимая игроком) будет зависеть от удалённости источника от слушателя (определяется наличием компонента Audio Listener, по умолчанию он есть у объекта Main Camera).

Если ваш звук оказался 3D (стоит галочка “3D Sound” в панели Inspector), не отчаивайтесь. Уберите «галочку» с пункта “3D Sound”, выберете в той же панели пункт “Force to mono” и нажмите “Apply”. Теперь модифицируем скрипт Selfdestroyer. Нам понадобится public переменная для звука, который будем проигрывать:
public AudioClip onDestroy;

Теперь в методе OnCollisionEnter помимо вызова метода Destroy добавим проигрывание звука:
void OnCollisionEnter(Collision obj)
{
	AudioSource.PlayClipAtPoint(onDestroy, transform.position);	
	Destroy(gameObject);
}


Такой вызов метода AudioSource.PlayClipAtPoint проиграет звук onDestroy так, будто источник звука находится в точке расположения объекта, к которому этот скрипт прикреплён (transform.position).

Осталось выбрать объект мишени на сцене или в панели иерархии и перетащить из проекта звуковой файл (onDestroySound) в поле “On Destroy (Audio Clip)” панели Inspector.

Замечание: можно добавить к любому объекту компонент Audio Source и после этого обращаться к нему как «audio» из скриптов. Есть даже специальный метод, который проиграет переданный звук в точке расположения объекта:

audio.PlayOneShot(AudioClip clip);


Однако в нашем случае проигрывание звука происходит непосредственно перед уничтожением объекта, и если использовать audio.PlayOneShot, то после удаления звук перестанет проигрываться. Это означает, что мы его и не услышим. Можете проверить сами (это даже полезно).

Итак, задание выполнено.

Часть 3. Эстетическая.


Казалось бы, чего ещё? Но меня терзала одна деталь: если бросать по несколько кубиков за раз (нажимать пробел несколько раз подряд), две «доски», образующие рычаг (plank1 и plank2) начинают смещаться друг относительно друга. Видно, например, на этом скриншоте (здесь в сцену добавлен ещё один источник света):



Тогда я подумал о самом простом решении: раз соединение plank1 и plank2 нам нужно всего на один раз, то почему бы от него не избавиться после того, как оно уже сыграло свою роль? Просто уничтожить plank2? Некрасиво.

Снова выбираем plank1 в панели иерархии и находим компонент Fixed Joint в панели Inspector. Замечаем любопытное поле:

Break Force: Infinity

То, что нужно! В моём случае подошло значение силы, требуемой для разлома соединений, в 10 (надеюсь, что ньютонов). Под «подошло» я имею в виду, что в момент отрыва метаемого кубика от поверхности рычага, plank2 отрывается от plank1 и отправляется в полёт:



Как оказалось, той же проблеме подвержен и Hinge Joint – странное поведение plank1 снова наблюдается при большом количестве падающих кубов. Решим проблему аналогично – установим в поле Break Force значение 30. В результате вся конструкция рычага после первого запуска разрушается, причём достаточно «физично».

Так выглядит сцена после первого запуска:



P.S. Для пущей красоты можно добавить материалы всем объектам в сцене: рычагу, всем кубам (большим и малым), полу. Как это делается, описано в начале статьи.

Заключение.


В этот раз кода чуть побольше (хотя всё так же меньше 10 «смысловых» строк, если не считать фигурные скобки и код, генерируемый автоматически), да и задание более содержательное. Уже появилась некоторая контролируемая нами физика (в виде соединений) и звук, включающийся при определённых событиях в сцене.
Теги:
Хабы:
+4
Комментарии 0
Комментарии Комментировать

Публикации

Истории

Работа

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн