作业与练习
1、编写一个简单的鼠标打飞碟(Hit UFO)游戏
- 游戏内容要求:
- 游戏有 n 个 round,每个 round 都包括10 次 trial;
- 每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制;
- 每个 trial 的飞碟有随机性,总体难度随 round 上升;
- 鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
- 游戏的要求:
- 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
- 近可能使用前面 MVC 结构实现人机交互与游戏模型分离
这星期学习的内容是与游戏世界的交互,而且学习了工厂模式,结合我们之前学得动作分离模式和导演场记模式,我们将要来实验这样的一个打飞碟游戏,游戏还是很好玩的。我们先大致了解一下工厂模式,因为之前已经讲过动作分离和场记,那么实际上我们的另外两个理解起来就很容易了。
飞碟工厂的设计模式如下:单实例化的工厂,分数控制和场景控制以及各种预设的分离和使用。
我们先声明一下我们想要实现的游戏的大概模式:
这样的一个模式的进阶有利于增加游戏的趣味性,难度越来越高,完成就越来越难。但得分也会增多,我们主要通过控制飞碟的飞出频率,飞出速度以及飞碟的大小来改变难度。具体实现我们下面慢慢讲。
1.首先是飞碟工厂的实现,工厂里面应该实现两个功能:getDisk(ruler) 和FreeDisk(disk):
* getDisk(ruler) //伪代码 IF (free list has disk) THEN a_disk = remove one from list ELSE a_disk = clone from Prefabs ENDIF Set DiskData of a_disk with the ruler Add a_disk to used list Return a_disk FreeDisk(disk) //伪代码 Find disk in used list IF (not found) THEN THROW exception Move disk from used to free list */
同时这里面也实现了飞碟的轮次的改变以及难度的增加
using System.Collections; using System.Collections.Generic; using UnityEngine; /*游戏规则: * 游戏总共分三轮,每一轮改变飞碟的大小和速度以及频率。 * 采用积分晋级制 * 打中第一轮的盘子奖励3分 * 打中第二轮的盘子奖励5分 * 打中第三轮的盘子奖励8分 * 每一轮总共飞出20个盘子 * 第一轮累计得分达到30分晋级, * 第二轮累计得分达到100分晋级, * 第三轮累计得分达到200分胜利! * 有点类似于我们在游乐场里面的投篮 * 难度逐渐增大 */ public class DiskFactory : System.Object { private static DiskFactory _instance; public SceneController sceneControler { get; set; } public List<GameObject> used; public List<GameObject> free; // Use this for initialization private static List<GameObject> diskList; public static DiskFactory getInstance() { if (_instance == null) { _instance = new DiskFactory(); _instance.used = new List<GameObject>(); _instance.free = new List<GameObject>(); } return _instance; } public GameObject getDisk(int round) { // if (sceneControler.num == 21) //每轮总共发射20个,如果得分达到一定要求进入下一轮,否则GameOver { if (sceneControler.round == 1 && sceneControler.score >= 30) { sceneControler.round++; sceneControler.num = 0; } if (sceneControler.round == 2 && sceneControler.score < 30) { sceneControler.game = 2;//输了比赛 } if (sceneControler.round == 2 && sceneControler.score >= 100) { sceneControler.round++; sceneControler.num = 0; } if (sceneControler.round == 3 && sceneControler.score < 100) { sceneControler.game = 2;//输了比赛 } if (sceneControler.round == 3 && sceneControler.score >= 200) { sceneControler.round++; sceneControler.num = 0; } if (sceneControler.round == 4 && sceneControler.score < 200) { sceneControler.game = 2;//输了比赛 } } if (sceneControler.round == 4) { sceneControler.game = 4;//赢了比赛 } GameObject newDisk; if (free.Count == 0) { newDisk = GameObject.Instantiate(Resources.Load("prefabs/Disk"), new Vector3(0, 0, -10), Quaternion.identity) as GameObject; } else { newDisk = free[0]; free.Remove(free[0]); } switch (round) //根据轮数制定飞碟的颜色和大小 { case 1: newDisk.transform.localScale = new Vector3(1.5f, 0.2f, 1.5f); newDisk.GetComponent<Renderer>().material.color = Color.blue; break; case 2: newDisk.transform.localScale = new Vector3(1.2f, 0.2f, 1.2f); newDisk.GetComponent<Renderer>().material.color = Color.gray; break; case 3: newDisk.transform.localScale = new Vector3(1.0f, 0.2f,1.0f); newDisk.GetComponent<Renderer>().material.color = Color.yellow; break; } used.Add(newDisk); return newDisk; } public void freeDisk(GameObject disk1) { for (int i = 0; i < used.Count; i++) { if (used[i] == disk1) { used.Remove(disk1); disk1.SetActive(true);//被鼠标击中的disk设置为false,所以这里全部激活一遍 free.Add(disk1); } } } }
2.再把大概的游戏主题加架起来,怎么得分,怎么控制,各种游戏的基础。
void Awake() //创建导演实例并载入资源 { SSDirector director = SSDirector.getInstance(); DiskFactory DF = DiskFactory.getInstance(); DF.sceneControler = this; director.currentScenceController = this; director.setFPS(60); director.currentScenceController.LoadResources(); } void Start() { round = 1; } public void LoadResources() { explosion = Instantiate(Resources.Load("prefabs/Explosion"), new Vector3(0, 0, -20), Quaternion.identity) as GameObject; plane = Instantiate(Resources.Load("prefabs/Plane"), new Vector3(0, 0, 0), Quaternion.identity) as GameObject; } // Update is called once per frame void Update() { Score.text = "Score:" + score.ToString(); Round.text = "Round:" + round.ToString(); if (Input.GetMouseButtonDown(0) && game == 1)//游戏中 { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { if (hit.transform.tag == "Disk") { if (round == 1 && game == 1) { score += 3; } if (round == 2 && game == 1) { score += 5; } if (round == 3 && game == 1) { score += 8; } explosion.transform.position = hit.collider.gameObject.transform.position; explosion.GetComponent<Renderer>().material = hit.collider.gameObject.GetComponent<Renderer>().material; explosion.GetComponent<ParticleSystem>().Play(); hit.collider.gameObject.SetActive(false); } } } if (game == 2) { GameOver(); } if (game == 4) { Win(); } } public IEnumerator waitForOneSecond() { while (StartTimes >= 0 && game == 3) { Time.text = StartTimes.ToString(); Time.color = Color.red; yield return new WaitForSeconds(1); //一秒减计数1 StartTimes--; } Time.text = ""; game = 1; } public void ReStart() { SceneManager.LoadScene("task1"); }
3.实现飞碟的飞行路径以及速度
飞行路径:
public override void Update() { Vector3 targetPos = target.transform.position; //让始终它朝着目标 gameobject.transform.LookAt(targetPos); float angle = Mathf.Min(1, Vector3.Distance(gameobject.transform.position, targetPos) / distanceToTarget) * 45; gameobject.transform.rotation = gameobject.transform.rotation * Quaternion.Euler(Mathf.Clamp(-angle, -42, 42), 0, 0); float currentDist = Vector3.Distance(gameobject.transform.position, target.transform.position); gameobject.transform.Translate(Vector3.forward * Mathf.Min(speed * Time.deltaTime, currentDist)); if (this.transform.position == target.transform.position) { DiskFactory.getInstance().freeDisk(gameobject); Destroy(target); this.enable = false; this.destroy = true; } }
飞行速度:
public override void Start() { speed = 5 + sceneControler.round * 5;//使速度随着轮数变化 startX = 5 - Random.value * 10;//使发射位置随机在(-5,5) targetY = -5; this.transform.position = new Vector3(startX, 0, -6); target = new GameObject();//创建终点 target.transform.position = new Vector3(targetX, targetY, 30); //计算两者之间的距离 distanceToTarget = Vector3.Distance(this.transform.position, target.transform.position); }
改变飞碟的发射频率
protected new void Update() { if (sceneController.round == 1 && sceneController.game == 1) { count++; if (count == 70)//70帧发射一个,发射的速度不同,发射慢一点 { DiskMove = Emit2.GetSSAction(); this.addAction(diskFactory.getDisk(sceneController.round), DiskMove); sceneController.num++;//记录发射的个数 count = 0; } base.Update(); } if (sceneController.round ==2 && sceneController.game == 1) { count++; if (count == 60)//60帧发射一个,发射的速度不同 { DiskMove = Emit2.GetSSAction(); this.addAction(diskFactory.getDisk(sceneController.round), DiskMove); sceneController.num++;//记录发射的个数 count = 0; } base.Update(); } if (sceneController.round ==3 && sceneController.game == 1) { count++; if (count == 50)//60帧发射一个,发射的速度不同 { DiskMove = Emit2.GetSSAction(); this.addAction(diskFactory.getDisk(sceneController.round), DiskMove); sceneController.num++;//记录发射的个数 count = 0; } base.Update(); } }
UI的实现:
void OnGUI() { GUIStyle fontstyle1 = new GUIStyle(); fontstyle1.fontSize = 50; if (GUI.Button(new Rect(10, 10, 120, 40), "开始游戏")) { action.StartGame(); } if (GUI.Button(new Rect(140,10 , 120, 40), "重新开始")) { action.ReStart(); } if (GUI.RepeatButton(new Rect(750, 10, 120, 40), "游戏规则")) { action.ShowRule(); } }
积分规则:
if (Physics.Raycast(ray, out hit)) { if (hit.transform.tag == "Disk") { if (round == 1 && game == 1) { score += 3; } if (round == 2 && game == 1) { score += 5; } if (round == 3 && game == 1) { score += 8; } explosion.transform.position = hit.collider.gameObject.transform.position; explosion.GetComponent<Renderer>().material = hit.collider.gameObject.GetComponent<Renderer>().material; explosion.GetComponent<ParticleSystem>().Play(); hit.collider.gameObject.SetActive(false); } }接着实现导演和场记就行就不在这里粘贴了,完整代码移步github。这次的工厂模型还是要好好加强处理。游戏的演示视屏链接是:https://pan.baidu.com/s/1dMO82DvbrXyfnUEodvmPOw。