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


