游戏规则
- 游戏开始后,小猫向前移动,玩家需要根据前方路况的不同做出反应调整小猫的位置,如果小猫不在能通过木桥的位置,或者在木桥上变道,则游戏失败
- 通过触碰虚拟按钮来移动小猫,需要注意的是,显示和现实是镜像的,所以会比较有难度
- 长按按钮不能把小猫从最左移动到最右或者从最右移动到最左,要触碰按钮后放开再次触碰才能达到目的
游戏效果
游戏结构
这次的游戏比较简单,就不贴UML图啦
游戏具体实现
资源
本游戏的两个资源都是asset store上的,都是免费的,分别搜索资源名就能找到啦。
AR
AR和virtualbutton的配置就不再细说,具体可以参考TA的PPT,按部就班就可以完成。需要注意的事virtualbutton要放在识别图的范围内,而且大小和触碰的方法都要注意,不然很容易点了半天却没有点到。
触碰和释放按钮方法
小猫一共有三个状态,触碰按钮先根据其当前状态改变其状态,然后再在Updatea()中判断其状态,如果不在正确的位置就移动小猫的正确的位置。当然点击和释放按钮都要改变按钮的颜色,这样用户体验比较好。
public class VirtualButtonEventHandler : MonoBehaviour, IVirtualButtonEventHandler{ public enum CatState {LEFT, MIDDLE, RIGHT} //for debug public float catz; // private Vector3 toLf; private Vector3 toRg; public CatState cat_state = CatState.MIDDLE; public GameObject cat; public GameObject LfBtnCube; public GameObject RgBtnCube; public Animator ani; public VirtualButtonBehaviour[] vbs; // Use this for initialization void Start () { vbs = GetComponentsInChildren<VirtualButtonBehaviour>(); for (int i = 0;i < vbs.Length;i ++) { vbs[i].RegisterEventHandler(this); } ani.SetTrigger("Walk"); //color of buttons LfBtnCube.GetComponent<MeshRenderer>().material.color = Color.black; RgBtnCube.GetComponent<MeshRenderer>().material.color = Color.black; toLf = new Vector3(-1, 0, 0); toRg = new Vector3(1, 0, 0); } // Update is called once per frame void Update () { if (Director.GetInstance().playing) { //for debug catz = cat.transform.position.z; //move cat if (cat_state == CatState.LEFT && cat.transform.position.z < 0.1f * 0.06f) { cat.transform.Translate(toLf * Time.deltaTime * 0.03f); } else if (cat_state == CatState.RIGHT && cat.transform.position.z > -0.1f * 0.06f) { cat.transform.Translate(toRg * Time.deltaTime * 0.03f); } else { if (cat.transform.position.z > 0) { cat.transform.Translate(toRg * Time.deltaTime * 0.03f); } else if (cat.transform.position.z < 0) { cat.transform.Translate(toLf * Time.deltaTime * 0.03f); } } } else { ani.ResetTrigger("Walk"); ani.SetTrigger("Idle"); } } public void OnButtonPressed(VirtualButtonAbstractBehaviour vb) { switch (vb.VirtualButtonName) { case "Lf": Debug.Log("Lfpressed"); //change color of button LfBtnCube.GetComponent<MeshRenderer>().material.color = Color.yellow; //change cat state if (cat_state == CatState.MIDDLE) cat_state = CatState.LEFT; else if (cat_state == CatState.RIGHT) cat_state = CatState.MIDDLE; else cat_state = CatState.LEFT; break; case "Rg": Debug.Log("Rgpressed"); //change color of button RgBtnCube.GetComponent<MeshRenderer>().material.color = Color.yellow; //change cat state if (cat_state == CatState.MIDDLE) cat_state = CatState.RIGHT; else if (cat_state == CatState.LEFT) cat_state = CatState.MIDDLE; else cat_state = CatState.RIGHT; break; } } public void OnButtonReleased(VirtualButtonAbstractBehaviour vb) { Debug.Log("released"); //ani.SetTrigger("Idle"); switch (vb.VirtualButtonName) { case "Lf": //change color of button LfBtnCube.GetComponent<MeshRenderer>().material.color = Color.black; break; case "Rg": //change color of button RgBtnCube.GetComponent<MeshRenderer>().material.color = Color.black ; break; } } }
判断是否死亡
新建一个轨道的子空物体,在上面根据轨道的不同类型挂上很多个trigger
当小猫碰撞trigger时将playing参数设为false即游戏结束,因为这次游戏的结构比较简单,playing就直接放在Director中,实现起来也比较方便
private void OnTriggerEnter(Collider other) { Director.GetInstance().playing = false; Debug.Log("Die!!"); }
动作控制
动作控制的话其实只有Idle和Walk两个动作,游戏进行时Walk,结束时Idle
if (Director.GetInstance().playing) { ... } else { ani.ResetTrigger("Walk"); ani.SetTrigger("Idle"); }
难点及解决方法
小猫移动
因为ARcamera是跟着小猫的,所以让小猫移动其实有点麻烦,不如直接让轨道移动,因为没有其他参照物看起来就跟小猫移动是一样的
void Update () { if(Director.GetInstance().playing) this.transform.position = new Vector3(this.transform.position.x - 0.0008f, this.transform.position.y, this.transform.position.z); }
无尽轨道
实现的方法是使用两段轨道,一段走完走上下一段的时候把之前走完的那一段接在现在正在走的轨道后面,如此循环看起来就跟轨道走不完一样
public class TrackLoop : MonoBehaviour { public float runningTrackx; public GameObject track1; public GameObject track2; public GameObject runningTrack; public GameObject preparingTrack; private float positionLf; private float positionRg; // Use this for initialization void Start () { runningTrack = track1; preparingTrack = track2; positionLf = runningTrack.transform.position.x; positionRg = preparingTrack.transform.position.x; } // Update is called once per frame void Update () { if (preparingTrack.transform.position.x <= positionLf) { Debug.Log("TrackLoop!!!"); runningTrack.transform.position = new Vector3(positionRg, runningTrack.transform.position.y, runningTrack.transform.position.z); preparingTrack.transform.position = new Vector3(positionLf, preparingTrack.transform.position.y, preparingTrack.transform.position.z); GameObject tmp = runningTrack; runningTrack = preparingTrack; preparingTrack = tmp; } } }
显示GameOver
这里是用了一个UI控件,然后游戏结束时SetActive(),就显示出来了
public class UserGUI : MonoBehaviour { //public GUISkin mySkin; public GameObject UI; void Start() { //GUI.skin = mySkin; UI.SetActive(false); } void Update() { if (!Director.GetInstance().playing) { //GUI.Label(new Rect(400, 100, 800, 450), "GameOver"); UI.SetActive(true); } } }