Статьи

Создайте игру дополненной реальности Pokémon GO Style с помощью Vuforia: Image Targets

В этом уроке мы вернемся к библиотеке Vuforia Augmented Reality (AR), исследуя один из ее самых интересных ресурсов — Image Target . Мы расширим игру Shoot the Cubes, которую мы создали на предыдущих уроках, добавив новый уровень, где игрок должен защищать свою базу от атакующих кубов.

Этот учебник может быть выполнен в одиночку, хотя, если вы хотите познакомиться с AR с помощью Vuforia и Unity3D, ознакомьтесь с предыдущими статьями серии.

  • Мобильная разработка
    Покемон GO Стиль дополненной реальности с Vuforia
    Жестяная мегали
  • Мобильная разработка
    Создайте игру дополненной реальности Pokémon GO Style с Vuforia
    Жестяная мегали

Любое изображение может быть Vuforia Image Target . Однако чем детальнее и сложнее изображение, тем лучше оно будет распознаваться алгоритмом.

Многие факторы будут частью распознавания вычислений, но в основном изображение должно иметь разумный уровень контрастности, разрешения и отличительных элементов. Фотография голубого неба не сработает очень хорошо, но картинка с травой будет работать изящно. Целевые изображения могут быть отправлены вместе с приложением, загружены в приложение через облако или непосредственно созданы пользователем в приложении.

Давайте начнем с добавления элемента ImageTarget в наш проект Unity.

Сначала загрузите материалы курса с кнопки на боковой панели. Затем в своем проекте Unity создайте новую сцену с именем DefendTheBase : в окне « Проект» выберите папку « Сцены » и нажмите « Создать» > « Сцена». Теперь откройте эту сцену и удалите все объекты сцены по умолчанию из иерархии.

Далее мы добавим свет и камеру. Нажмите Add > Light > Directional Light, чтобы добавить направленный свет. Выберите этот новый источник света и установите « Мягкую тень» в качестве параметра « Тип тени» .

После этого перетащите объект ARCamera из Vuforia > Prefabs . Выберите объект ARCamera и на панели инспектора установите лицензионный ключ приложения, созданный на странице разработчика Vuforia ( инструкции см. В первом руководстве ). Выберите DEVICE_TRACKING для World Center Mod .

Наконец, перетащите ImageTarget в иерархию от Vuforia > Prefabs .

Теперь мы должны добавить базу данных Vuforia. Сначала перейдите на https://developer.vuforia.com/target-manager . Нажмите на Добавить базу данных и выберите имя.

Есть три типа базы данных на выбор:

  1. Устройство : база данных сохраняется на устройстве, и все цели обновляются локально.
  2. Облако : База данных на серверах Vuforia.
  3. VuMark : База данных, эксклюзивная для целей VuMark. Он также сохраняется на устройстве.

В этом случае выберите опцию « Устройство» и нажмите « Создать» .

Выберите новую базу данных, чтобы мы могли начать добавлять к ней цели. Теперь пришло время добавить цели в базу данных. Сейчас мы просто будем использовать опцию Single Image .

Перейдите к ранее загруженным файлам, выберите ImageTarget1 , установите его ширину 1 и нажмите « Добавить» . (Примечание. Если вы предпочитаете создавать собственную цель изображения, сначала прочтите руководство .)

База данных Vuforia

Теперь вы можете скачать базу данных, выбрав Unity Editor в качестве выбранной платформы. Откройте файл и выберите все элементы для импорта. Мы также должны подготовить нашу сцену Unity для распознавания ImageTarget с этой базой данных, которую мы создали.

В редакторе Unity нажмите на объект I mageTarget . Сначала найдите и разверните Image Target Behavior в инспекторе объектов. Выберите тип предопределенного . Выберите цель изображения, которую мы создали ранее для базы данных . Наконец, убедитесь, что опции Enable Extended Tracking и Enable Smart Terrain отключены.

Настройки целевого изображения

Префаб ImageTarget состоит из ряда компонентов, включая некоторые сценарии, такие как Image Target Behavior, T urn Off Behavior и обработчик событий по умолчанию для Tracker . Если вы хотите глубоко понять, как работает система, прочитайте эти сценарии и попытайтесь понять их связь с другими компонентами.

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

Создайте копию этого скрипта, которую мы можем расширить. Сначала выберите Обработчик событий по умолчанию , нажмите на опции и выберите Редактировать скрипт . Теперь сделайте копию сценария. Если вы используете MonoDevelop, нажмите « Файл» > « Сохранить как» и сохраните как ImageTargetBehavior , сохранив его в папке « Сценарии ».

Нам не понадобится пространство имен Vuforia в нашем скрипте. Уберите строку « namespace Vuforia » и скобки. Это означает, что нам нужно явно ссылаться на пространство имен Vuforia когда мы хотим получить доступ к его классам:

1
2
3
4
5
6
using UnityEngine;
using System.Collections;
public class BaseScript : MonoBehaviour, Vuforia.ITrackableEventHandler
{
// code here
}

Наиболее важным методом в этом классе будет метод OnTrackableStateChanged который принимает вызовы, когда целевое изображение найдено или потеряно устройством камеры. В соответствии с целевым статусом он вызывает OnTrackingFound или OnTrackingLost , и нам нужно будет также отредактировать эти методы. Но сначала давайте подумаем о том, как мы хотим, чтобы цель изображения работала.

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

  • Как только цель распознается системой, появляется база, и враги начинают появляться и летят к базе в стиле камикадзе.
  • Каждый раз, когда враг поражает базу, база будет получать некоторый урон, и враг будет уничтожен.
  • Чтобы выиграть игру, пользователь должен стрелять и уничтожать всех врагов, прежде чем база будет уничтожена.
  • Если цель изображения потеряна (больше не видна с камеры устройства), игра запустит таймер обратного отсчета. Если таймер обнуляется, игра проиграна. Пока цель потеряна, все враги перестанут продвигаться к базе.

Поэтому нам нужно адаптировать игровую механику к тому, что мы создали в предыдущем уроке. В следующем разделе мы создадим логику появления врага с пустым объектом с именем _SpawnController , используя ту же логику, что и в первой части игры.

А пока давайте посмотрим на логику отслеживания найденных.

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
private void OnTrackingFound ()
{
    EnableRendererAndCollider ();
    // Inform the system that the target was found
    StartCoroutine (InformSpawnCtr (true));
}
 
private void OnTrackingLost ()
{
    DisableRendererAndCollider ();
    // Inform the system that the target was lost
    StartCoroutine (InformSpawnCtr (false));
}
 
// inform SpanController that base was founded
private IEnumerator InformSpawnCtr (bool isOn)
{
    // move spawning position
    GameObject spawn = GameObject.FindGameObjectWithTag («_SpawnController»);
 
    yield return new WaitForSeconds (0.2f);
 
    // inform SpanController
    if (isOn) {
        spawn.GetComponent<SpawnScript2> ().BaseOn (transform.position);
    } else {
        spawn.GetComponent<SpawnScript2> ().BaseOff ();
    }
}

Вернувшись в редактор Unity, мы можем создать базовый объект, который будет порождаться контроллером spawn.

Сначала на объекте ImageTarget отключите скрипт обработчика событий по умолчанию .

Затем нажмите « Добавить компонент» и выберите целевой сценарий поведения . На панели « Иерархия» щелкните правой кнопкой мыши ImageTarget и создайте новый куб с именем « Base ». Этот куб должен быть вставлен в объект ImageTarget .

Убедитесь, что на базе включены Box Collider и Mesh Renderer .

При желании вы также можете вставить объект Plane в ImageTarget, используя ImageTarget, ранее представленный в Vuforia, в качестве текстуры. Это создаст интересный эффект, проецируя тени от цели и создавая более богатый опыт.

Окончательные настройки иерархии и целевого изображения

Теперь мы адаптируем _SpawnController, использованный в последнем уроке. Сохраните текущую сцену и откройте ShootTheCubesMain из последнего урока. На панели « Иерархия» выберите _SpawnController и перетащите его в папку « Prefabs », чтобы сделать его « Unity Prefab» .

Сохраните эту новую сцену и снова откройте DefendTheBase . Перетащите _SpawnController из папки prefabs на панель « Иерархия» . Выбрав _SpawnController , нажмите « Добавить тег» на панели « Инспектор» . Назовите новый тег _SpawnController и примените его к объекту.

В окне «Проект» выберите элемент « Куб» в папке « Prefab » и установите для его тега « Инспектор» значение «Враг».

Установить тег Кубики враг

Наконец, откройте папку Scripts и откройте SpawnScript . Нам нужно, чтобы этот скрипт адаптировался к загруженной сцене.

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
54
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
using System.Collections.Generic;
 
using Vuforia;
 
public class SpawnScript : MonoBehaviour
{
 
    #region VARIABLES
    private bool mSpawningStarted = false;
 
    // Cube element to spawn
    public GameObject mCubeObj;
    // Qtd of Cubes to be Spawned
    public int mTotalCubes = 10;
 
    private int mCurrentCubes = 0;
 
    // Time to spawn the Cubes
    public float mTimeToSpawn = 1f;
     
    private int mDistanceFromBase = 5;
 
    private List<GameObject> mCubes;
 
    private bool mIsBaseOn;
    private Scene mScene;
 
    #endregion // VARIABLES
 
 
    #region UNITY_METHODS
 
    // Use this for initialization
    void Start ()
    {
        mScene = SceneManager.GetActiveScene();
        mCubes = new List<GameObject> ();
 
        if ( mScene.name == «ShootTheCubesMain» )
        {
            StartSpawn();
        }
    }
 
    // Update is called once per frame
    void Update ()
    {
 
    }
 
    #endregion // UNITY_METHODS

Далее нам нужно создать два открытых метода для приема вызовов от TargetBehaviorScript когда цель найдена или потеряна:

  • BaseOn (Vector3 basePosition) , когда цель обнаруживается камерой и отображается объект Base . Он изменит позицию нереста, запустит процесс и проинформирует все кубы, которые были ранее добавлены на сцену, о том, что база видна.

  • Метод BaseOff() будет использоваться, когда цель потеряна. Это остановит процесс постановки и сообщит всем элементам куба, что база была потеряна.

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
#region PUBLIC_METHODS
 
   // Base was found by the tracker
   public void BaseOn (Vector3 basePosition)
   {
       Debug.Log («SpawnScript2: BaseOn»);
 
       mIsBaseOn = true;
 
       // change position
       SetPosition (basePosition);
 
       // start spawning process if necessary
       StartSpawn ();
 
       // inform all cubes on screen that base appeared
       InformBaseOnToCubes ();
   }
 
   // Base lost by the tracker
   public void BaseOff ()
   {
       mIsBaseOn = false;
       mSpawningStarted = false;
 
       // inform all cubes on screen that base is lost
       InformBaseOffToCubes ();
   }
 
   #endregion // PUBLIC_METHODS

SetPosition (System.Nullable<Vector3> pos) использует текущую позицию цели для изменения осей x, y и z объекта, а также может получить null значение, когда загруженная сцена — ShootTheCubesMain .

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
#region PRIVATE_METHODS
 
   // We’ll use a Coroutine to give a little
   // delay before setting the position
   private IEnumerator ChangePosition ()
   {
       Debug.Log («ChangePosition»);
       yield return new WaitForSeconds (0.2f);
       // Define the Spawn position only once
 
       // change the position only if Vuforia is active
       if (VuforiaBehaviour.Instance.enabled)
           SetPosition (null);
        
   }
 
   // Set position
   private void SetPosition (System.Nullable<Vector3> pos)
   {
       if (mScene.name == «ShootTheCubesMain») {
           // get the camera position
           Transform cam = Camera.main.transform;
 
           // set the position 10 units ahead of the camera position
           transform.position = cam.forward * 10;
       } else if (mScene.name == «DefendTheBase») {
           if (pos != null) {
               Vector3 basePosition = (Vector3)pos;
               transform.position =
                   new Vector3 (basePosition.x, basePosition.y + mDistanceFromBase, basePosition.z);
           }
       }
 
   }

InformBaseOnToCubes() и InformBaseOffToCubes() отвечают за информирование всех поэтапных кубов о текущем базовом состоянии.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
// Inform all spawned cubes of the base position
   private void InformBaseOnToCubes ()
   {
       // Debug.Log(«InformBaseOnToCubes»);
       foreach (GameObject cube in mCubes) {
           cube.GetComponent<CubeBehaviorScript> ().SwitchBaseStatus (mIsBaseOn);
       }
   }
 
   // Inform to all cubes that the base is off
   private void InformBaseOffToCubes ()
   {
       // Debug.Log(«InformBaseOffToCubes»);
       foreach (GameObject cube in mCubes) {
           cube.GetComponent<CubeBehaviorScript> ().SwitchBaseStatus (mIsBaseOn);
       }
   }

SpawnLoop() и SpawnElement() используют почти ту же логику, что и в предыдущем уроке.

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
// Start spawning process
   private void StartSpawn ()
   {
       if (!mSpawningStarted) {
           // begin spawn
           mSpawningStarted = true;
           StartCoroutine (SpawnLoop ());
       }
   }
 
   // Loop Spawning cube elements
   private IEnumerator SpawnLoop ()
   {
       if (mScene.name == «ShootTheCubesMain») {
           // Defining the Spawning Position
           StartCoroutine (ChangePosition ());
       }
 
       yield return new WaitForSeconds (0.2f);
       // Spawning the elements
       while (mCurrentCubes <= (mTotalCubes — 1)) {
           // Start the process with different conditions
           // depending on the current stage name
           if (mScene.name == «ShootTheCubesMain» ||
               (mScene.name == «DefendTheBase» && mIsBaseOn)) {
 
               mCubes.Add (SpawnElement ());
               mCubes [mCurrentCubes].GetComponent<CubeBehaviorScript> ().SwitchBaseStatus (mIsBaseOn);
               mCurrentCubes++;
 
           }
 
           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;
   }
 
   #endregion // PRIVATE_METHODS

Теперь нам нужно создать несколько врагов. Мы будем использовать объект Cube, который мы создали в последнем уроке, внося некоторые изменения в его скрипт.

В папке Prefabs добавьте объект Cube в иерархию. Затем выберите объект и отредактируйте 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
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
 
public class CubeBehaviorScript : MonoBehaviour {
 
    #region VARIABLES
 
    public float mScaleMax = 1f;
    public float mScaleMin = 0.2f;
 
    public int mCubeHealth = 100;
 
    // Orbit max Speed
    public float mOrbitMaxSpeed = 30f;
 
    public float velocityToBase = 0.4f;
    public int damage = 10;
 
    // Orbit speed
    private float mOrbitSpeed;
 
    // Orbit direction
    private Vector3 mOrbitDirection;
 
    // Max Cube Scale
    private Vector3 mCubeMaxScale;
 
    // Growing Speed
    public float mGrowingSpeed = 10f;
    private bool mIsCubeScaled = false;
 
    private bool mIsAlive = true;
    private AudioSource mExplosionFx;
 
    private GameObject mBase;
    private bool mIsBaseVisible = false;
 
    private Vector3 mRotationDirection;
    private Scene mScene;
 
    #endregion

Если сцена называется DefendTheBase , она должна найти объект Base и начать двигаться к нему.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
#region UNITY_METHODS
 
   void Start () {
       // Get Scene name
       mScene = SceneManager.GetActiveScene();
       CubeSettings();
   }
 
   void Update () {
       // makes the cube orbit and rotate
       RotateCube();
 
       if ( mScene.name == «DefendTheBase» ) {
           // move cube towards the base, when it’s visible
           MoveToBase ();
       }
 
       // scale cube if needed
       if ( !mIsCubeScaled )
           ScaleObj();
   }
   #endregion

CubeSettings() также необходимо адаптировать в соответствии с загруженной сценой. Куб вращается только по оси Y для сцены DefendTheBase .

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
#region PRIVATE_METHODS
   private void CubeSettings ()
   {
       // defining the orbit direction
       float x = Random.Range ( -1f, 1f );
       float y = Random.Range (-1f, 1f);
       float z = Random.Range ( -1f, 1f );
 
       // TODO update tutorial with new code
       // define settings according to scene name
       if ( mScene.name == «ShootTheCubesMain» )
       {
           mOrbitDirection = new Vector3( x, y, z );
       }
       else if ( mScene.name == «DefendTheBase» )
       {
           // orbit only on y axis
           mOrbitDirection = new Vector3 (0, y, 0);
 
           // scale size must be limited
           mScaleMin = 0.05f;
           mScaleMax = 0.2f;
 
           velocityToBase = 0.2f;
       }
 
       // rotating around its axis
       float rx = Random.Range (-1f, 1f);
       float ry = Random.Range (-1f, 1f);
       float rz = Random.Range (-1f, 1f);
 
       mRotationDirection = new Vector3 (rx, ry, rz);
 
 
       // 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 later
       transform.localScale = Vector3.zero;
 
       // getting Explosion Sound Effect
       mExplosionFx = GetComponent<AudioSource> ();
   }

Мы добавим новую логику в метод RotateCube() . Объекты куба будут вращаться вокруг базы, пока цель видна. Когда цель не видна, они будут продолжать вращаться вокруг камеры , используя ту же логику, что и в предыдущем уроке.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Rotate the cube around the base
   private void RotateCube ()
   {
       // rotate around base or camera
       if (mIsBaseVisible && mBase != null && mIsAlive) {
           // rotate cube around base
           transform.RotateAround (
               mBase.transform.position, mOrbitDirection, mOrbitSpeed * Time.deltaTime);
       } else {
           transform.RotateAround (
               Camera.main.transform.position, mOrbitDirection, mOrbitSpeed * Time.deltaTime);
       }
       transform.Rotate (mRotationDirection * 100 * Time.deltaTime);
   }
   // 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;
   }

Чтобы переместить объект к базе, нам нужно сначала проверить, присутствует ли база, а затем применить шаги позиционирования к объекту.

1
2
3
4
5
6
7
8
9
// Move the cube toward the base
   private void MoveToBase ()
   {
       // make the cube move towards the base only if base is present
       if (mIsBaseVisible && mIsAlive && gameObject != null && mBase != null) {
           float step = velocityToBase * Time.deltaTime;
           transform.position = Vector3.MoveTowards (transform.position, mBase.transform.position, step);
       }
   }

Метод DestroyCube() такой же, как и раньше, но теперь мы добавим новый метод — метод TargetHit(GameObject) который будет вызываться при ударе по базе. Обратите внимание, что BaseHealthScript, указанный в TargetHit() еще не создан.

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
// make a damage on target
   private void TargetHit (GameObject target)
   {
       Debug.Log («TargetHit: » + target.name);
       if (target.name == «Base») {
           // make damage on base
           MyBase baseCtr = target.GetComponent<MyBase> ();
           baseCtr.TakeHit (damage);
           StartCoroutine (DestroyCube ());
       }
   }
 
   // Destroy Cube
   private IEnumerator DestroyCube(){
       mIsAlive = false;
 
       mExplosionFx.Play();
 
       GetComponent<Renderer>().enabled = false;
 
       yield return new WaitForSeconds(mExplosionFx.clip.length);
       Destroy(gameObject);
   }
 
   #endregion

Наконец, мы добавим открытые методы, которые будут вызываться, когда куб получает удар, когда он сталкивается с базой или когда база меняет статус.

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
#region PUBLIC_METHODS
 
   // Cube gor Hit
   // return ‘false’ when cube was destroyed
   public bool Hit( int hitDamage ){
       mCubeHealth -= hitDamage;
       if ( mCubeHealth >= 0 && mIsAlive ) {
           StartCoroutine( DestroyCube());
           return true;
       }
       return false;
   }
 
   public void OnCollisionEnter (Collision col)
   {
       TargetHit (col.gameObject);
   }
 
   // Receive current base status
   public void SwitchBaseStatus (bool isOn)
   {
       // stop the cube on the movement toward base
       mIsBaseVisible = isOn;
       if (isOn) {
           mBase = GameObject.Find («Base»);
       } else {
           mBase = null;
       }
   }
 
   #endregion

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

Давайте начнем добавлять панель здоровья. На панели « Иерархия» в редакторе Unity выберите « Создать» > « Интерфейс» > « Слайдер» . Новый элемент Canvas будет добавлен в иерархию. Он содержит элементы пользовательского интерфейса, в том числе новый слайдер . Разверните холст и выберите ползунок .

Измените имя элемента слайдера на UIHealth . На панели « Инспектор» разверните « Прямоугольное преобразование» и установите « Ширина» на 400 и « Высота» на 40 . Установите Pos X на -220 , Pos Y на 30 и Pos Z на 0 .

Теперь разверните скрипт слайдера в иерархии. Отмените выбор опции Interactable . Для Target Graphic , нажмите на маленькую «точку» справа и выберите фоновое изображение.

  • Установите минимальное значение 0 и максимальное значение 100 .
  • Выберите Целые числа .
  • Установите значение 100 .
Настройки UIHealth

Теперь разверните панель « Слайдер», чтобы отобразить его дочерние элементы: фон , область заливки и область обработки слайдов .

  • Удалить область слайда ручки .
  • Выберите Фон и установите его Цвет на более темный оттенок зеленого, например #12F568FF .
  • Разверните Fill Area и выберите объект Fill и установите его цвет #7FEA89FF .

Вот так должно выглядеть окно игры с индикатором здоровья.

Игровое окно с баром здоровья

Код очень прост; он просто вычитает урон, нанесенный противникам, из общего количества здоровья базы. Как только здоровье становится равным нулю, игрок проигрывает игру. Это также добавит анимацию вращения на базу. Создайте новый C # скрипт с именем MyBase .

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
 
public class MyBase : MonoBehaviour
{
    #region VARIABLE
 
    public float rotationSpeed = 10f;
 
    public int health = 100;
    public AudioClip explosionSoundFx;
    public AudioClip hitSoundFx;
    // TODO choose a different sound for the Hit
 
    private bool mIsAlive = true;
    private AudioSource mAudioSource;
    public Slider mHealthSlider;
 
    #endregion // VARIABLES
 
 
    #region UNITY_METHODS
 
    // Use this for initialization
    void Start ()
    {
        mAudioSource = GetComponent<AudioSource> ();
        mHealthSlider.maxValue = health;
        mHealthSlider.value = health;
    }
     
    // Update is called once per frame
    void Update ()
    {
        RotateBase ();
    }
 
    #endregion // UNITY_REGION
 
    #region PRIVATE_METHODS
 
    private void RotateBase ()
    {
        if ( mIsAlive && gameObject != null ) {
            // implement object rotation
            transform.Rotate ( Vector3.up, rotationSpeed * Time.deltaTime);
        }
    }
 
    // Destroy base
    private IEnumerator DestroyBase ()
    {
        mIsAlive = false;
        mAudioSource.clip = explosionSoundFx;
        mAudioSource.Play ();
 
        GetComponent<Renderer> ().enabled = false;
 
        // inform all Enemies that Base is Lost
        GameObject[] enemies = GameObject.FindGameObjectsWithTag («Enemy»);
        foreach (GameObject e in enemies) {
            e.gameObject.GetComponent<EnemyScript> ().SwitchBaseStatus (false);
        }
 
        yield return new WaitForSeconds (mAudioSource.clip.length);
        Destroy (gameObject);
 
    }
 
    #endregion // PRIVATE_METHODS
 
    #region PUBLIC_METHODS
 
    // receive damage
    public void TakeHit (int damage)
    {
        health -= damage;
 
        mHealthSlider.value = health;
 
        if (health <= 0) {
            StartCoroutine (DestroyBase ());
        } else {
            mAudioSource.clip = hitSoundFx;
            mAudioSource.Play ();
        }
    }
 
    #endregion // PUBLIC_METHODS
}

Теперь нам нужно добавить и настроить скрипт.

Выберите Base в иерархии, нажмите Add Component и добавьте аудиоисточник . Теперь перетащите MyBase на элемент Base и на панели « Инспектор» разверните MyBase . Выберите звуковой эффект для взрыва и нажмите. Я использовал клип для взрыва, использованный в последнем уроке, но не стесняйтесь добавлять свой. Наконец, в Health Slider , выберите элемент UISlider .

Базовые настройки

Наш новый игровой опыт почти закончен. Нам нужно только выстрелить лазерами, чтобы начать защищать нашу базу. Давайте создадим скрипт для лазера!

Сначала перетащите _PlayerController из папки Prefab в иерархию. Разверните _PlayerController и выберите _LaserController . На панели « Инспектор» найдите Laser Script и нажмите « Изменить» .

Единственное, что нам нужно изменить в этом скрипте — это положение лазера.

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
// 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;
 
       // Show the Laser using a Coroutine
       StartCoroutine (LaserFx ());
 
       // Holds the Hit information
       RaycastHit hit;
 
       // Set the origin position of the Laser Line
       // It will add 10 units down from the ARCamera
       // We adopted this logic for simplicity
       Vector3 laserStartPos = new Vector3 (cam.position.x, cam.position.y -2f, cam.position.z);
       mLaserLine.SetPosition (0, laserStartPos);
 
       // 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);
 
           // check target type
           if (hit.collider.tag == «Enemy») {
 
               CubeBehaviorScript cubeCtr = hit.collider.GetComponent<CubeBehaviorScript> ();
               if (cubeCtr != null) {
                   if (hit.rigidbody != null) {
                       hit.rigidbody.AddForce (-hit.normal * mHitForce);
                       cubeCtr.Hit (mLaserDamage);
                   }
               }
           }
 
       } else {
           // Set the enfo of the laser line to be forward the camera
           // using the Laser range
           mLaserLine.SetPosition (1, cam.forward * mFireRange);
       }
   }

Это было много работы, но теперь пришло время играть в игру! Распечатайте целевое изображение и попробуйте запустить игру на своем телефоне или планшете. Повеселитесь и посмотрите, сможете ли вы придумать способы улучшить игру!

На этом этапе вы хорошо понимаете, как работает система Vuforia и как ее использовать с Unity. Я ожидаю, что вам понравилось это путешествие так же, как и мне. До скорого!

Чтобы узнать больше об дополненной реальности с Vuforia и Unity, посмотрите наш видеокурс здесь на Envato Tuts +!

  • Мобильное приложение
    Покемон GO Style Дополненная реальность: маркеры
    Дерек Дженсен