前段时间赶ddl,没时间做项目了,现在继续。
请在完成前几讲的基础上完成本讲内容。
本次实验我们将制作一个指挥小兔子吃胡萝卜的小游戏。
一、准备工作
首先要下载一些3D素材,为以下两个链接:
兔子和萝卜,添加到我的资源后,在unity中打开,再下载并导入即可。
然后通过本链接下载我们要用的素材PlaneDetectionStarterPackage.unitypackage
,然后通过unity
的资源>导入包
导入。
二、平面检测
part1
像往常一样,新建一个场景,命名为PlaneDetection
,并打开。将Assets>NRSDK>Prefabs
中的NRCameraRig
和NRInput
拖入,并删除Main Camera
(下图忘删了)。点击NRInput
,在检查器中找到Session Config
一栏,双击点开NRKernalSessionConfig
,确认Plane Finding Mode
和Image Tracking Mode
是不是都启动了(见下图)。
确认一下NRInput
的Raycast Mode
是Laser
。
新建一个空的GameObject
,并命名为PlaneDetector
,添加Component(组件)
,搜索Plane Detector
并添加。再将Assets>PlaneDetectionStarterPackage
中的WoodenPlaneVisualizer
拖拽到Detected Plane Prefab
处。
接着我们创建一个光标,来给小兔子引路。将Assets>PlaneDetectionStarterPackage
中的Reticle Prefab
拖入场景中。
接下来我们要编写一个脚本,在Assets>Scripts
(这是前几讲建的一个文件夹,如果没有的话可以再建一个)中新建脚本ReticleBehaviour
,代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NRKernal;
public class ReticleBehaviour : MonoBehaviour
{
public GameObject Child;
public GameObject CurrentPlane;
void Start()
{
Child = transform.GetChild(0).gameObject;
}
void Update()
{
var handControllerAnchor=NRInput.DomainHand==ControllerHandEnum.Left?ControllerAnchorEnum.LeftLaserAnchor:ControllerAnchorEnum.RightLaserAnchor;
Transform laserAnchor = NRInput.AnchorsHelper.GetAnchor(NRInput.RaycastMode == RaycastModeEnum.Gaze ? ControllerAnchorEnum.GazePoseTrackerAnchor : handControllerAnchor);
RaycastHit hitResult;
if(Physics.Raycast(new Ray(laserAnchor.transform.position,laserAnchor.transform.forward),out hitResult,10))
{
var hit = hitResult.collider.gameObject;
if(hit != null&&
hit.GetComponent<NRTrackableBehaviour>()?.Trackable.GetTrackableType()==TrackableType.TRACKABLE_PLANE)
{
CurrentPlane= hit;
transform.position = hitResult.point;
Child.SetActive(true);
}
else Child.SetActive(false);
}
}
}
将上述脚本拖给Reticle Prefab
。
在资产中搜索Demo White Rabbit
,将其拖拽到Assets>PlaneDetectionStarterPackage
文件夹中。
新建一个脚本RabbitBehaviour
用来控制兔子的移动,代码如下:
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Security.Cryptography;
using System.Threading;
using UnityEngine;
public class RabbitBehaviour : MonoBehaviour
{
public GameObject playerctrl;
public ReticleBehaviour Reticle;
public float Speed;
Animator m_Animator;
void Update()
{
var trackingPosition = Reticle.transform.position;
m_Animator = gameObject.GetComponent<Animator>();
if(Vector3.Distance(trackingPosition,transform.position)<0.2)
{
m_Animator.SetBool("Run", false);
return;
}
m_Animator.SetBool("Run", true);
var lookRotation = Quaternion.LookRotation(trackingPosition - transform.position);
transform.rotation =
Quaternion.Lerp(transform.rotation, lookRotation, Time.deltaTime * 10f);
transform.position =
Vector3.MoveTowards(transform.position, trackingPosition, Speed * Time.deltaTime);
}
}
接下来设计一下兔子的动作。首先在Assets
创建一个Anim
文件夹,然后在文件夹内新建一个Animator Controller(动画器控制器)
,命名为RabbitAnim
。
将Assets>Rabbits>Animations
中的Rabbit@Idle
和Rabbit@Run
依次拖入Base Layer
中。
在方块上右击,点击创建过渡
,就可以将一个块和另一个块相连。我们将Idle
和Run
相互连起来,最终效果如下图。
点击参数
,点击+
,选择Bool
,将其重命名为Run
。
我们分别点击两个箭头,对触发条件进行修改。首先,取消勾选Has Exit Time(有退出时间)
,修改过渡持续时间
,添加Conditions
,参数为Run
,两个箭头的值分别为true
和false
,具体见下面两图(注意不要搞错了)
打开Assets>PlaneDetectionStartedPackage>DemoWhiteRabbit
,然后点击检查器
右上角的锁锁住(记得之后要解锁),再将Assets>Anim>RabbitAnim
拖入控制器
处。
在场景中新建一个空的GameObject
,重命名为Rabbit Spawner
,再在Assets>Scripts
文件夹中创建脚本RabbitManager
,脚本代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NRKernal;
public class RabbitManager : MonoBehaviour
{
public GameObject WhiteRabbit;
public ReticleBehaviour Reticle;
public RabbitBehaviour Rabbit;
private GameObject LockedPlane;
private void Update()
{
if(Rabbit==null && WasTapped()&&Reticle.CurrentPlane!=null)
{
var obj = Instantiate(WhiteRabbit);
Rabbit = obj.GetComponent<RabbitBehaviour>();
Rabbit.Reticle= Reticle;
Rabbit.transform.position=Reticle.transform.position;
}
}
private bool WasTapped()
{
return NRInput.GetButtonDown(ControllerButton.TRIGGER);
}
}
把上述脚本添加到RabbitSpawner
中,再将Assets>PlaneDetectionStartedPackage>DemoWhiteRabbit
拖入White Rabbit
,将场景中的Reticle Prefab
拖入Reticle
。
在Assets>RootVegetables>Prefabs
中选择一个你喜欢的萝卜拖入Assets>PlaneDetectionStartedPackage
中。
在Scripts
中新建脚本CarrotSpawner
,代码如下:(写完之后有报错是正常现象)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NRKernal;
using NRKernal.NRExamples;
using System.Net;
public class CarrotSpawner : MonoBehaviour
{
public CarrotBehaviour carrot;
public GameObject CarrotPrefab;
public ReticleBehaviour Reticle;
public static Vector3 RandomInTriangle(Vector3 v1,Vector3 v2)
{
float u = Random.Range(0.0f, 1.0f);
float v = Random.Range(0.0f, 1.0f);
if(v+u>1)
{
v = 1 - v;
u=1- u;
}
return (v1*u) + (v2*v);
}
public Vector3 FindRandomLocation(GameObject plane)
{
var mesh = plane.GetComponent<PolygonPlaneVisualizer>().m_PlaneMesh;
var triangles=mesh.triangles;
var triangle = triangles[(int)Random.Range(0,triangles.Length-1)]/3*3;
var vertices = mesh.vertices;
var randomInTriangle = RandomInTriangle(vertices[triangle], vertices[triangle + 1]);
var randomPoint = plane.transform.TransformPoint(randomInTriangle);
randomPoint.y=Reticle.CurrentPlane.GetComponent<NRTrackableBehaviour>().Trackable.GetCenterPose().position.y;
return randomPoint;
}
public void SpawnGem(GameObject plane)
{
var gemClone = Instantiate(CarrotPrefab);
gemClone.transform.position=FindRandomLocation(plane);
carrot = gemClone.GetComponent<CarrotBehaviour>();
}
private void Update()
{
if(Reticle.CurrentPlane != null)
{
if(carrot ==null)
{
SpawnGem(Reticle.CurrentPlane);
}
}
}
}
再新建一个脚本CarrotBehaviour
,不对这个脚本写任何代码。
在资产中寻找PolygonPlaneVisualizer
脚本文件,打开后,将m_PlaneMesh
修改为public
。改完后,回到unity
,应该就没有报错了。
将这个脚本与Assets>PlaneDetectionStartedPackage
中的那个萝卜关联起来。
接着创建一个空的GameObject
,命名成Carrot
。然后将上面创建控制脚本文件CarrotSpawner
与这个Object
关联,并且把萝卜模型以及Reticle
的模型拖拽到对应的位置。
最后需要将下面这段代码增加到RabbitBehaviour
的脚本文件中,用来实现兔子吃萝卜的简单碰撞动作,当兔子靠近萝卜后,碰撞产生,萝卜消失。
private void OnTriggerEnter(Collider other)
{
var Carrot = other.GetComponent<CarrotBehaviour>();
if(Carrot != null)
{
Destroy(other.gameObject);
}
}
对兔子模型增加Rabbit Behaviour
、Box Collider
和Rigidbody
三个组件,并修改参数到合适即可。然后,对萝卜模型也增加Box Collider
组件。如下图所示。
至此,就可以导出运行了。
三、额外功能
根据先前所讲的内容,我们将额外添加手势识别和录屏的功能。
1、手势控制(该功能目前存在问题,之后有时间的时候将补档)
这里,我们想要不再使用手机作为控制器,而是使用手来控制,具体操作如下:
将NRInput
的Input Source Type
改为Hands
。
在资产中搜索NRHand
,将NRHand_L
和NRHand_R
分别拖入NRInput
中作为其中Right
和Left
的子对象。
2、录屏
这个比较简单,可以参考之前的文章lesson 5。这里仅做简单说明。
首先要修改Assets>Plugins>Android>AndroidManifest
(如果和之前用的同一个项目可以不用改了,详情见上一讲)。
搜索RGBCamera-Record
场景并拖入,复制对象VideoCaptureExample
,并粘贴在PlaneDetection
场景中,再将RGBCamera-Record
场景移除即可。