Обнаружение столкновений в вашей игре является важным элементом как реализма, так и функциональности. На нашем уровне из последнего урока вы в настоящее время сможете ходить по деревьям и уходить с самого уровня, который не является ни полезным, ни реалистичным! Давайте исправим это с помощью коллайдеров.
Окончательный результат предварительного просмотра
Примечание: вы встретите аббревиатуру «RMB» на протяжении всей этой статьи. Я имею в виду нажатие «правой кнопки мыши».
Шаг 1: Представляем коллайдеры
Обнаружение столкновений в вашей игре является важным элементом как реализма, так и функциональности; в настоящее время ходя вокруг вашего уровня, вы сможете ходить по деревьям, что не очень реалистично. Давайте исправим это с помощью коллайдеров .
Коллайдеры — это компоненты, встроенные в Unity, которые обеспечивают обнаружение столкновений с использованием их различных «ограничивающих рамок», зеленые линии которых показаны вокруг дерева на изображении ниже. Сетка-коллайдер, однако, не имеет никаких зеленых линий, окружающих изображение под ним, так как он использует сетку дерева, обведенную синим цветом.
При использовании коллайдеров столкновение обрабатывается для нас, при этом коллайдер вычисляет, какая часть ограничивающего прямоугольника была перехвачена первой, и контролирует его реакцию на столкнувшиеся объекты. Эти данные хранятся и предоставляются нам с помощью нескольких функций, что позволяет нам запускать более конкретное поведение, когда объекты входят, занимают и покидают ограничивающую рамку, если это необходимо. Мы рассмотрим некоторые из этих функций в этом руководстве.
Однако сейчас давайте воспользуемся несколькими коллайдерами, чтобы просто не дать нашему игроку уйти с края карты.
Шаг 2: Использование коллайдеров в качестве границ уровня
Создайте пустой GameObject, используя верхнее меню GameObject> Create Empty, и переименуйте его в «Границы». Убедитесь, что его положение в Инспекторе равно (0, 0, 0).
Дублируйте его, используя RMB> Duplicate или Ctrl + D и переименуйте его в «Bottom». Перетащите «Низ» на «Границы», чтобы сделать его дочерним объектом объекта «Границы».
С выбранным «Bottom» добавьте компонент Box Collider, используя верхнее меню, Component> Physics> Box Collider, и в инспекторе установите значения Position и Center Box Collider к значениям, показанным ниже:
Нажмите ось Y (зеленая) Гизмо сцены в верхнем правом углу окна сцены, чтобы перевести вид сцены в вид сверху и уменьшить масштаб, чтобы вы могли видеть весь свой уровень, как у меня ниже:
Дублируйте «bottom» три раза и переименуйте их в «Right», «Left» и «Top». Выберите каждый из них за раз и установите значения их положения и центра компонента Box Collider на значения, указанные ниже.
Теперь вы должны были окружить свой уровень со всех четырех сторон пустыми игровыми объектами, каждый из которых содержит компоненты Box Colliders, как показано ниже. Если вы сейчас запустите свою игру, вы обнаружите, что больше не можете упасть с карты.
Давайте теперь применим коллайдеры в другой ситуации …
Шаг 3: Хватит ходить по деревьям!
В настоящее время мы можем просто пройтись по деревьям на нашем уровне, что не слишком хорошо. Вот что мы собираемся сделать …
Мы создадим новый префаб с выбранным нами деревом, применим коллайдер и изменим его размер, чтобы он соответствовал стволу дерева, а затем поменяем дерево, которое мы использовали в инструменте Terrains ‘Place Trees’ на префаб с коллайдером. Довольно просто верно? Давайте начнем…
В окне проекта выберите дерево ольхи в «Terrain Assets / Trees Ambient-Occlusion /» и перетащите его в сцену; расположите его ближе к контроллеру от первого лица, чтобы вы могли легко и быстро перейти к нему.
С выбранным деревом в Иерархии добавьте капсульный коллайдер в качестве компонента через верхнее меню «Компонент> Физика> Капсульный коллайдер».
Появится диалоговое окно ниже, предупреждающее вас о том, что добавление этого компонента разорвет соединение с его исходным префабом. То есть, если вы измените исходное дерево ольхи, изменения не будут видны в этом экземпляре.
Для нас это не проблема, поэтому выберите «Добавить», чтобы подтвердить добавление компонента капсульного коллайдера.
Нажмите play и попробуйте пробежать по этому конкретному дереву, и вы обнаружите, что даже близко не можете подобраться к нему из-за размера капсульных коллайдеров «Bounding Box».
С выбранным деревом используйте Инспектор, чтобы отрегулировать значения радиуса и центральной точки коллайдера так, чтобы он занимал только ствол дерева, как показано ниже.
Не забудьте остановить свою игру! Не останавливайся! В противном случае ваши изменения будут отменены, когда игра остановлена!
Как мило с моей стороны, чтобы напомнить вам 🙂
Запустите игру еще раз, и вы сможете идти прямо к дереву, но не через его ствол, идеально!
Итак, у нас теперь есть Дерево, через которое наш пользователь не может пройти, однако мы все равно можем пройти через все другие деревья, которые были размещены с помощью инструмента Места деревьев Terrain. Давайте заменим их на дерево, к которому мы только что добавили коллайдер. Как мы это сделаем? С использованием префабов.
В окне проекта создайте новую папку и переименуйте ее в «Prefabs», а в ней создайте новый «Prefab» и переименуйте в «tree-prefab». Присвойте дерево с коллайдером префабу, выделив его в иерархии и перетащив на префаб. Наконец, удалите дерево из Иерархии.
Теперь у нас есть дерево с коллайдерами, настроенными и готовыми к использованию; все, что осталось сделать, это выбрать Terrain в Иерархии и в Инспекторе выбрать инструмент «Place Trees» (который мы использовали в предыдущем уроке), чтобы разместить деревья над ландшафтом.
Текущее дерево, размещаемое над картой, является деревом «Ольха»; выберите его и нажмите «Редактировать деревья …», чтобы открыть диалоговое окно «Редактировать дерево». Щелкните по кругу справа от выбранного дерева, в данном случае — «Ольха», и выберите древовидный префаб в появившемся диалоговом окне «Выбрать объект GameObject».
Все деревья теперь будут заменены на наше дерево с коллайдером. Чтобы закончить, выберите местность и в нижней части Инспектора убедитесь, что включено «Создание столкновений деревьев».
Теперь запустите игру снова, и вы должны столкнуться со всеми деревьями, которые вы поместили! Если вы разместили более одного типа деревьев, повторите процесс для других деревьев, которые у вас есть.
Шаг 4: Создание нашего сборного пикапа
Теперь мы собираемся начать разработку префаба для нашего пикапа, который будет разбросан по уровню для сбора игроком. Загрузите этот пакет Unity в папку Projects Asset и дважды щелкните его, чтобы распаковать его.
Пакет извлечет папку Models вместе с моделью .FBX для нашей раскладки и добавит новый префаб ‘pickup-prefab’ в вашу папку Prefab.
Прежде чем пользователь соберет звукосниматели, мы должны создать их на карте. Мы могли бы сделать это вручную, перетащив их из окна проекта на ландшафт. Тем не менее, было бы немного интереснее, если бы они случайно появлялись по карте в заранее определенных точках появления … пора начинать писать сценарии!
Шаг 5: Краткое введение в UnityScript
Unity предоставляет несколько вариантов программирования на языке JavaScript, UnityScript, C # и форму Python — Boo. Все три одинаково быстры и могут взаимодействовать. Все три могут использовать библиотеки .NET, которые поддерживают базы данных, регулярные выражения, XML, сети и так далее. Все три также поддерживаются MonoDevelop, однако мы будем использовать JavaScript (UnityScript).
На этом этапе предыдущий опыт программирования будет полезен и, вероятно, будет означать, что этот раздел представляет собой краткое резюме. Для тех из вас, кто плохо знаком с программированием, я бы посоветовал проверить Nettuts + Javascript из серии Null, если вы хотите выйти за рамки основ Unity. Если есть интерес, я могу опубликовать более подробное руководство по написанию сценариев в Unity, дайте мне знать в комментариях …
А пока, сделайте глубокий вдох и давайте прямо сейчас погрузимся.
переменные
Переменные являются контейнерами для информации. Они могут называться как угодно, но должны быть буквенно-цифровыми, начинаться со строчной буквы и иметь префикс с ключевым словом «var», как показано в примере ниже. Для имен переменных, состоящих более чем из одного слова, например, «demo test», используется соглашение camelBack / CamelCase , в результате чего получается «demoTest».
Если вы попытаетесь использовать имя переменной, уже используемой Unity, например, «transform», вы получите ошибку, поскольку имена переменных ДОЛЖНЫ быть уникальными.
01
02
03
04
05
06
07
08
09
10
11
12
|
// basic variable declaration & assignment example
public var demo = 5;
// basic variable declaration example
public var demo;
function DemoFunction()
{
// variable assignment within function
demo = 5;
}
|
Как показано выше, переменные не должны иметь значение, назначенное им сразу после объявления. Они могут быть инициализированы или им присвоено значение позже в более поздней части сценария.
Однако вы всегда должны определять тип данных для информации, хранящейся в переменной, когда она объявлена, чтобы компилятор знал, с каким типом данных он имеет дело, и сможет выполнять быстрее.
Некоторые примеры распространенных типов данных:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
// VARIABLE DATATYPE DECLARATION & ASSIGNMENT EXAMPLES
// String — list of characters within quotation marks
public var demo : String;
public var demo : String = «this is the variable ‘playerSpeed’ being assigned a String value»;
// int — a whole number
public var demo : int;
public var demo : int = 100;
// Float — a decimal number
public var demo : float;
public var demo : float = 1.47;
// Boolean — true or false
public var demo : Boolean;
public var demo : Boolean = true;
// Array — a list of objects
public var demo : Array;
public var demo : Array = [];
// Vector3 — set of XYZ values
public var demo : Vector3;
public var demo : Vector3 = Vector3(1, 1, 1);
|
В этой статье, посвященной оптимизации производительности, вы можете увидеть некоторые статистические данные о производительности, полученной благодаря использованию типизации данных.
Пока что все вышеперечисленные переменные имеют префикс «public». Все «публичные» переменные будут доступны в качестве параметров в Инспекторе, когда выбран «Сценарий» или «GameObject», к которому прикреплен «Сценарий», а также будут доступны в других сценариях, в которых «Сценарий» присоединен как компонент.
Если вы не собираетесь изменять значение с помощью инспектора или хотите, чтобы оно не было доступно для других сценариев, добавьте к нему префикс «private».
1
2
3
4
5
|
// PUBLIC VARIABLES DECLARATION
public var demo : int;
// PRIVATE VARIABLES DECLARATION
private var demo : int;
|
Если вы хотите иметь глобальную переменную, вы можете использовать модификатор static, как показано ниже. Доступ к нему можно получить из любого другого сценария, используя имя сценария, за которым следует точка и, наконец, имя переменной. Это называется «точечный синтаксис» и демонстрируется в следующем сценарии.
1
2
3
4
5
|
// GLOBAL (STATIC) VARIABLE DECLARATION
static var demo : int;
// USING DOT SYNTAX TO ACCESS STATIC VARIABLE
ScriptName.demo;
|
функции
Функции определяют серию инструкций, которые могут вызываться повторно в вашем скрипте.
Как и переменные, они должны быть буквенно-цифровыми, но начинаться с заглавной буквы. Если вы попытаетесь использовать имя функции, уже используемое Unity, например «Update ()», вы получите ошибку, так как функция SIGNATURES должна быть уникальной.
SIGNATURE функции состоит из ее имени, списка параметров и типа возвращаемого значения (если есть). Никакие две функции не могут иметь одинаковую подпись, хотя две функции могут иметь одинаковое имя. Когда две функции имеют одинаковое имя, но разные подписи, это называется перегрузкой , к которой мы вскоре придем.
Подобно соглашению с переменными, функции из более чем одного слова, такие как «демонстрационная функция», используют соглашение CamelCase с лидирующей заглавной буквой, что приводит к «DemoFunction». В некоторых случаях это не так, например, print (…).
Чтобы использовать или «вызывать» вашу функцию, достаточно просто ввести имя функции в вашем коде, за которым следует пара скобок «()».
1
2
3
4
5
6
7
8
|
// BASIC FUNCTION EXAMPLE
function DemoFunction()
{
// instructions here
}
// CALLING BASIC FUNCTION
DemoFunction();
|
Однако вы можете иметь несколько функций с одинаковым именем, но разными параметрами, как показано ниже («перегрузка» функции). Функции также могут иметь обязательные и дополнительные параметры, как показано ниже:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// BASIC FUNCTION WITH PARAMATER
function DemoFunction(para:int)
{
// instructions here
}
// CALLING BASIC FUNCTION WITH PARAMETER
DemoFunction(1);
// OVERLOADING ‘DemoFunction’ FUNCTION WITH MORE PARAMATERS
// NOTE THE SECOND PARAMETER IS OPTIONAL WITH A DEFAULT VALUE OF 0
function DemoFunction(para1:int, para2:String = «default parameter»)
{
// instructions here
}
// CALLING BASIC FUNCTION WITH DEFAULT PARAMETER
DemoFunction(1);
// CALLING BASIC FUNCTION WITH SPECIFIED PARAMETER
DemoFunction(1, «specified parameter»);
|
Вызов функции с параметрами — это просто вопрос ввода параметров в скобках для используемой функции, как показано выше.
Порядок выполнения функции события
Поскольку скрипт выполняется в Unity, есть несколько «функций событий», которые вызываются в определенном порядке. Вы можете использовать их для управления поведением вашей игры. Давайте посмотрим на наиболее популярные, в порядке исполнения, при запуске в Unity:
Функция события: «Пробуждение ()»
Функция Awake () всегда вызывается перед любыми функциями Start, а также сразу после создания префаба.
Функция события: «Старт ()»
Функция Start () вызывается перед первым обновлением фрейма, только если включен экземпляр скрипта.
Функция события: «Update ()»
Функция «Update ()» вызывается каждый кадр, в котором отображается игра, т.е. если ваша частота кадров составляет 30 кадров в секунду (кадров в секунду), то функция будет вызываться 30 раз в секунду. Вы должны поместить большую часть своего кода поведения в эту функцию, кроме всего, что связано с физикой. Стоит отметить, что частота кадров Unity Player может колебаться во время выполнения, в зависимости от оборудования, на котором он работает, и вычислительной мощности, необходимой для каждого кадра. Поэтому очень важно, чтобы ваш код в этой функции был максимально оптимизирован.
Функция события: ‘LateUpdate ()’
Функция «LateUpdate ()» вызывается один раз за кадр после завершения функции «Update ()». Все вычисления, выполняемые в Update (), будут завершены, когда начнется LateUpdate ().
Функция события: ‘FixedUpdate ()’
Функция FixedUpdate () вызывается на каждом физическом шаге; он будет синхронизирован с физическим движком Unity. Код в этой функции также должен быть максимально оптимизирован.
Функция обработчика событий: ‘OnApplicationQuit ()’
Функция OnApplicationQuit фактически определяется как обработчик событий; все обработчики событий начинаются с «Вкл». Он вызывается для всех игровых объектов при выходе из приложения. В редакторе он вызывается, когда пользователь останавливает режим воспроизведения. В веб-плеере он вызывается, когда веб-представление закрыто.
Вы можете найти больше информации о Порядке исполнения и Порядке обновления в Руководстве Unity.
Вы начнете замечать множество ссылок на Руководство по Unity, Справочное руководство по Unity и, в частности, Руководство по написанию сценариев Unity . Эти страницы должны быть первыми, куда вы заглядываете, если хотите узнать больше об определенном Компоненте или конкретной функции или свойстве. Научись звонить им домой.
Шаг 6: наш первый сценарий — порождение пикапа
Наконец пришло время, пришло время создать наш первый скрипт в Unity!
Создание скрипта PickupController
Прежде чем писать какие-либо сценарии, создайте новую папку в окне проекта и переименуйте ее в «Сценарии».
Создайте новый Script by RMB в папке Scripts и выберите «Create> JavaScript», затем используйте F2, чтобы переименовать скрипт в «PickupController».
Дважды щелкните скрипт, чтобы открыть его в редакторе по умолчанию Unity, который для тех, кто на ПК, — UniSciTe, а для тех, кто на OSX, — Unitron. Нам не нужна функция Update
которая добавлена по умолчанию, поэтому удалите ее. Теперь мы готовы начать писать сценарии появления нашего пикапа.
Однако прежде чем мы начнем, нам нужно выбрать место, где мы хотели бы, чтобы наш пикап появлялся; мы сделаем это, используя пустой GameObject и получим положение его компонента Transform.
Добавление SpawnPoint
Используйте верхнее меню, чтобы добавить Empty GameObject, GameObject> Create Empty, и в Инспекторе установите его положение (0, 0, 0) и переименуйте его в «PickupSpawnPoints». Это будет родительский объект, который будет содержать нашу точку (ы) появления, чтобы помочь организовать нашу Иерархию.
При именовании сценариев или объектов GameObject убедитесь, что вы получаете ТОЧНО имена, как они есть в руководстве, поскольку они будут использоваться позже в наших сценариях и в противном случае будут возвращать различные ошибки.
В Иерархии выберите Игрока, а затем с помощью главного меню добавьте еще один Пустой GameObject, который добавит Пустой GameObject на сцену в позиции Игрока, переименуйте его в «spawnpoint» и перетащите его на PickupSpawnPoints GameObject, чтобы сделать PickupSpawnPoints родительским ,
С выбранной точкой появления GameObject используйте Transform Tools, чтобы переместить его перед игроком, как на скриншоте выше, и убедитесь, что он находится над местностью.
Чтобы наш скрипт PickupController мог получать объекты GameObject, которые являются точками SpawnPoints, мы будем использовать теги …
Пометка SpawnPoint
Пометка GameObjects очень похожа на присвоение им имен, однако при использовании тегов нам доступны несколько дополнительных полезных функций: GameObject.FindWithTag (…) и GameObject.FindGameObjectsWithTag (…) .
Unity поставляется с несколькими встроенными тегами, которые вы можете найти в раскрывающемся списке тегов в верхней части инспектора; Вы также можете добавить свой собственный, что мы будем делать сейчас.
Чтобы открыть диспетчер тегов, выберите «Добавить тег» в нижней части раскрывающегося списка «Теги» или в верхнем меню «Правка»> «Настройки проекта»> «Теги».
Чтобы добавить тег, просто раскройте список «Теги», щелкните следующий доступный элемент и начните вводить имя тега, который вы хотите добавить. Добавьте SpawnPoint в элемент 0, как показано ниже.
Теперь, когда мы добавили наш тег SpawnPoint, нам нужно перейти и применить его к нашей точке появления. Снова выберите точку появления в Иерархии и примените новый тег SpawnPoint из выпадающего меню. Легко.
Итак, наша точка появления теперь настроена, время начинать писать сценарии!
Сценарии нашего скрипта PickupController
Вернитесь к скрипту PickupController и добавьте следующий код:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
// the pickup prefab, assigned via the Inspector
public var pickupPrefab:GameObject;
// the spawnpoint that our pickup will be spawned at
private var spawnPoint:GameObject;
function Awake()
{
// retrieve GameObject tagged as ‘SpawnPoint’ within the ‘PickupSpawnPoints’ GameObject which this script is a Component of
spawnPoint = gameObject.FindWithTag(«SpawnPoint»);
// spawn the pickup
SpawnPickup();
}
function SpawnPickup()
{
// retrieve the position and rotation of the pickup’s spawn point
var spawnedPickupPosition:Vector3 = spawnPoint.transform.position;
var spawnedPickupRotation:Quaternion = spawnPoint.transform.rotation;
// instantiate (create) the pickup prefab with the above position and rotation
var spawnedPickup:GameObject = Instantiate(pickupPrefab, spawnedPickupPosition, spawnedPickupRotation);
// set the spawned pickup as a child of the ‘PickupSpawnPoints’ gameobject that this script is a Component of
spawnedPickup.transform.parent = spawnPoint.transform;
}
|
Посмотрите на комментарии и посмотрите, имеет ли это смысл. У нас есть две переменные: одна общедоступная, которая может быть назначена через инспектор, и одна частная, которая назначена в функции Awake.
Функция Awake, если вы помните, вызывается во время загрузки скрипта; он извлекает наш Spawnpoint GameObject через свой тег и присваивает его переменной spawnPoint скрипта. Затем вызывается SpawnPickup ().
Функция SpawnPickup извлекает позицию и вращение точки появления, а затем создает экземпляр pickupPrefab в найденной позиции и вращении.
Новый клон нашего pickupPrefab возвращается Instantiate (…) и сохраняется как spawnedPickup, а затем становится дочерним для spawnPoint, назначая его родительское свойство компоненту Transform компонента spawnPoint. Это не обязательно, а просто чтобы поддерживать нашу Иерархию организованной.
Хотя этот сценарий завершен, и мы настроили нашу точку появления, если вы запустите игру прямо сейчас, ничего не произойдет, потому что мы еще не прикрепили сценарий в качестве компонента к любому из наших объектов GameObject — поэтому он никогда не будет выполнен ,
Добавление нашего скрипта PickupController в качестве компонента
Добавление скрипта в качестве компонента GameObject невероятно просто и может быть выполнено любым из двух способов.
Во-первых, выберите GameObject, для которого вы хотите добавить скрипт в качестве Компонента; это может быть GameObject в Иерархии ИЛИ Prefab в Окне Проекта. Затем вы просто нажимаете и перетаскиваете скрипт, который хотите добавить в качестве компонента, в Инспектор и отпускаете его, и он будет добавлен для вас. Легко!
Второй способ — просто перетащить скрипт на имя GameObject / Prefab, в который вы хотите добавить его, либо в иерархии, либо в окне проекта. Вам решать, что использовать; Я использую оба.
Поэтому нам нужно добавить наш скрипт PickupController в качестве компонента GameObject PickupSpawnPoints — сделайте это, используя любой из вышеперечисленных методов. Затем сценарий должен появиться в Инспекторе при выборе игрового объекта PickupSpawnPoints, как показано ниже:
Если вы сейчас попробуете запустить игру, вы увидите ошибку в консоли в левом нижнем углу экрана, щелкните по ней, чтобы просмотреть ошибку более подробно.
Если вы прочитаете ошибку, это имеет смысл: мы не присвоили нашу переменную pickupPrefab, поэтому, когда сценарий пытается создать экземпляр переменной, нет ничего (нулевого) для создания экземпляра, и поэтому он выдает ошибку.
Чтобы назначить наш pickup-prefab переменной pickupPrefab с помощью Инспектора, есть два подхода. Первый — через диалоговое окно «Выбор GameObject», доступное, когда вы щелкаете по кружку справа от «None (игровой объект)», а второе — просто перетаскивая префаб в текст «None (игровой объект)».
Используйте один из описанных выше методов, чтобы назначить prefab pickup-переменной переменной pickupPrefab компонента PickupController в игровом объекте PickupSpawnPoints.
Если вы сейчас запустите игру, звукосниматель появится на месте spawnPoint — вы, однако, сможете пройти прямо через него!
Перед тем, как забрать звукосниматели, давайте сделаем их немного интереснее и сделаем так, чтобы несколько датчиков случайным образом появлялись в диапазоне точек появления … не волнуйтесь, это не так сложно и не так сложно, как кажется!
Шаг 7: Случайное появление нескольких пикапов
Наличие только одного пикапа в одной точке появления довольно просто и понятно, поэтому давайте изменим наш сценарий, чтобы поддерживать неограниченное количество точек появления и выборки в них случайным образом.
Настройка нашего скрипта PickupController
Удалите переменные, определенные в верхней части вашего скрипта, и замените их следующими:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
// the pickup prefab, assigned via the Inspector
public var pickupPrefab:GameObject;
// the number of pickups to have around the level at any one time
public var numberOfPickups:int = 2;
// the ARRAY of spawnpoints that our pickup will be spawned at
private var spawnPointList:GameObject[];
// array of which spawn points are currently available for spawning at
private var spawnIndexAvailableList:Array = [];
// variable to hold the total number of spawn points, saves having to recalculate
private var numberOfSpawnPoints:int;
|
Итак, какие изменения были сделаны?
Первая переменная pickupPrefab осталась идентичной, но закрытая переменная spawnPoint, которая была введена в качестве GameObject, теперь стала массивом GameObjects, поэтому мы можем хранить несколько точек появления.
Переменная spawnIndexAvailableList отслеживает, какие точки появления доступны, т. Е. У них не создается датчик, поэтому несколько датчиков не создаются в одном месте.
Теперь удалите и замените текущую функцию Awake()
на следующую:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
function Awake()
{
// retrieve GameObjects tagged as ‘SpawnPoint’ within the ‘PickupSpawnPoints’ GameObject which this script is a Component of
spawnPointList = gameObject.FindGameObjectsWithTag(«SpawnPoint»);
// retreive number of spawn points
numberOfSpawnPoints = spawnPointList.length;
// make sure number of pickups doesn’t exceed number of spawn points
if (numberOfPickups > numberOfSpawnPoints) numberOfPickups = numberOfSpawnPoints;
// make all spawn points available by setting each index to true
for (var i:int = 0; i < numberOfSpawnPoints; i++)
{
spawnIndexAvailableList[i] = true;
}
// spawn X amount of pickups according to numberOfPickups
for (var j:int = 0; j < numberOfPickups; j++)
{
SpawnPickup();
}
}
|
Так что это немного сложнее, чем раньше … давайте посмотрим, что происходит.
Первое изменение, которое вы заметите, это то, что мы теперь используем FindGameObjectsWithTag(...)
который будет извлекать ВСЕ GameObjects, помеченные как «SpawnPoint», в отличие от FindWithTag(...)
который будет возвращать только один.
Количество найденных точек возрождения затем сохраняется как переменная, чтобы избежать необходимости пересчитать ее позже в скрипте. Чтобы избежать ошибок при порождении, мы проверяем, что количество пикапов, которые мы собираемся порождать, не превышает количество точек появления.
Затем мы устанавливаем для каждого элемента spawnIndexAvailableList
значение true
поскольку изначально все точки появления доступны для появления. Наконец, мы порождаем пикапы, вызывая SpawnPickup()
внутри цикла for.
Последнее, что нужно сделать, это удалить текущую функцию SpawnPickup
и заменить ее следующим:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
function SpawnPickup()
{
// generate a random integer to use as the index to select a spawn point from the list
var randomSpawnIndex:int = Random.Range(0, numberOfSpawnPoints);
// while the selected spawn index is unavailable regenerate another one
while (!spawnIndexAvailableList[randomSpawnIndex])
{
randomSpawnIndex = Random.Range(0, numberOfSpawnPoints);
}
// retrieve the position and rotation of the pickups spawn point
var spawnedPickupPosition:Vector3 = spawnPointList[randomSpawnIndex].transform.position;
var spawnedPickupRotation:Quaternion = spawnPointList[randomSpawnIndex].transform.rotation;
// instantiate (create) the pickup prefab with the above position and rotation
var spawnedPickup:GameObject = Instantiate(pickupPrefab, spawnedPickupPosition, spawnedPickupRotation);
// set the spawned pickup as a child of the ‘PickupSpawnPoints’ gameobject that this script is a Component of
// this is so we can use SendMessageUpwards within scripts attached to the pickupPrefab to call functions within this script
spawnedPickup.transform.parent = spawnPointList[randomSpawnIndex].transform;
// set the name of the pickup as its index
spawnedPickup.name = randomSpawnIndex.ToString();
// make the spawn index unavailable to prevent another pickup being spawned in this position
spawnIndexAvailableList[randomSpawnIndex] = false;
}
|
Опять же, это совсем не так, как раньше, поэтому давайте быстро разберемся с тем, что сейчас происходит.
Сначала мы генерируем случайное целое число между 0 и numberOfSpawnPoints, используя Random.Range (…), который будет использоваться для получения точки появления в этом индексе из spawnPointList.
Затем мы используем цикл while для проверки того, доступен ли этот индекс, и для продолжения создания нового индекса, пока он не станет доступен; это предотвращает появление нескольких пикапов в одной и той же позиции.
Затем мы получаем положение и вращение, как мы делали в предыдущей версии этой функции, единственное отличие состоит в том, что мы получаем доступ к позиции и повороту выбранной точки появления через массив, а не напрямую.
Инстанцирование объекта остается таким же, как и раньше, как и процесс превращения spawnedPickup в дочерний объект точки вызова (за исключением, конечно, ссылки через Array).
Затем мы устанавливаем имя датчика в качестве его индекса, используя ToString () для преобразования целого числа в значение String и, наконец, устанавливаем сгенерированный индекс как недоступный, назначая false
этому индексу массива.
Добавление большего количества очков возрождения
Теперь все, что осталось сделать, это продублировать точку появления в Иерархии (RMB> Duplicate или Ctrl + D) и расположить их вокруг уровня. Первоначально для тестирования я расположил четыре точки появления вокруг игрока, прежде чем дублировать больше и расположить их дальше по уровню.
В конце концов я добавил 25 точек появления вокруг своей сцены, а затем с помощью Инспектора изменил количество датчиков в PickupController до 15.
Когда вы сейчас запустите игру, вы должны увидеть более одного пикапа, появившегося в разных точках появления по всей карте, давайте теперь подберем их!
Шаг 8: Сбор пикапа с использованием коллайдеров
Теперь, когда у нас есть пикапы повсюду, пришло время реально их забрать … снова время для коллайдеров!
В настоящее время мы просто проходим через звукосниматели, но нам нужно как-то обнаружить, что игрок столкнулся с ними. Мы можем сделать это с помощью Box Collider.
В окне проекта выберите сборный пикап и добавьте Box Collider, используя верхнее меню, Component> Physics> Box Collider. Если вы снова запустите игру, вы не сможете проходить через пикапы, а сталкиваться с ними.
Теперь давайте использовать встроенные функции Unity для написания дополнительного поведения при обнаружении этого столкновения.
Создание скрипта PlayerController
В папке «Сценарии» создайте новый сценарий и переименуйте его в «PlayerController». Этот сценарий будет обрабатывать все варианты поведения, возникающие в результате столкновения с любым из датчиков.
Выберите Player в Иерархии и добавьте скрипт PlayerController в качестве Компонента, перетащив его в Инспектор, или используйте другой метод, если хотите.
Откройте скрипт PlayerController, удалите функцию Update () и добавьте следующий код:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
// the PickupController component of the ‘PickupSpawnPoints’ GameObject
private var pickupController:PickupController;
function Awake()
{
// retrieve the PickupSpawnPoints gameObject
var pickupSpawnPoints:GameObject = gameObject.Find(«PickupSpawnPoints»);
// and then retreive the PickupController Component of the above PickupSpawnPoints gameObject
pickupController = pickupSpawnPoints.GetComponent(«PickupController»);
}
function OnControllerColliderHit (hit:ControllerColliderHit)
{
if (hit.gameObject.tag == «Pickup»)
{
// call the Collected(…) function of the PickupController Component (script) and
// pass the pickup we hit as the parameter for the function
pickupController.Collected(hit.gameObject);
}
}
|
Так что у нас здесь?
Функция Awake () получает GameObject PickupSpawnPoints со сцены, используя GameObject.Find (…), и сохраняет возвращенный GameObject в переменную.
Компонент PickupController компонента GameObject PickupSpawnPoints затем извлекается с помощью GameObject.GetComponent (…) и присваивается переменной pickupController.
Почему мы получаем компонент PickupController? Давайте посмотрим на функцию OnControllerColliderHit (…) и попробуем понять, что происходит …
Функция OnControllerColliderHit (…) запускается каждый кадр, в котором коллайдер CharacterController игрока сталкивается с RigidBody или другим коллайдером. Так как у нашего пикапа есть BoxCollider, обнаруживается столкновение между ним и игроком, и запускается эта функция.
Функция делает информацию о столкновении доступной нам через ее параметр типа данных ControllerColliderHit . Мы можем использовать это, чтобы обнаружить, с чем столкнулся GameObject.
Чтобы определить, столкнулись ли мы с захватом, мы используем оператор if, чтобы проверить, помечен ли объект GameObject как «Подбор». Наш сборочный пикап в настоящее время не помечен, но мы скоро это исправим.
Если столкнувшийся с GameObject тег помечен как «Pickup», то мы вызываем функцию Collected (…) PickupController, передавая объект GameObject, с которым мы столкнулись, в качестве параметра функций.
Однако с этим также есть проблема: у скрипта PickupController еще нет функции Collected (…), поэтому давайте пойдем и добавим ее сейчас, прежде чем применять тег к нашему префабу раскладки.
Добавление функции Collected (…)
Откройте PickupController и добавьте следующий код в конец скрипта:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
function Collected(pickupCollected:GameObject)
{
// retrieve name of the collected pickup and cast to int
var index:int = parseInt(pickupCollected.name);
// pickup has been destroyed so make the spawn index available again
spawnIndexAvailableList[index] = true;
// destroy the pickup
Destroy(pickupCollected);
// spawn a new pickup
SpawnPickup();
}
|
Таким образом, функция Collected (…) извлекает имя собранного нами звукоснимателя и затем снова делает доступным точку появления, на которой она расположена. Затем он вызывает функцию Destroy (…) для уничтожения пикапа со сцены. Наконец, он затем вызывает новый звукосниматель, вызывая функцию SpawnPickup ().
Итак, чтобы быстро пробежаться по потоку событий, чтобы было ясно, что происходит:
- Игрок сталкивается с пикапом, который будет помечен как «Пикап».
- Вызывается функция Collect (…) функции PickupController, которая передает столкновение, с которым столкнулись.
- Пикап уничтожен, а новый вызывается вызовом SpawnPickup ()
Теперь давайте отметим наш пикап, чтобы все это заработало!
Пометка Пикапа
Итак, вы помните, как мы делали это раньше с тегом SpawnPoint? Это точно так же!
Выберите сборку-сборку в окне проекта и в верхней части инспектора выберите «Добавить тег» из выпадающего списка «Тег». В Диспетчере тегов щелкните внутри элемента 1 и введите «Пикап». Это тег создан.
Примените тег, повторно выбрав сборный пикап и выбрав тег «Пикап» в раскрывающемся списке «Тег».
Если вы снова запустите игру, то при столкновении с пикапом она исчезнет, а затем возродится. Давайте теперь также введем случайную задержку между игроком, собирающим пикап и его возрождением.
Шаг 9: Возобновление после случайной задержки
Это достаточно простое и быстрое дополнение к нашему скрипту благодаря встроенной функции в Unity!
Откройте скрипт PickupController и добавьте следующие две переменные в начало скрипта:
1
2
3
|
// minimum and maximum spawn delay times
public var minimumSpawnDelayTime:int = 1;
public var maximumSpawnDelayTime:int = 5;
|
Чтобы создать задержку в Unity, мы можем использовать yield WaitForSeconds (…), который будет задерживать код, следующий за ним, на количество секунд, указанное в параметре. Мы можем использовать комбинацию этого и Random.Range (…), чтобы заставить наш скрипт ждать случайное количество времени перед повторным вызовом следующего захвата.
Добавьте следующую строку над вызовом SpawnPickup () в функции Collected (…):
1
2
|
// wait for a random amount of seconds between minimumSpawnDelayTime and maximumSpawnDelayTime
yield WaitForSeconds(Random.Range(minimumSpawnDelayTime, maximumSpawnDelayTime));
|
Когда вы сейчас запустите игру и соберете звукосниматель, произойдет случайная задержка, прежде чем звукосниматель возродится, потрясающе!
Вывод
Итак, теперь у вас есть базовые знания UnityScript и коллайдеров! Довольно хорошо подходит для вашего второго урока в Unity! Это просто показывает, насколько удивительной и доступной является эта программа!
В следующем уроке мы представим выигрыш с более сложным игровым процессом с множеством различных пикапов для сбора, некоторые из которых мешают игроку, а некоторые обеспечивают повышение скорости и т. Д. Мы также введем таймер обратного отсчета в комплекте с HUD, отображающим как счет, так и времени осталось!
Мы также добавим небольшую анимацию в наши пикапы и представим всем любимое: частицы!