项目地址 演示视频 使用说明:创建一个空GameObject将FirstSceneController.cs挂载到新建游戏对象即可
游戏内容要求:
游戏有 n 个 round,每个 round 都包括10 次 trial每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制每个 trial 的飞碟有随机性,总体难度随 round 上升鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定游戏的要求:
使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类尽可能使用前面 MVC 结构实现人机交互与游戏模型分离设计实现如下:
1. 制作UFO prefabs如下 2. BaseCode.cs定义UFO类,并定义SSDriector
在UFO命名空间下定义,方便其他class调用
namespace UFO { public class Disk : MonoBehaviour{ public float size; //大小 public Color color; //颜色 public float speed; //速度 public Vector3 position; //初始位置 public Vector3 direction; //运动方向 } public class Director : System.Object { private static Director _instance; public ISceneController currentSceneController { get; set; } public static Director getInstance() { if (_instance == null) { _instance = new Director(); } return _instance; } } public interface ISceneController { //加载场景 void Init (); SceneController getSceneController(); //initQueue(mode) } }3. 实现DiskFactory.cs以工厂模式管理飞碟的生产与回收
UFO根据回合数设定不同速度和大小UFO设定随机初始位置和初始速度方向UFO6种颜色随机(白、黄、红、蓝、绿、黑) public class DiskFactory : MonoBehaviour { private List<Disk> toDelete = new List<Disk>(); private List<Disk> toUse = new List<Disk>(); public Color[] colors = {Color.white, Color.yellow, Color.red, Color.blue, Color.green, Color.black};//可选颜色 public GameObject GetDisk(int round) { //根据回合数对飞碟设置属性并返回 GameObject newDisk = null; if (toUse.Count > 0) { newDisk = toUse[0].gameObject; toUse.Remove(toUse[0]); } else { newDisk = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/UFO"), Vector3.zero, Quaternion.identity); newDisk.AddComponent<Disk>(); } // 飞碟的速度为 round * 7 newDisk.GetComponent<Disk>().speed = 7.0f * round; // 飞碟随 round 越来越小 newDisk.GetComponent<Disk>().size = (1 - round*0.1f); // 飞碟颜色随机 int color = UnityEngine.Random.Range(0, 6);//共有六种颜色 newDisk.GetComponent<Disk>().color = colors[color]; // 飞碟的发射方向 float RanX = UnityEngine.Random.Range(-1, 3) < 1 ? -1 : 1;//-1,0则为负方向,1,2则为正方向 newDisk.GetComponent<Disk>().direction = new Vector3(-RanX, UnityEngine.Random.Range(-2f, 2f), 0); // 飞碟的初始位置 newDisk.GetComponent<Disk>().position = new Vector3(RanX*13, UnityEngine.Random.Range(-2f, 2f), UnityEngine.Random.Range(-1f, 1f)); toDelete.Add(newDisk.GetComponent<Disk>()); newDisk.SetActive(false); newDisk.name = newDisk.GetInstanceID().ToString(); return newDisk; } public void FreeDisk(GameObject disk){ Disk cycledDisk = null; foreach (Disk toCycle in toDelete){ if (disk.GetInstanceID() == toCycle.gameObject.GetInstanceID()){ cycledDisk = toCycle; } } if (cycledDisk != null){ cycledDisk.gameObject.SetActive(false); toUse.Add(cycledDisk); toDelete.Remove(cycledDisk); } } }3. 设计ActionManager.cs管理场景的动作
管理飞碟的运动以及与用户交互点击飞碟,分数加一,飞碟消失 public class ActionManager : MonoBehaviour { public Vector3 direction;//运动方向 public float speed;//初速度 public GameObject cam; public void diskFly(Vector3 direction,float speed) {//赋予飞碟初速度和方向 this.direction = direction; this.speed = speed; } // Start is called before the first frame update void Start(){ cam = GameObject.Find("Main Camera"); } // Update is called once per frame void Update(){ this.gameObject.transform.position += speed * direction * Time.deltaTime; if (Input.GetButtonDown("Fire1")){ //光标拾取物体的结果,即鼠标集中飞碟的结果 Debug.Log("Fired Pressed"); Debug.Log(Input.mousePosition); Vector3 mp = Input.mousePosition; //get Screen Position //create ray, origin is camera, and direction to mousepoint Camera ca; if (cam != null) ca = cam.GetComponent<Camera>(); else ca = Camera.main; Ray ray = ca.ScreenPointToRay(Input.mousePosition); //Return the ray's hits RaycastHit[] hits = Physics.RaycastAll(ray); foreach (RaycastHit hit in hits){ print(hit.transform.gameObject.name); if (hit.collider.gameObject.tag.Contains("Finish")){ //plane tag Debug.Log("hit " + hit.collider.gameObject.name + "!"); } Singleton<DiskFactory>.Instance.FreeDisk(hit.transform.gameObject);//飞碟消失 //this.gameObject.GetComponent<UserGUI>().score ++; //SceneController.getInstance().addScore(); Director.getInstance ().currentSceneController.getSceneController().addScore();//分数加一 } } } }4. 设计SceneController.cs场景控制器
显示信息:回合数,总飞碟数,所得分数 public class SceneController { public int round ; //回合数 public int total ; //总飞碟数 public int score ;//得到的分数 private static SceneController sceneCtrl; public SceneController() {//用于SceneController归0 score = 0; total = 0; score = 0; } public static SceneController getInstance(){ if (sceneCtrl == null){ sceneCtrl = new SceneController(); } return sceneCtrl; } public void addRound(){ round++; } public void addTotal(){ total++; } public void addScore(){ score++; } public int getRound(){ return round; } public int getTotal() { return total; } public int getScore() { return score; } }5. 设计FirstSceneController.cs第一场景控制器
控制已发射的UFO个数,10/round控制飞碟发射时间间隔(与回合数成反比)负责控制游戏场景的加载和切换 public class FirstSceneController : MonoBehaviour, ISceneController{ public int diskFlyTimes; //已经发射的飞碟个数,每回合10个,最多30个 public float time;// 时间,用于控制飞碟发射间隔 public int round; // 当前回合数 // 飞碟队列 public Queue<GameObject> diskQueue = new Queue<GameObject>();//飞碟队列 public SceneController sceneCtrl; // Start is called before the first frame update void Start() { // 当前场景控制器 Director.getInstance().currentSceneController = this; this.gameObject.AddComponent<DiskFactory>(); this.gameObject.AddComponent<UserGUI>(); Director.getInstance().currentSceneController.Init();//初始化FirstSceneController相关数据 } // 初始化每个回合的飞碟队列,每个回合的飞碟属性不同 void initQueue() { for(int i = 0; i < 10; i++) diskQueue.Enqueue(Singleton<DiskFactory>.Instance.GetDisk(round)); } // Update is called once per frame void Update() { // round = sceneCtrl.getRound(); time += Time.deltaTime; // 发射飞碟的间隔回合数成反比 if(time >= 2.0f-0.3*round) { if(diskFlyTimes >= 30) { //游戏结束 Reset(); } else if ((diskFlyTimes % 10) == 0 ) { //更新回合(此步骤必须在发射飞碟前面) round++; //在initQueue()之前 sceneCtrl.addRound(); //回合数增加 initQueue(); //初始化新的飞盘队列 } if (diskFlyTimes < 30) { time = 0; ThrowDisk(); //发射飞盘 diskFlyTimes++; //飞盘数增加 sceneCtrl.addTotal(); //综费盘数增加 } } } public void ThrowDisk() { if(diskQueue.Count > 0) { GameObject disk = diskQueue.Dequeue(); disk.GetComponent<Renderer>().material.color = disk.GetComponent<Disk>().color; disk.transform.position = disk.GetComponent<Disk>().position; disk.transform.localScale = disk.GetComponent<Disk>().size * disk.transform.localScale; disk.SetActive(true); disk.AddComponent<ActionManager>(); disk.GetComponent<ActionManager>().diskFly(disk.GetComponent<Disk>().direction, disk.GetComponent<Disk>().speed); } } public void Init() { sceneCtrl = new SceneController(); //SceneController元素归0 diskFlyTimes = 0; time = 0; round = 0; diskQueue.Clear(); //清空飞盘队列 } public SceneController getSceneController() { //返回SceneController return sceneCtrl; } void Reset() { //游戏重置 this.gameObject.GetComponent<UserGUI>().reset = 1; } }7. 设计UserGUI.cs负责显示信息
回合数Round,未击中飞碟数Miss,分数Score重新开始按钮游戏界面 public class UserGUI : MonoBehaviour { public int reset; GUIStyle style; GUIStyle buttonStyle; // Start is called before the first frame update void Start() { reset = 0; style = new GUIStyle(); style.fontSize = 30; style.normal.textColor = Color.green;// buttonStyle = new GUIStyle("button"); buttonStyle.fontSize = 30; buttonStyle.normal.textColor = Color.green;// } // Update is called once per frame void Update() { } private void OnGUI() { if(reset == 1) { if(GUI.Button(new Rect(380, 250, 100, 80), "Reset", buttonStyle)) { Director.getInstance().currentSceneController.Init(); reset = 0; } } int round = Director.getInstance().currentSceneController.getSceneController().getRound(); int total = Director.getInstance().currentSceneController.getSceneController().getTotal(); int score = Director.getInstance().currentSceneController.getSceneController().getScore(); int miss = total - score;//未击中的飞碟数 string text = "Round: " + round.ToString() + "\nMiss: " + miss.ToString() + "\nScores: " + score.ToString(); GUI.Label(new Rect(10, 10, Screen.width, 50),text,style); } }用自定义组件定义几种飞碟,做成预制
参考官方脚本手册实现自定义组件,编辑并赋予飞碟一些属性实现旋转UFO、透明UFO
public class Rotate: MonoBehaviour { public float speed; public float xangle, yangle, zangle; void Update() { Vector3 axis = new Vector3(xangle, yangle, zangle); this.transform.RotateAround(new Vector3(0, 0, 0), axis, speed * Time.deltaTime); this.transform.Rotate(Vector3.up * 100 * Time.deltaTime); } }参考博客 师兄博客 师兄博客2
