Advertisement
  1. Code
  2. Mobile Development

Crea un Juego de Realidad Aumentada del Estilo de Pokémon GO con Vuforia: Parte 2

Scroll to top
Read Time: 14 min
This post is part of a series called Create a Pokémon GO Style Augmented Reality Game With Vuforia.
Create a Pokémon GO Style Augmented Reality Game With Vuforia
Create a Pokémon GO Style Augmented Reality Game With Vuforia: Image Targets

Spanish (Español) translation by Javier Salesi (you can also view the original English article)

En el post anterior de ésta serie, aprendimos cómo configurar Vuforia y comenzamos a desarrollar un juego de RA (Realidad Aumentada) desde cero, adoptando una lógica similar a la utilizada en Pokémon Go!

Hemos comenzado a desarrollar un juego de Realidad Aumentada llamado Shoot the Cubes (Dispárale a los Cubos). Ahora es momento de mejorar el juego al agregar interacción y hacer la experiencia más adictiva. Nos concentraremos principalmente en las posibilidades que nos ofrece Unity, dejando a un lado las especificaciones de Vuforia. Tener experiencia con el motor de Unity no es un requisito.

1. Haciendo que los Cubos cobren Vida

Comencemos a trabajar en nuestro juego. Hasta ahora hemos logrado crear una escena de Realidad Aumentada que se mueve con el dispositivo del usuario. Mejoraremos ésta aplicación al hacer que se generen los cubos y vuelen, y al permitir al jugador buscarlos y destruirlos con un disparo láser.

1.1. Generando los Elementos

Ya hemos establecido una posición inicial del _SpawnController de acuerdo a la rotación de la cámara del dispositivo. Ahora estableceremos una área alrededor de éste punto donde se generarán nuestros cubos. Ahora actualizaremos el SpawnScript para hacer que _SpawnController instancie los elementos cubos con diferentes tamaños y posiciones aleatorias, relativas a _SpawnController.

Editemos la clase SpawnScript, añadiendo algunas variables al control del proceso de generación.

1
public class SpawnScript : MonoBehaviour {
2
    // Cube element to spawn

3
    public GameObject mCubeObj;
4
    
5
    // Qtd of Cubes to be Spawned

6
    public int mTotalCubes      = 10;
7
8
    // Time to spawn the Cubes

9
    public float mTimeToSpawn  = 1f;
10
11
    // hold all cubes on stage

12
    private GameObject[] mCubes;
13
14
    // define if position was set

15
    private bool mPositionSet;
16
}

Crearemos una corrutina llamada SpawnLoop para gestionar el proceso de generación. También será responsable de establecer la posición inicial del _SpawnController cuando el juego inicia. Nota que el método Random.insideUnitSphere causa que los cubos sean instanciados en ubicaciones aleatorias dentro de una área esférica alrededor del _SpawnManager.

1
public class SpawnScript : MonoBehaviour {
2
    
3
    // Loop Spawning cube elements

4
    private IEnumerator SpawnLoop() 
5
	{
6
		// Defining the Spawning Position

7
		StartCoroutine( ChangePosition() );
8
9
		yield return new WaitForSeconds(0.2f);
10
11
		// Spawning the elements

12
		int i = 0;
13
		while ( i <= (mTotalCubes-1) ) {
14
15
			mCubes[i] = SpawnElement();
16
			i++;
17
			yield return new WaitForSeconds(Random.Range(mTimeToSpawn, mTimeToSpawn*3));
18
		}
19
	}
20
21
	// Spawn a cube

22
	private GameObject SpawnElement() 
23
	{
24
		// spawn the element on a random position, inside a imaginary sphere

25
		GameObject cube = Instantiate(mCubeObj, (Random.insideUnitSphere*4) + transform.position, transform.rotation ) as GameObject;
26
		// define a random scale for the cube

27
		float scale = Random.Range(0.5f, 2f);
28
		// change the cube scale

29
		cube.transform.localScale = new Vector3( scale, scale, scale );
30
		return cube;
31
	}
32
}

Finalmente, editamos la función Start(). Asegúrate de remover la línea de StartCoroutine(ChangePosition() ); y remplazarla con una llamada para iniciar la corrutina SpawnLoop.

1
public class SpawnScript : MonoBehaviour {
2
    void Start () {
3
	    // Initializing spawning loop

4
	    StartCoroutine( SpawnLoop() );
5
6
	    // Initialize Cubes array according to

7
	    // the desired quantity

8
	    mCubes = new GameObject[ mTotalCubes ];
9
    }
10
}

Ahora de regreso en Unity tendrás que crear un prefab cube (cubo) para ser instanciado por el script.

  • Crea un directorio llamado Prefabs en la ventana Project.
  • Cambia la escala del elemento Cube (cubo) en todos los ejes a 1 y cambia la rotación a 0 en todos los ejes.
  • Arrastra el Cube al directorio Prefabs.
  • Borra el Cube (cubo) de la ventana Hierarchy.
  • Selecciona el _SpawnController y arrastra el prefab Cube desde el directorio Prefabs al campo M Cube Obj, localizado en el área SpawnScript del inspector.


Finalmente, borra la Sphere (esfera) localizada dentro del _SpawnManager.

Ahora, si presionas reproducir en Unity y ejecutas el proyecto en el dispositivo, deberías ver los cubos generándose.

1.2. Rotando los Cubos

Necesitamos agregar movimiento a esos cubos para hacer las cosas más interesantes. Rotemos los cubos alrededor de sus ejes y sobre el ARCamera. También sería magnífico agregar algún factor aleatorio sobre el movimiento del cubo para crear una sensación más orgánica.

Arrastra el Prefab Cube desde el directorio Prefabs a la ventana hierarchy (jerarquía).

  • En el directorio Scripts crea un nuevo C# Script llamado CubeBehaviorScript.
  • Arrastra el Script al prefab Cube y ábrelo para editarlo.

Cada cubo tendrá características aleatorias. El tamaño, velocidad de rotación y dirección de rotación serán definidas aleatoriamente, usando algunas referencias previamente establecidas. Creemos algunas variables del controlador e inicialicemos el estado del cubo.

1
using UnityEngine;
2
using System.Collections;
3
4
public class CubeBehaviorScript : MonoBehaviour {
5
    // Cube's Max/Min scale

6
    public float mScaleMax  = 2f;
7
    public float mScaleMin  = 0.5f;
8
9
    // Orbit max Speed

10
    public float mOrbitMaxSpeed = 30f;
11
12
    // Orbit speed

13
    private float mOrbitSpeed;
14
15
    // Anchor point for the Cube to rotate around

16
    private Transform mOrbitAnchor;
17
18
    // Orbit direction

19
    private Vector3 mOrbitDirection;
20
21
    // Max Cube Scale

22
    private Vector3 mCubeMaxScale;
23
    
24
    // Growing Speed

25
    public float mGrowingSpeed	= 10f;
26
    private bool mIsCubeScaled	= false;
27
28
    void Start () {
29
        CubeSettings();
30
    }
31
32
    // Set initial cube settings

33
    private void CubeSettings(){
34
        // defining the anchor point as the main camera

35
        mOrbitAnchor = Camera.main.transform;
36
37
        // defining the orbit direction

38
        float x = Random.Range(-1f,1f);
39
        float y = Random.Range(-1f,1f);
40
        float z = Random.Range(-1f,1f);
41
        mOrbitDirection = new Vector3( x, y , z );
42
43
        // defining speed

44
        mOrbitSpeed = Random.Range( 5f, mOrbitMaxSpeed );
45
46
        // defining scale

47
        float scale = Random.Range(mScaleMin, mScaleMax);
48
        mCubeMaxScale = new Vector3( scale, scale, scale );
49
50
        // set cube scale to 0, to grow it lates

51
        transform.localScale = Vector3.zero;
52
    }
53
}

Ahora es momento de añadir algo de movimiento a nuestro cubo. Hagámoslo rotar alrededor de sí mismo y alrededor del ARCamera, utilizando la velocidad y la dirección de la rotación definida antes.

1
    // Update is called once per frame

2
    void Update () {
3
		// makes the cube orbit and rotate

4
		RotateCube();
5
	}
6
    
7
    // Makes the cube rotate around a anchor point

8
    // and rotate around its own axis

9
	private void RotateCube(){
10
		// rotate cube around camera

11
		transform.RotateAround(
12
			mOrbitAnchor.position, mOrbitDirection, mOrbitSpeed * Time.deltaTime);
13
14
		// rotating around its axis

15
		transform.Rotate( mOrbitDirection * 30 * Time.deltaTime);
16
	}

Para hacerlo más orgánico, el cubo se escalará del tamaño cero después de que es generado.

1
    // Update is called once per frame

2
    void Update () {
3
		// makes the cube orbit and rotate

4
		RotateCube();
5
6
		// scale cube if needed

7
		if ( !mIsCubeScaled )
8
			ScaleObj();
9
	}
10
    
11
    // Scale object from 0 to 1

12
    private void ScaleObj(){
13
14
		// growing obj

15
		if ( transform.localScale != mCubeMaxScale )
16
			transform.localScale = Vector3.Lerp( transform.localScale, mCubeMaxScale, Time.deltaTime * mGrowingSpeed );
17
		else
18
			mIsCubeScaled = true;
19
	}

2. Bucando y Destruyendo

Esos cubos son demasiado felices volando de esa manera. ¡Debemos destruirlos con disparos láser! Creemos un arma en nuestro juego y comencemos a dispararles.

2.1. Disparando un Láser

El disparo del láser debe estar conectado al ARCamera y a su rotación. Cada vez que el jugador 'presiona' la pantalla del dispositivo, un láser será disparado. Usaremos la clase Physics.Raycast para verificar si el láser impactó el objetivo y si fue así mermaremos su salud.

  • Creemos un nuevo objeto vacío llamado _PlayerController y otro objeto vacío llamado _LaserController dentro de él.
  • Agreguemos un C# script llamado LaserScript dentro del directorio Scripts y lo arrastramos a _LaserController.

Dentro de LaserScript, usaremos un LineRenderer para mostrar el rayo láser, usando un punto de origen conectado a la parte inferior del ARCamera. Para obtener el punto de origen del rayo láser_el cañón del arma virtual-obtendremos el Transform de la cámara en el momento cuando un láser es disparado y lo mueve 10 unidades hacia abajo.

Primero, creemos algunas variables para controlar los ajustes del láser y obtener mLaserLine.

1
using UnityEngine;
2
using System.Collections;
3
4
public class LaserScript : MonoBehaviour {
5
6
    public float mFireRate  = .5f;
7
    public float mFireRange = 50f;
8
    public float mHitForce  = 100f;
9
    public int mLaserDamage = 100;
10
11
    // Line render that will represent the Laser

12
    private LineRenderer mLaserLine;
13
14
    // Define if laser line is showing

15
    private bool mLaserLineEnabled;
16
17
    // Time that the Laser lines shows on screen

18
    private WaitForSeconds mLaserDuration = new WaitForSeconds(0.05f);
19
20
    // time of the until the next fire

21
    private float mNextFire;
22
    
23
    // Use this for initialization

24
    void Start () {
25
		// getting the Line Renderer

26
		mLaserLine = GetComponent<LineRenderer>();
27
	}
28
}

La función responsable para disparar es Fire() . Esa será llamada cada vez que el jugador presione el botón fire. Nota que Camera.main.transform está siendo usado para obtener la posición y la rotación de ARCamera y que el láser esté posicionado 10 unidades abajo de ella. Ésto posiciona al láser en la parte inferior de la cámara.

1
// Shot the Laser

2
    private void Fire(){
3
        // Get ARCamera Transform

4
		Transform cam = Camera.main.transform;
5
6
		// Define the time of the next fire

7
		mNextFire = Time.time + mFireRate;
8
9
		// Set the origin of the RayCast

10
		Vector3 rayOrigin = cam.position;
11
12
		// Set the origin position of the Laser Line

13
		// It will always 10 units down from the ARCamera

14
		// We adopted this logic for simplicity

15
		mLaserLine.SetPosition(0, transform.up * -10f );
16
}

Pare verificar si el objetivo fue impactado, utilizaremos un Raycast.

1
// Shot the Laser

2
    private void Fire(){
3
        // Hold the Hit information

4
    	RaycastHit hit;
5
6
		// Checks if the RayCast hit something

7
		if ( Physics.Raycast( rayOrigin, cam.forward, out hit, mFireRange )){
8
9
			// Set the end of the Laser Line to the object hit

10
			mLaserLine.SetPosition(1, hit.point );
11
12
		} else {
13
			// Set the enfo of the laser line to be forward the camera

14
			// using the Laser range

15
			mLaserLine.SetPosition(1, cam.forward * mFireRange );
16
		}
17
}

Por último, es tiempo de verificar si el botón de disparar fue presionado y llama a los efectos del láser cuando el disparo es ejecutado.

1
// Update is called once per frame

2
void Update () {
3
    if ( Input.GetButton("Fire1") && Time.time > mNextFire ){
4
        Fire();
5
        }    
6
    }
7
8
private void Fire() {
9
    // anterior code supressed for simplicity

10
    
11
    // Show the Laser using a Coroutine

12
    StartCoroutine(LaserFx());
13
}
14
15
// Show the Laser Effects

16
private IEnumerator LaserFx(){
17
    mLaserLine.enabled = true;
18
19
    // Way for a specific time to remove the LineRenderer

20
    yield return mLaserDuration;
21
    mLaserLine.enabled = false;
22
}

De regreso en Unity necesitaremos añadir un componente LineRenderer al objeto _LaserController.

  • Selecciona _LaserController y en la ventana Inspector, haz click en Add Component. Nómbralo "Line Renderer" y selecciona el nuevo componente.
  • Crea un nuevo directorio llamado Materials, y crea un nuevo material dentro de él llamado Laser.
  • Selecciona el material Laser y cámbialo a cualquier color que desees.
  • Selecciona el _LaserController y arrastra el material Laser al campo Materials del componente LineRenderer.
  • Aún en LineRenderer, bajo Parameters establece Start With en 1 y End With en 0.

Si pruebas el juego ahora deberías ver un láser siendo disparado desde la parte inferior de la pantalla. Si gustas agrega un componente AudioSource con un efecto de soido láser a _LaserController para aderezarlo.

2.1. Impactando los Cubos

Nuestros láseres necesitan impactar sus objetivos, infligir daño y eventualmente destruir los cubos. Necesitaremos agregar un RigidBody a los cubos, aplicar fuerza y daño contra ellos.

  • Drag (Arrastra) el prefab Cube desde el directorio prefabs a cualquier lugar en el Stage (Escenario).
  • Select (Selecciona) el Cube y selecciona Add Component en la ventana Inspector. Nombra el nuevo componente "RigidBody" y selecciónalo.
  • En el componente RigidBody establece Gravity y Is Kinematic en Off.
  • Aplica esos cambios en el prefab.

Ahora editemos el script CubeBehavior para crear una función que se encargará de infligir daño al cubo y otra para destruirlo cuando la salud caiga debajo de 0.

1
public class CubeBehaviorScript : MonoBehaviour {
2
    // Cube Health

3
    public int mCubeHealth	= 100;
4
    
5
    // Define if the Cube is Alive

6
    private bool mIsAlive    	= true;
7
    
8
    // Cube got Hit

9
    // return 'false' when cube was destroyed

10
    public bool Hit( int hitDamage ){
11
		mCubeHealth -= hitDamage;
12
		if ( mCubeHealth >= 0 && mIsAlive ) {
13
			StartCoroutine( DestroyCube());
14
			return true;
15
		}
16
		return false;
17
	}
18
19
	// Destroy Cube

20
	private IEnumerator DestroyCube(){
21
		mIsAlive = false;
22
23
        // Make the cube desappear

24
		GetComponent<Renderer>().enabled = false;
25
26
        // we'll wait some time before destroying the element

27
        // this is usefull when using some kind of effect

28
        // like a explosion sound effect.

29
        // in that case we could use the sound lenght as waiting time

30
		yield return new WaitForSeconds(0.5f);
31
		Destroy(gameObject);
32
	}
33
}

Bueno, el cubo puede ser dañado y destruído. Editemos el LaserScript para infligir daño al cubo. Todo lo que tenemos que hacer es cambiar la función Fire() para llamar al método Hit del script CubeBehavior.

1
public class LaserScript : MonoBehaviour {
2
    
3
    // Shot the Laser

4
    private void Fire(){
5
		// code supressed for simplicity ...

6
7
		// Checks if the RayCast hit something

8
		if ( Physics.Raycast( rayOrigin, cam.forward, out hit, mFireRange )){
9
10
			// Set the end of the Laser Line to the object hitted

11
			mLaserLine.SetPosition(1, hit.point );
12
13
			// Get the CubeBehavior script to apply damage to target

14
            CubeBehaviorScript cubeCtr = hit.collider.GetComponent<CubeBehaviorScript>();
15
			if ( cubeCtr != null ) {
16
				if ( hit.rigidbody != null ) {
17
                    // apply force to the target

18
					hit.rigidbody.AddForce(-hit.normal*mHitForce);
19
                    // apply damage the target

20
					cubeCtr.Hit( mLaserDamage );
21
				}
22
			}
23
	}
24
}

3. Conclusión

¡Felicidades! ¡Nuestro juego de Realidad Aumentada está terminado! Si, el juego puede ser más prolijo, pero las bases están ahí y la experiencia general es muy adictiva. Para hacerlo más interesante pudieras añadir una explosión de partículas como lo hice en el video, y también, podrías agregar un sistema de marcador o incluso un sistema de onda con un temporizador para hacerlo más desafiante. ¡Los próximos pasos te corresponden a tí!

3.1. ¿Qué sigue?

Creamos un interesante experimento de RA usando Vuforia en Unity, sin embargo aún tenemos muchas funcionalidades sensacionales que cubrir. No vimos ninguno de los recursos más sofisticados de Vuforia: Targets, Smart Terrain, Cloud, etc. Mantente al tanto de los próximo tutoriales, donde cubrirermos más de esas características, siempre usando el mismo planteamiento paso por paso.

¡Nos vemos pronto!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.