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

图像目标

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

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

添加目标

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

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

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

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

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

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

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

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

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

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

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


现在,您可以下载数据库,选择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 foundStartCoroutine (InformSpawnCtr (true));
}private void OnTrackingLost ()
{DisableRendererAndCollider ();// Inform the system that the target was lostStartCoroutine (InformSpawnCtr (false));
}// inform SpanController that base was founded
private IEnumerator InformSpawnCtr (bool isOn)
{// move spawning positionGameObject spawn = GameObject.FindGameObjectWithTag ("_SpawnController");yield return new WaitForSeconds (0.2f);// inform SpanControllerif (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 VARIABLESprivate bool mSpawningStarted = false;// Cube element to spawnpublic GameObject mCubeObj;// Qtd of Cubes to be Spawnedpublic int mTotalCubes = 10;private int mCurrentCubes   = 0;// Time to spawn the Cubespublic 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 initializationvoid Start (){mScene = SceneManager.GetActiveScene();mCubes = new List<GameObject> ();if ( mScene.name == "ShootTheCubesMain" ){StartSpawn();}}// Update is called once per framevoid Update (){}#endregion // UNITY_METHODS

接下来,我们需要创建两个公共方法来在找到或丢失目标时接收来自TargetBehaviorScript调用:

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

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

#region PUBLIC_METHODS// Base was found by the trackerpublic void BaseOn (Vector3 basePosition){Debug.Log ("SpawnScript2: BaseOn");mIsBaseOn = true;// change positionSetPosition (basePosition);// start spawning process if necessaryStartSpawn ();// inform all cubes on screen that base appearedInformBaseOnToCubes ();}// Base lost by the trackerpublic void BaseOff (){   mIsBaseOn = false;mSpawningStarted = false;// inform all cubes on screen that base is lostInformBaseOffToCubes ();}#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 positionprivate IEnumerator ChangePosition (){Debug.Log ("ChangePosition");yield return new WaitForSeconds (0.2f);// Define the Spawn position only once// change the position only if Vuforia is activeif (VuforiaBehaviour.Instance.enabled)SetPosition (null);}// Set positionprivate void SetPosition (System.Nullable<Vector3> pos){if (mScene.name == "ShootTheCubesMain") {// get the camera positionTransform cam = Camera.main.transform;// set the position 10 units ahead of the camera positiontransform.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 positionprivate void InformBaseOnToCubes (){//            Debug.Log("InformBaseOnToCubes");foreach (GameObject cube in mCubes) {cube.GetComponent<CubeBehaviorScript> ().SwitchBaseStatus (mIsBaseOn);}}// Inform to all cubes that the base is offprivate void InformBaseOffToCubes (){//          Debug.Log("InformBaseOffToCubes");foreach (GameObject cube in mCubes) {cube.GetComponent<CubeBehaviorScript> ().SwitchBaseStatus (mIsBaseOn);}}

SpawnLoop()SpawnElement()方法使用的逻辑与上一教程几乎相同。

// Start spawning processprivate void StartSpawn (){if (!mSpawningStarted) {// begin spawnmSpawningStarted = true;StartCoroutine (SpawnLoop ());}}// Loop Spawning cube elementsprivate IEnumerator SpawnLoop (){if (mScene.name == "ShootTheCubesMain") {// Defining the Spawning PositionStartCoroutine (ChangePosition ());}yield return new WaitForSeconds (0.2f);// Spawning the elementswhile (mCurrentCubes <= (mTotalCubes - 1)) {// Start the process with different conditions// depending on the current stage nameif (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 cubeprivate GameObject SpawnElement (){// spawn the element on a random position, inside a imaginary sphereGameObject cube = Instantiate (mCubeObj, (Random.insideUnitSphere * 4) + transform.position, transform.rotation) as GameObject;// define a random scale for the cubefloat scale = Random.Range (0.5f, 2f);// change the cube scalecube.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 VARIABLESpublic float mScaleMax   = 1f;public float mScaleMin = 0.2f;public int mCubeHealth   = 100;// Orbit max Speedpublic float mOrbitMaxSpeed = 30f;public float velocityToBase = 0.4f;public int damage = 10;// Orbit speedprivate float mOrbitSpeed;// Orbit directionprivate Vector3 mOrbitDirection;// Max Cube Scaleprivate Vector3 mCubeMaxScale;// Growing Speedpublic 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_METHODSvoid Start () {// Get Scene namemScene = SceneManager.GetActiveScene();CubeSettings();}void Update () {// makes the cube orbit and rotateRotateCube();if ( mScene.name == "DefendTheBase" ) {// move cube towards the base, when it's visibleMoveToBase ();}// scale cube if neededif ( !mIsCubeScaled )ScaleObj();}#endregion

CubeSettings()还需要根据加载的场景进行调整。 多维数据集仅在DefendTheBase场景的y轴上旋转。

#region PRIVATE_METHODSprivate void CubeSettings (){// defining the orbit directionfloat 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 nameif ( mScene.name == "ShootTheCubesMain" ){mOrbitDirection = new Vector3( x, y, z );}else if ( mScene.name == "DefendTheBase" ){// orbit only on y axismOrbitDirection = new Vector3 (0, y, 0);// scale size must be limitedmScaleMin = 0.05f;mScaleMax = 0.2f;velocityToBase = 0.2f;}// rotating around its axisfloat rx = Random.Range (-1f, 1f);float ry = Random.Range (-1f, 1f);float rz = Random.Range (-1f, 1f);mRotationDirection = new Vector3 (rx, ry, rz);// defining speedmOrbitSpeed = Random.Range (5f, mOrbitMaxSpeed);// defining scalefloat scale = Random.Range (mScaleMin, mScaleMax);mCubeMaxScale = new Vector3 (scale, scale, scale);// set cube scale to 0, to grow it latertransform.localScale = Vector3.zero;// getting Explosion Sound EffectmExplosionFx = GetComponent<AudioSource> ();}

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

// Rotate the cube around the baseprivate void RotateCube (){// rotate around base or cameraif (mIsBaseVisible && mBase != null && mIsAlive) {// rotate cube around basetransform.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 1private void ScaleObj(){// growing objif ( transform.localScale != mCubeMaxScale )transform.localScale = Vector3.Lerp( transform.localScale, mCubeMaxScale, Time.deltaTime * mGrowingSpeed );elsemIsCubeScaled = true;}

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

// Move the cube toward the baseprivate void MoveToBase (){// make the cube move towards the base only if base is presentif (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 targetprivate void TargetHit (GameObject target){Debug.Log ("TargetHit: " + target.name);if (target.name == "Base") {// make damage on baseMyBase baseCtr = target.GetComponent<MyBase> ();baseCtr.TakeHit (damage);StartCoroutine (DestroyCube ());}}// Destroy Cubeprivate 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 destroyedpublic 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 statuspublic void SwitchBaseStatus (bool isOn){// stop the cube on the movement toward basemIsBaseVisible = 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

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

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

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


基本运行状况脚本

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

using UnityEngine;
using UnityEngine.UI;
using System.Collections;public class MyBase : MonoBehaviour
{#region VARIABLEpublic float rotationSpeed = 10f;public int health = 100;public AudioClip explosionSoundFx;public AudioClip hitSoundFx;// TODO choose a different sound for the Hitprivate bool mIsAlive = true;private AudioSource mAudioSource;public Slider mHealthSlider;#endregion // VARIABLES#region UNITY_METHODS// Use this for initializationvoid Start (){mAudioSource = GetComponent<AudioSource> ();mHealthSlider.maxValue = health;mHealthSlider.value = health;}// Update is called once per framevoid Update (){RotateBase ();}#endregion // UNITY_REGION#region PRIVATE_METHODSprivate void RotateBase (){if ( mIsAlive && gameObject != null ) {// implement object rotationtransform.Rotate ( Vector3.up, rotationSpeed * Time.deltaTime);}}// Destroy baseprivate IEnumerator DestroyBase (){mIsAlive = false;mAudioSource.clip = explosionSoundFx;mAudioSource.Play ();GetComponent<Renderer> ().enabled = false;// inform all Enemies that Base is LostGameObject[] 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 damagepublic 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 Laserprivate void Fire (){// Get ARCamera TransformTransform cam = Camera.main.transform;// Define the time of the next firemNextFire = Time.time + mFireRate;// Set the origin of the RayCastVector3 rayOrigin = cam.position;// Show the Laser using a CoroutineStartCoroutine (LaserFx ());// Holds the Hit informationRaycastHit hit;// Set the origin position of the Laser Line// It will add 10 units down from the ARCamera// We adopted this logic for simplicityVector3 laserStartPos = new Vector3 (cam.position.x, cam.position.y -2f, cam.position.z);mLaserLine.SetPosition (0, laserStartPos);    // Checks if the RayCast hit somethingif (Physics.Raycast (rayOrigin, cam.forward, out hit, mFireRange)) {// Set the end of the Laser Line to the object hitmLaserLine.SetPosition (1, hit.point);// check target typeif (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 rangemLaserLine.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

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

  1. 使用Vuforia创建神奇宝贝GO风格增强现实游戏

    您将要创造的 1.简介 在本系列的第一篇文章中 ,我们讨论了Vuforia在创建增强现实体验方面的出色表现,现在我们准备在实际的应用程序中实践这些概念. 在本教程中,我们将开始在Unity 3D上使用 ...

  2. 使用Vuforia创建PokémonGO风格增强现实游戏:第2部分

    您将要创造的 我们已经开始开发名为Shoot the Cubes的增强现实游戏. 现在是时候通过增加互动和增加体验来改进游戏了. 我们将主要关注Unity给我们带来的可能性,而忽略Vuforia的细节 ...

  3. 使用高通Vuforia开发AR增强现实游戏(开篇)

    @废话在前 之前想做暴风魔镜的VR游戏,最后来香港学习设备没带过来就没继续做,现在开始学习研究做一下AR游戏开发,毕竟对设备的要求比较低,很方便,看了不少资料,最后还是选了Vuforia这个经典的AR ...

  4. 【Unity教程】创建一个完整的驾驶游戏

    专业游戏设计 你会学到什么 在unity HDRP创建一个完整的驾驶游戏 定制不同类型的汽车 将人工智能汽车和人工智能航路点系统添加到你的赛道上 添加汽车展厅菜单以解锁和购买新车 在Blender中设 ...

  5. 怀旧:制作一款复古风格的游戏

    教程信息 难度:中级 预计完成时间:1小时 正如很多古老的事物都会被再一次兴起一样,复古的游戏也越来越受欢迎了.凭借着强烈的复古情怀以及IOS和Andriod设备上大量的休闲类.街机风格的游戏,复古风 ...

  6. 如何用Unity 和Vuforia 创建一款AR应用

    预先善其事,必先利其器,话不多说,一下就是我们使用软件的版本号: ·    Vuforia 5 ·    Unity 3D 5.x ·    Android Studio and SDK tools ...

  7. Unity 2D游戏开发快速入门第1章创建一个简单的2D游戏

    Unity 2D游戏开发快速入门第1章创建一个简单的2D游戏 即使是现在,很多初学游戏开发的同学,在谈到Unity的时候,依然会认为Unity只能用于制作3D游戏的.实际上,Unity在2013年发布 ...

  8. FreeEIM通讯软件无论是画面风格还是游戏内容

    摘要:FreeEIM通讯软件 2012年07月25日而增长最快的将是网络内容安全设备,48小时后,才是王道梦幻修女出来看仙女哦不对,简直是美呆了,FreeEIM通讯软件无论是画面风格还是游戏内容,里面 ...

  9. android 赛车 源码,android 3D风格赛车游戏源码

    android 3D风格赛车游戏源码,基于Libgdx 框架开发,三维视觉,包含20量敌方车辆和10量我方车辆,支持Admob广告插件,带游戏排行榜和成就系统 ,兼容手机.平板电脑等多种屏幕尺寸 ,游 ...

最新文章

  1. idm 爬取网站 跳转路径_儋州网站案例基本流程,电子元件网络推广,浅析
  2. WCF中的web.config配置文件
  3. unzipping/Users/xq/.gradle/wrapper /dists/gradle-3.3-all/55gk2rcmfc6p2dg9u9ohc3hw9/gradle-3.3-all.zi
  4. android app两种调试方法
  5. 用友U8清除站点异常任务SQL语句
  6. java calendar clear_java.util.Calendar clear()方法
  7. 2014年10月30日-----SQL的基础知识
  8. java爬取_java实现爬取知乎用户基本信息
  9. Linux之vim详解
  10. 【dsp】基于dsp28335的称重系统设计
  11. [Windows]msvcr110.dll运行库缺失问题解决方案
  12. [清橙A1364]社交网络结构洞【暴力】
  13. ISTP概况及网络版检索方法
  14. PHP 乐心 发送验证码 验证码识别
  15. 计算机键盘正确指法,键盘指法,详细教您盲打及快速打字指法练习的步骤
  16. springmvc ajax 传参
  17. k3 修复服务器,金蝶K3数据修复服务工具包
  18. virtualbox虚拟机识别U盘的详细教程
  19. 线程的6种状态(NEW,RUNNABLE,BLOCKED,WAITING,TINED_WATING,TEMINATE)
  20. C语言编写取单词首字母,C语言练习之单词首字母大写

热门文章

  1. Substance Painter 透明贴图
  2. C++公有继承,保护继承,私有继承的区别
  3. Symbian S60 Camera
  4. 【JZOJ 4639】Angel Beats!
  5. Extending Conductor
  6. iOS 打开照相机与本地相册选择图片
  7. (四)C语言中的字符类型
  8. 想学习SharePoint,需要准备哪些方面的准备?--写给SharePoint新人
  9. Java学习笔记 --- 字符类型
  10. 正则表达式搜索匹配的原理是什么