В последнем посте этой серии мы узнали, как настроить Vuforia и начать разработку AR-игры с нуля, применяя логику, аналогичную той, которая используется в Pokémon GO!
-
Создайте игру дополненной реальности Pokémon GO Style с Vuforia
-
Покемон GO Стиль дополненной реальности с Vuforia
-
Покемон GO Style Дополненная реальность: маркеры
Мы начали разработку игры дополненной реальности под названием Shoot the Cubes. Теперь пришло время улучшить игру, добавив взаимодействие и сделав опыт более увлекательным. Мы сконцентрируемся в основном на возможностях, которые нам предоставляет Unity, оставляя в стороне специфику Vuforia. Опыт работы с движком Unity не обязателен.
1. Как кубики выглядят живыми
Давайте начнем работать над нашей игрой. До сих пор нам удалось создать сцену дополненной реальности, которая перемещается вместе с устройством пользователя. Мы улучшим это приложение, заставляя кубы появляться и летать, и позволяя игроку искать и уничтожать их лазерным выстрелом.
1.1. Нерест Стихий
Мы уже установили начальную позицию _SpawnController в соответствии с поворотом камеры устройства. Теперь мы установим область вокруг этой точки, где будут появляться наши кубики. Теперь мы обновим SpawnScript
чтобы _SpawnController создавал экземпляры элементов куба с различными размерами и случайными позициями относительно _SpawnController .
Давайте отредактируем класс SpawnScript
, добавив некоторые переменные для управления процессом порождения.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
public class SpawnScript : MonoBehaviour {
// Cube element to spawn
public GameObject mCubeObj;
// Qtd of Cubes to be Spawned
public int mTotalCubes = 10;
// Time to spawn the Cubes
public float mTimeToSpawn = 1f;
// hold all cubes on stage
private GameObject[] mCubes;
// define if position was set
private bool mPositionSet;
}
|
Мы создадим сопрограмму под названием SpawnLoop
для управления процессом появления. Он также будет ответственен за установку начальной позиции _SpawnController в начале игры. Обратите внимание, что метод Random.insideUnitSphere
вызывает создание экземпляров кубов в случайных местах в сферической области вокруг _SpawnManager .
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
29
30
31
32
|
public class SpawnScript : MonoBehaviour {
// Loop Spawning cube elements
private IEnumerator SpawnLoop()
{
// Defining the Spawning Position
StartCoroutine( ChangePosition() );
yield return new WaitForSeconds(0.2f);
// Spawning the elements
int i = 0;
while ( i <= (mTotalCubes-1) ) {
mCubes[i] = SpawnElement();
i++;
yield return new WaitForSeconds(Random.Range(mTimeToSpawn, mTimeToSpawn*3));
}
}
// Spawn a cube
private GameObject SpawnElement()
{
// spawn the element on a random position, inside a imaginary sphere
GameObject cube = Instantiate(mCubeObj, (Random.insideUnitSphere*4) + transform.position, transform.rotation ) as GameObject;
// define a random scale for the cube
float scale = Random.Range(0.5f, 2f);
// change the cube scale
cube.transform.localScale = new Vector3( scale, scale, scale );
return cube;
}
}
|
Наконец, отредактируйте функцию Start()
. Обязательно удалите StartCoroutine( ChangePosition() );
и замените его вызовом для запуска сопрограммы SpawnLoop
.
01
02
03
04
05
06
07
08
09
10
|
public class SpawnScript : MonoBehaviour {
void Start () {
// Initializing spawning loop
StartCoroutine( SpawnLoop() );
// Initialize Cubes array according to
// the desired quantity
mCubes = new GameObject[ mTotalCubes ];
}
}
|
Теперь вернувшись в Unity, вам нужно будет создать префаб куба, который будет создан сценарием.
- Создайте папку с именем Prefabs в окне проекта .
- Измените масштаб элемента куба по всем осям на 1 и измените вращение на 0 по всем осям.
- Перетащите куб в папку Prefabs .
- Удалите Куб из окна Иерархии .
- Выберите _SpawnController и перетащите сборный куб из папки Prefabs в поле M Cube Obj , расположенное в области
SpawnScript
инспектора.
Теперь, если вы нажмете кнопку воспроизведения в Unity и запустите проект на устройстве, вы должны увидеть появление кубов.
1.2. Вращающиеся кубики
Нам нужно добавить движение к этим кубам, чтобы сделать вещи более интересными. Давайте вращать кубики вокруг их осей и над ARCamera . Было бы также здорово добавить некоторый случайный фактор к движению куба, чтобы создать более органичное ощущение.
Перетащите сборный куб из папки « Сборные » в иерархию.
- В папке Scripts создайте новый скрипт C # с именем CubeBehaviorScript .
- Перетащите сценарий в сборный куб и откройте его для редактирования.
Каждый куб будет иметь случайные характеристики. Размер, скорость вращения и направление вращения будут определены случайным образом, используя некоторые ранее определенные ссылки. Давайте создадим некоторые переменные контроллера и инициализируем состояние куба.
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
using UnityEngine;
using System.Collections;
public class CubeBehaviorScript : MonoBehaviour {
// Cube’s Max/Min scale
public float mScaleMax = 2f;
public float mScaleMin = 0.5f;
// Orbit max Speed
public float mOrbitMaxSpeed = 30f;
// Orbit speed
private float mOrbitSpeed;
// Anchor point for the Cube to rotate around
private Transform mOrbitAnchor;
// Orbit direction
private Vector3 mOrbitDirection;
// Max Cube Scale
private Vector3 mCubeMaxScale;
// Growing Speed
public float mGrowingSpeed = 10f;
private bool mIsCubeScaled = false;
void Start () {
CubeSettings();
}
// Set initial cube settings
private void CubeSettings(){
// defining the anchor point as the main camera
mOrbitAnchor = Camera.main.transform;
// defining the orbit direction
float x = Random.Range(-1f,1f);
float y = Random.Range(-1f,1f);
float z = Random.Range(-1f,1f);
mOrbitDirection = new Vector3( x, y , z );
// defining speed
mOrbitSpeed = Random.Range( 5f, mOrbitMaxSpeed );
// defining scale
float scale = Random.Range(mScaleMin, mScaleMax);
mCubeMaxScale = new Vector3( scale, scale, scale );
// set cube scale to 0, to grow it lates
transform.localScale = Vector3.zero;
}
}
|
Теперь пришло время добавить движение в наш куб. Давайте заставим его вращаться вокруг себя и вокруг ARCamera , используя произвольную скорость и направление, определенные ранее.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
// Update is called once per frame
void Update () {
// makes the cube orbit and rotate
RotateCube();
}
// Makes the cube rotate around a anchor point
// and rotate around its own axis
private void RotateCube(){
// rotate cube around camera
transform.RotateAround(
mOrbitAnchor.position, mOrbitDirection, mOrbitSpeed * Time.deltaTime);
// rotating around its axis
transform.Rotate( mOrbitDirection * 30 * Time.deltaTime);
}
|
Чтобы сделать его более органичным, куб будет увеличиваться с нулевого размера после его появления.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
// Update is called once per frame
void Update () {
// makes the cube orbit and rotate
RotateCube();
// scale cube if needed
if ( !mIsCubeScaled )
ScaleObj();
}
// Scale object from 0 to 1
private void ScaleObj(){
// growing obj
if ( transform.localScale != mCubeMaxScale )
transform.localScale = Vector3.Lerp( transform.localScale, mCubeMaxScale, Time.deltaTime * mGrowingSpeed );
else
mIsCubeScaled = true;
}
|
2. Поиск и уничтожение
Эти кубики слишком счастливы, что летают вот так. Мы должны уничтожить их лазерами! Давайте создадим оружие в нашей игре и начнем стрелять в них.
2.1. Стрельба из лазера
Лазерный выстрел должен быть подключен к ARCamera и его вращению. Каждый раз, когда игрок «стучит» по экрану устройства, будет выпущен лазер. Мы будем использовать класс Physics.Raycast
чтобы проверить, попал ли лазер в цель, и если это так, мы удалим из него немного здоровья.
- Создайте новый пустой объект с именем _PlayerController и еще один пустой объект с именем _LaserController внутри него.
- Добавьте скрипт C # с именем LaserScript в папку Scripts и перетащите его в _LaserController .
Внутри LaserScript мы будем использовать LineRenderer
для отображения лазерного луча, используя точку начала, соединенную с нижней частью ARCamera . Чтобы получить исходную точку лазерного луча — ствол виртуальной пушки — мы получим камеру Transform
в момент, когда лазер выстрелит, и сдвинем его на 10 единиц вниз.
Сначала давайте создадим некоторые переменные для управления настройками лазера и получим mLaserLine
.
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
|
using UnityEngine;
using System.Collections;
public class LaserScript : MonoBehaviour {
public float mFireRate = .5f;
public float mFireRange = 50f;
public float mHitForce = 100f;
public int mLaserDamage = 100;
// Line render that will represent the Laser
private LineRenderer mLaserLine;
// Define if laser line is showing
private bool mLaserLineEnabled;
// Time that the Laser lines shows on screen
private WaitForSeconds mLaserDuration = new WaitForSeconds(0.05f);
// time of the until the next fire
private float mNextFire;
// Use this for initialization
void Start () {
// getting the Line Renderer
mLaserLine = GetComponent<LineRenderer>();
}
}
|
Функция, ответственная за стрельбу — это Fire()
. Это будет вызываться каждый раз, когда игрок нажимает кнопку огня. Обратите внимание, что Camera.main.transform
используется для определения положения и вращения ARCamera и что лазер расположен на 10 единиц ниже этого. Это позиционирует лазер в нижней части камеры.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
// Shot the Laser
private void Fire(){
// Get ARCamera Transform
Transform cam = Camera.main.transform;
// Define the time of the next fire
mNextFire = Time.time + mFireRate;
// Set the origin of the RayCast
Vector3 rayOrigin = cam.position;
// Set the origin position of the Laser Line
// It will always 10 units down from the ARCamera
// We adopted this logic for simplicity
mLaserLine.SetPosition(0, transform.up * -10f );
}
|
Чтобы проверить, была ли цель поражена, мы будем использовать Raycast
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
// Shot the Laser
private void Fire(){
// Hold the Hit information
RaycastHit hit;
// Checks if the RayCast hit something
if ( Physics.Raycast( rayOrigin, cam.forward, out hit, mFireRange )){
// Set the end of the Laser Line to the object hit
mLaserLine.SetPosition(1, hit.point );
} else {
// Set the enfo of the laser line to be forward the camera
// using the Laser range
mLaserLine.SetPosition(1, cam.forward * mFireRange );
}
}
|
Наконец, пришло время проверить, была ли нажата кнопка огня, и вызвать лазерные эффекты при выстреле.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// Update is called once per frame
void Update () {
if ( Input.GetButton(«Fire1») && Time.time > mNextFire ){
Fire();
}
}
private void Fire() {
// anterior code supressed for simplicity
// Show the Laser using a Coroutine
StartCoroutine(LaserFx());
}
// Show the Laser Effects
private IEnumerator LaserFx(){
mLaserLine.enabled = true;
// Way for a specific time to remove the LineRenderer
yield return mLaserDuration;
mLaserLine.enabled = false;
}
|
Вернувшись в Unity, нам нужно добавить компонент LineRenderer в объект _LaserController .
- Выберите _LaserController и в окне инспектора нажмите « Добавить компонент» . Назовите его « Line Renderer » и выберите новый компонент.
- Создайте новую папку с именем Materials и создайте внутри нее новый материал, который называется Laser .
- Выберите материал Laser и измените его на любой понравившийся вам цвет.
- Выберите _LaserController и перетащите материал Laser в поле Материалы компонента LineRenderer .
- Все еще в LineRenderer , в разделе « Параметры» установите « Начать с» на 1 и « Конец с» на 0 .
Если вы тестируете игру сейчас, вы должны увидеть лазерный выстрел из нижней части экрана. Не стесняйтесь добавлять компонент AudioSource с лазерным звуковым эффектом в _LaserController, чтобы оживить его.
2.1. Поразить кубики
Наши лазеры должны поразить цели, нанести урон и в конечном итоге уничтожить кубики. Нам нужно добавить RigidBody к кубам, применяя силу и урон к ним.
- Перетащите сборный кубик из папки prefabs в любое место на сцене .
- Выберите куб и выберите Добавить компонент в окно инспектора . Назовите новый компонент » RigidBody » и выберите его.
- Для компонента RigidBody установите для параметра Гравитация и Is Kinematic значение Выкл .
- Примените эти изменения к сборному.
Теперь давайте отредактируем скрипт CubeBehavior
чтобы создать функцию, отвечающую за нанесение урона кубу, и еще одну за его уничтожение, когда здоровье падает ниже 0.
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
29
30
31
32
33
|
public class CubeBehaviorScript : MonoBehaviour {
// Cube Health
public int mCubeHealth = 100;
// Define if the Cube is Alive
private bool mIsAlive = true;
// Cube got Hit
// return ‘false’ when cube was destroyed
public bool Hit( int hitDamage ){
mCubeHealth -= hitDamage;
if ( mCubeHealth >= 0 && mIsAlive ) {
StartCoroutine( DestroyCube());
return true;
}
return false;
}
// Destroy Cube
private IEnumerator DestroyCube(){
mIsAlive = false;
// Make the cube desappear
GetComponent<Renderer>().enabled = false;
// we’ll wait some time before destroying the element
// this is usefull when using some kind of effect
// like a explosion sound effect.
// in that case we could use the sound lenght as waiting time
yield return new WaitForSeconds(0.5f);
Destroy(gameObject);
}
}
|
Хорошо, теперь куб может получить урон и быть уничтоженным. Давайте отредактируем LaserScript
чтобы применить повреждение к кубу. Все, что нам нужно сделать, это изменить функцию Fire()
для вызова метода CubeBehavior
скрипта CubeBehavior
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class LaserScript : MonoBehaviour {
// Shot the Laser
private void Fire(){
// code supressed for simplicity …
// Checks if the RayCast hit something
if ( Physics.Raycast( rayOrigin, cam.forward, out hit, mFireRange )){
// Set the end of the Laser Line to the object hitted
mLaserLine.SetPosition(1, hit.point );
// Get the CubeBehavior script to apply damage to target
CubeBehaviorScript cubeCtr = hit.collider.GetComponent<CubeBehaviorScript>();
if ( cubeCtr != null ) {
if ( hit.rigidbody != null ) {
// apply force to the target
hit.rigidbody.AddForce(-hit.normal*mHitForce);
// apply damage the target
cubeCtr.Hit( mLaserDamage );
}
}
}
}
|
3. Вывод
Поздравляем! Наша игра дополненной реальности завершена! Да, игра может быть более отточенной, но основы есть, и общий опыт довольно увлекателен. Чтобы сделать его более интересным, вы можете добавить взрыв частиц, как я делал в видео, и, кроме того, вы можете добавить систему очков или даже волновую систему с таймером, чтобы сделать это более сложной задачей. Следующие шаги до вас!
3.1. Что дальше?
Мы создали интересный AR-эксперимент с использованием Vuforia на Unity, однако у нас еще есть много интересных функций, которые нужно охватить. Мы не видели ни одного из более сложных ресурсов Vuforia: Targets, Smart Terrain, Cloud и так далее. Следите за следующими уроками, в которых мы рассмотрим больше этих функций, всегда используя один и тот же пошаговый подход.
До скорого!