使用Vuforia创建神奇宝贝GO风格增强现实游戏:图像目标

在本教程中,我们将跳回Vuforia增强现实(AR)库,探索其最有趣的资源之一-Image Target 。 我们将在之前的课程中扩展“射击立方体”游戏,并增加一个新的等级,玩家需要捍卫自己的基地以免攻击立方体。

图像目标

任何类型的图像都可以是Vuforia 图像目标 。 但是,图像越详细和复杂,算法识别的效果就越好。

识别计算将包括很多因素,但是基本上图像必须具有合理水平的对比度,分辨率和区分元素。 蓝天的照片效果不佳,但是一张草的照片可以正常工作。 图像目标可以随应用程序一起提供,可以通过云上传到应用程序,也可以由用户直接在应用程序中创建。

添加目标

首先,向我们的Unity项目添加一个ImageTarget元素。

首先,从侧栏中的按钮下载课程资源。 然后,在您的Unity项目中,创建一个名为DefendTheBase的新场景:在“ 项目”窗口中,选择“ 场景”文件夹,然后单击“ 创建” >“ 场景”。 现在打开该场景,并从层次结构中删除所有默认场景对象。

接下来,我们将添加一个灯光和一个摄像头。 单击添加 > 灯光 > 定向光以添加定向光。 选择此新光源并将“ 软阴影”设置为“ 阴影类型”选项。

之后,从Vuforia > Prefabs拖放一个ARCamera对象。 选择ARCamera对象,然后在检查器面板中设置在Vuforia开发人员页面上创建的App许可证密钥有关说明,请参阅第一个教程 )。 选择DEVICE_TRACKING作为World Center Mod

最后,拖放一个ImageTarget Vuforia > Prefabs的层次结构。

现在我们必须添加一个Vuforia数据库。 首先,浏览至https://developer.vuforia.com/target-manager 。 单击添加数据库,然后选择一个名称。

有三种类型的数据库可供选择:

  1. 设备 :数据库保存在设备上,并且所有目标都在本地更新。
  2. :Vuforia服务器上的数据库。
  3. VuMark :VuMark目标专用的数据库。 它也保存在设备上。

在这种情况下,选择设备选项,然后单击创建

选择新数据库,以便我们可以开始向其中添加目标。 现在是时候向数据库添加目标了。 现在,我们仅使用“ 单个图像”选项。

导航到以前下载的文件,选择ImageTarget1 ,并将其Width设置为1并单击Add 。 (注意:如果您想创建自己的Image Target,请先阅读该指南 。)

Vuforia数据库

现在,您可以下载数据库,选择Unity Editor作为所选平台。 打开文件,然后选择所有要导入的元素。 我们还必须准备Unity场景,以使用我们创建的该数据库识别ImageTarget

在Unity编辑器中,单击I mageTarget对象。 首先,在对象检查器中找到并展开“ 图像目标行为 ”。 选择一种预定义类型 。 选择我们先前为Database创建的映像目标。 最后,确保同时禁用“ 启用扩展跟踪”和“ 启用智能地形”选项。

图像目标设置

ImageTarget预制件由一系列组件组成,包括一些脚本,例如Image Target Behavior, T urn Off BehaviorDefault Tracker Event Handler 。 如果您想深入了解系统的工作原理,请阅读这些脚本并尝试了解它们与其他组件的关系。

但是,对于本教程,我们不会做得太深。 我们只需要专注于Default Tracker Event Handler ,当图像目标跟踪状态发生变化时,它将接收呼叫。 因此,让我们将此脚本用作创建自己的脚本行为的基础。

创建此脚本的副本,我们可以对其进行扩展。 首先选择Default Tracker Event Handler ,单击选项并选择Edit Script 。 现在,复制脚本。 如果使用的是MonoDevelop,请单击“ 文件” >“ 另存为”,然后另存为ImageTargetBehavior ,然后将其保存在“ 脚本”文件夹中。

TargetBehaviorScript脚本

我们的脚本中不需要Vuforia命名空间。 删除“ namespace Vuforia ”行和括号。 这意味着我们要访问Vuforia名称空间时需要显式引用它:

using UnityEngine;
using System.Collections;
public class BaseScript : MonoBehaviour, Vuforia.ITrackableEventHandler
{
    // code here
}

此类中最重要的方法是OnTrackableStateChanged方法,该方法可在相机设备找到或丢失图像目标时接收呼叫。 根据目标状态,它调用OnTrackingFoundOnTrackingLost ,我们也需要编辑这些方法。 但是首先,让我们考虑一下我们希望图像目标如何表现。

在此游戏中,用户将捍卫出现在图像目标上的基础。 让我们考虑以下游戏机制:

  • 一旦目标被系统识别,基地就会出现,敌人开始以神风队风格生成并飞向基地。
  • 每次敌人击中基地时,基地都会受到一定的伤害,敌人将被摧毁。
  • 为了赢得游戏,用户必须在摧毁基地之前射击并消灭所有敌人。
  • 如果图像目标丢失(从设备相机中不再可见),游戏将启动倒数计时器。 如果计时器为零,则游戏将失败。 当目标丢失时,所有敌人将停止向基地前进。

因此,我们需要在上一个教程中构建的内容之上调整这些游戏机制。 我们将在下一部分使用名为_SpawnController的空对象创建敌人的生成逻辑,使用游戏第一部分中采用的相同逻辑。

现在,让我们看一下跟踪发现的逻辑。

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 ColliderMesh Renderer

您也可以选择使用在Vuforia中较早提交的ImageTarget作为纹理在ImageTarget内插入一个Plane对象。 这将产生有趣的效果,从目标投射阴影并创造更丰富的体验。

层次结构和图像目标最终设置

修改SpawnScript

现在,我们将适应上一教程中使用的_SpawnController 。 保存当前场景, 然后从上一教程中打开ShootTheCubesMain 。 在“ 层次结构”面板中,选择_SpawnController并将其拖动到Prefabs文件夹以使其成为Unity Prefab

保存此新场景,然后重新打开DefendTheBase 。 将_SpawnControllerprefabs文件夹拖动到“ 层次结构”面板。 选择_SpawnController后 ,在“ 检查器”面板上单击“ 添加标签 ”。 将新标签命名为_SpawnController并将其应用于对象。

在“项目”窗口中,选择“ 预制”文件夹中的“ 多维数据集”元素,然后将其“ 标记”设置回到“检查器”中,使其位于检查器中。

将多维数据集标签设置为敌人

最后,打开Scripts文件夹并打开SpawnScript 。 我们需要使此脚本适应加载的场景。

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调用:

  • 当摄像机找到目标并显示Base对象时,将调用BaseOn (Vector3 basePosition) 。 它将更改生成位置,开始该过程,并通知先前添加到该阶段的所有多维数据集可见该基础。

  • 当目标丢失时,将使用BaseOff()方法。 它将停止暂存过程,并通知所有多维数据集元素已丢失基础。

#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轴,当加载的场景为ShootTheCubesMain时,它也可以接收null值。

#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()负责向所有暂存多维数据集通知当前基本状态。

// 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()方法使用的逻辑与上一教程几乎相同。

// 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

我们将在此脚本中保留几乎相同的逻辑,但有以下差异:

  • 当摄像机找到目标时, 立方体将追击基地
  • 魔方击中基地时 ,它会自我破坏并对基地造成一定的伤害。
  • 脚本需要知道加载的场景的名称并相应地进行调整。
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对象并开始向其移动。

#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()还需要根据加载的场景进行调整。 多维数据集仅在DefendTheBase场景的y轴上旋转。

#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()方法添加一些新逻辑。 当目标可见时,多维数据集对象将围绕底座旋转。 当目标不可见时,它们将继续使用与上一教程相同的逻辑围绕Camera旋转。

// 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;
	}

要将对象移向基座,我们首先需要检查基座是否存在,然后将定位步骤应用于该对象。

// 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()尚未建立。

// 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

最后,我们将添加要在多维数据集命中时,与基础碰撞时或当基础更改状态时调用的公共方法。

#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编辑器的“ 层次结构”面板中,单击创建 > UI > 滑块 。 新的Canvas元素将添加到层次结构中。 它包含UI元素,包括新的Slider 。 展开“ 画布”,然后选择“ 滑块”

将滑块元素名称更改为UIHealth 。 在“ 检查器”面板中,展开“ 矩形变换”,然后将“ 宽度”设置为400 ,将“ 高度”设置40 。 将位置X设置为-220位置Y设置30位置Z设置0

现在,在层次结构中展开滑块脚本。 取消选择“ 交互”选项。 对于“ 目标图形” ,单击右侧的小“点”,然后选择背景图像。

  • 将“ 最小值”设置为0 ,将“ 最大值”设置100
  • 选择整数
  • 设置为100
UIHealth设置

现在,展开“ 滑块”面板以显示其子元素: BackgroundFill AreaHandle Slide Area

  • 删除处理幻灯片区域
  • 选择背景并将其颜色设置为较深的绿色阴影,例如#12F568FF
  • 展开“ 填充区域”并选择“ 填充”对象,并将其颜色设置为#7FEA89FF

这就是游戏窗口与运行状况栏的外观。

带健康栏的游戏窗口
基本运行状况脚本

代码很简单; 它只是从基地生命值的总和中减去敌人造成的伤害。 一旦生命值降为零,玩家就会输掉比赛。 还将向基础添加旋转动画。 创建一个名为MyBase的新C#脚本。

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
}

现在,我们需要添加和配置脚本。

在层次结构中选择基础 ,单击添加组件 ,然后添加音频源 。 现在将MyBase拖动到Base元素,然后在“ 检查器”面板中,展开MyBase 。 为爆炸和打击选择声音效果。 我已经使用了上一教程中使用的爆炸剪辑,但可以随时添加自己的爆炸剪辑。 最后,在Health Slider中 ,选择UISlider元素。

基本设定

保卫基地

我们新的游戏体验即将完成。 我们只需要发射一些激光就可以保卫我们的基地。 让我们为激光创建一个脚本!

首先将_PlayerControllerPrefab文件夹拖到层次结构中。 展开_PlayerController并选择_LaserController 。 在“ 检查器”面板中,找到“ 激光脚本” ,然后单击“ 编辑”

我们需要在此脚本中更改的唯一内容是激光的位置。

// 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中使用它有了很好的了解。 我希望您和我一样喜欢这次旅行。 再见!

翻译自: https://code.tutsplus.com/tutorials/create-a-pokemon-go-style-augmented-reality-game-with-vuforia-part-3--cms-28246

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值