Unity3d--飞碟游戏
一.游戲規(guī)則與要求
-
游戲內(nèi)容要求:
游戲有 n 個(gè) round,每個(gè) round 都包括10 次 trial;
每個(gè) trial 的飛碟的色彩、大小、發(fā)射位置、速度、角度、同時(shí)出現(xiàn)的個(gè)數(shù)都可能不同。它們由該 round 的 ruler 控制;
每個(gè) trial 的飛碟有隨機(jī)性,總體難度隨 round 上升;
鼠標(biāo)點(diǎn)中得分,得分規(guī)則按色彩、大小、速度不同計(jì)算,規(guī)則可自由設(shè)定。 -
游戲的要求:
使用帶緩存的工廠模式管理不同飛碟的生產(chǎn)與回收,該工廠必須是場(chǎng)景單實(shí)例的!具體實(shí)現(xiàn)見參考資源 Singleton 模板類
近可能使用前面 MVC 結(jié)構(gòu)實(shí)現(xiàn)人機(jī)交互與游戲模型分離
二.游戲UML類圖
三.代碼說(shuō)明
首先是動(dòng)作控制器:前面大題的架構(gòu)與上次作業(yè)中的架構(gòu)基本相同,在場(chǎng)景控制器中使用FlyActionManager 類的函數(shù),然后在FlyActionManager 類中調(diào)頁(yè)UFOFlyAction類進(jìn)行每一幀對(duì)飛碟位置的更新即可實(shí)現(xiàn)飛碟飛的動(dòng)作。
在UFOFlyAction類中,給飛碟一個(gè)方向和一個(gè)力,然后飛碟每一幀計(jì)算下一幀做有向下加速度的飛行動(dòng)作的位置,然后進(jìn)行賦值即可實(shí)現(xiàn)的飛行動(dòng)作。
最后當(dāng)飛碟被點(diǎn)中或者飛出場(chǎng)景外就需要等待場(chǎng)景控制器和飛碟工廠進(jìn)行配合回收飛碟。
接下來(lái)是飛碟工廠類:它實(shí)現(xiàn)了要求
使用帶緩存的工廠模式管理不同飛碟的生產(chǎn)與回收,該工廠必須是場(chǎng)景單實(shí)例的!具體實(shí)現(xiàn)見參考資源 Singleton 模板類
飛碟工廠類的目的是管理飛碟實(shí)例,同時(shí)對(duì)外屏蔽飛碟實(shí)例的的提取和回收細(xì)節(jié)。它可以根據(jù)輪數(shù)不同生產(chǎn)出不同的飛碟,然后對(duì)于被點(diǎn)中或者飛出場(chǎng)景外的飛碟就可以進(jìn)行回收。飛碟工廠從倉(cāng)庫(kù)中獲取這種飛碟,如果倉(cāng)庫(kù)中沒(méi)有,則新的實(shí)例化一個(gè)飛碟,然后添加到正在使用的飛碟列表中。
using System.Collections; using System.Collections.Generic; using UnityEngine;public class DiskFactory : MonoBehaviour {public GameObject disk_prefab = null; private List<DiskData> used = new List<DiskData>(); private List<DiskData> free = new List<DiskData>(); public GameObject GetDisk(int round){ float start_y = -10f; string tag;disk_prefab = null;if (round == 1){tag = "disk1";;}else if(round == 2){tag = "disk2";}else{tag = "disk3";}for(int i=0;i<free.Count;i++){if(free[i].tag == tag){disk_prefab = free[i].gameObject;free.Remove(free[i]);break;}}if(disk_prefab == null){if (tag == "disk1"){disk_prefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk1"), new Vector3(0, start_y, 0), Quaternion.identity);}else if (tag == "disk2"){disk_prefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk2"), new Vector3(0, start_y, 0), Quaternion.identity);disk_prefab.GetComponent<DiskData> ().score = 2;}else{disk_prefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk3"), new Vector3(0, start_y, 0), Quaternion.identity);disk_prefab.GetComponent<DiskData> ().score = 3;}float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;disk_prefab.GetComponent<MeshRenderer> ().material.color = disk_prefab.GetComponent<DiskData>().color;disk_prefab.GetComponent<DiskData>().direction = new Vector3(ran_x, start_y, 0);disk_prefab.GetComponent<DiskData> ().tag = tag;disk_prefab.transform.localScale = disk_prefab.GetComponent<DiskData>().scale;}used.Add(disk_prefab.GetComponent<DiskData>());return disk_prefab;}public void FreeDisk(GameObject disk){for(int i = 0;i < used.Count; i++){if (disk.GetInstanceID() == used[i].gameObject.GetInstanceID()){used[i].gameObject.SetActive(false);free.Add(used[i]);used.Remove(used[i]);break;}}} }飛碟的參數(shù)類: 包含每個(gè)飛碟的顏色,分?jǐn)?shù),位置,大小,以及是屬于哪個(gè)round的飛碟。
using System.Collections; using System.Collections.Generic; using UnityEngine;public class DiskData : MonoBehaviour {public int score = 1; public Color color = Color.white; public Vector3 direction; public Vector3 scale = new Vector3( 1 ,0.1f, 1); public string tag; }然后是場(chǎng)景控制器類: 它實(shí)現(xiàn)了接口中的函數(shù)Hit (Vector3 pos);Restart ();GetScore();GameOver ();isCounting()以及getEmitTime ()。游戲開始時(shí)通過(guò)isCounting()以及getEmitTime ()判斷倒計(jì)時(shí)3秒是否完成,若倒計(jì)時(shí)結(jié)束則將counting(是否倒計(jì)時(shí))設(shè)為false并開始發(fā)送飛碟。在每一幀中可以進(jìn)行
InvokeRepeating(“LoadResources”, 1f, speed),即延時(shí)以speed的速度senddisk扔出飛碟,每次得分足夠后就加速speed以更快的方式扔出飛碟,提高游戲難度。Hit函數(shù)實(shí)現(xiàn)了玩家通過(guò)點(diǎn)擊發(fā)出子彈摧毀飛碟。玩家若擊中飛碟則觸發(fā)飛碟的粒子爆炸效果。其他函數(shù)都是與GUI交互的函數(shù)。
在此處滿足了要求:
該工廠必須是場(chǎng)景單實(shí)例的
實(shí)現(xiàn)了singleton類并使用singleton類初始化工廠類,使工廠是場(chǎng)景單實(shí)例的。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement;public class Controllor : MonoBehaviour,ISceneControllor,IUserAction {public FlyActionManager fam;public DiskFactory df;public UserGUI ug;public ScoreRecorder sr;public RoundControllor rc;private Queue<GameObject> dq = new Queue<GameObject> ();private List<GameObject> dfree = new List<GameObject> ();private GameObject explosion;private float emit_time = 3;private int round = 1;private float t = 1;private float speed = 2;private int score_round = 5;private bool flag = false;private bool game_over = false; private bool counting = true;public bool isCounting(){return counting;}public int getEmitTime(){return (int)emit_time+1;}void Start(){SSDirector director = SSDirector.GetInstance(); director.CurrentSceneControllor = this; df = Singleton<DiskFactory>.Instance;sr = gameObject.AddComponent<ScoreRecorder> () as ScoreRecorder;fam = gameObject.AddComponent<FlyActionManager>() as FlyActionManager;ug = gameObject.AddComponent<UserGUI>() as UserGUI;rc = gameObject.AddComponent<RoundControllor> () as RoundControllor;explosion = Instantiate (Resources.Load<GameObject> ("Prefabs/ParticleSystem1"), new Vector3(0, -100, 0), Quaternion.identity);t = speed;}void Update (){if (emit_time > 0) {counting = true;emit_time -= Time.deltaTime;} else {counting = false;t-=Time.deltaTime;if (t < 0) {LoadResources ();SendDisk ();t = speed;}if ((sr.score >= 10 && round == 1) || (sr.score >= 30 && round == 2)) {round++;rc.loadRoundData (round);}}}public void setting(float speed_,GameObject explosion_){speed = speed_; explosion = explosion_;}public void LoadResources(){dq.Enqueue(df.GetDisk(round)); }private void SendDisk(){float position_x = 16; if (dq.Count != 0){GameObject disk = dq.Dequeue();dfree.Add(disk);disk.SetActive(true);float ran_y = Random.Range(1f, 4f);float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;disk.GetComponent<DiskData>().direction = new Vector3(ran_x, ran_y, 0);Vector3 position = new Vector3(-disk.GetComponent<DiskData>().direction.x * position_x, ran_y, 0);disk.transform.position = position;float power = Random.Range(10f, 15f);float angle = Random.Range(15f, 28f);fam.UFOfly(disk,angle,power);}for (int i = 0; i < dfree.Count; i++){GameObject temp = dfree[i];if (temp.transform.position.y < -10 && temp.gameObject.activeSelf == true){df.FreeDisk(dfree[i]);dfree.Remove(dfree[i]);ug.ReduceBlood();}}}public void Hit (Vector3 pos){Ray ray = Camera.main.ScreenPointToRay(pos);RaycastHit[] hits;hits = Physics.RaycastAll(ray);bool not_hit = false;for (int i = 0; i < hits.Length; i++){RaycastHit hit = hits[i];if (hit.collider.gameObject.GetComponent<DiskData>() != null){for (int j = 0; j < dfree.Count; j++){if (hit.collider.gameObject.GetInstanceID() == dfree[j].gameObject.GetInstanceID()){not_hit = true;}}if(!not_hit){return;}dfree.Remove(hit.collider.gameObject);sr.Record(hit.collider.gameObject);explosion.transform.position = hit.collider.gameObject.transform.position;explosion.GetComponent<ParticleSystem>().Play();hit.collider.gameObject.transform.position = new Vector3(0, -100, 0);df.FreeDisk(hit.collider.gameObject);break;}}}public void Restart (){SceneManager.LoadScene(0);}public int GetScore (){return sr.score;}public void GameOver (){game_over = true;} }我們還需要一個(gè)記分員類記錄每次的得分:它可以根據(jù)擊中不同加上不同的分?jǐn)?shù)。
using System.Collections; using System.Collections.Generic; using UnityEngine;public class ScoreRecorder : MonoBehaviour {public int score = 0;void Start(){score = 0;}public void Record(GameObject disk){score = score + disk.GetComponent<DiskData> ().score;} }**接口類:**聲明了需要使用的函數(shù)提供給GUI使用并交給場(chǎng)景控制器實(shí)現(xiàn)。
using System.Collections; using System.Collections.Generic; using UnityEngine;public interface ISceneControllor{void LoadResources (); }public interface IUserAction{void Hit (Vector3 pos);void Restart ();int GetScore();void GameOver ();bool isCounting();int getEmitTime (); }輪數(shù)控制器類: 實(shí)現(xiàn)了要求
每個(gè) trial 的飛碟的色彩、大小、發(fā)射位置、速度、角度、同時(shí)出現(xiàn)的個(gè)數(shù)都可能不同。它們由該 round 的 ruler 控制;
我可以在類中控制飛碟的發(fā)射間隔以及爆炸效果。
using System.Collections; using System.Collections.Generic; using UnityEngine;public class RoundControllor : MonoBehaviour {private IUserAction action;private float speed;private GameObject explosion;void Start(){action = SSDirector.GetInstance().CurrentSceneControllor as IUserAction;speed = 2;}public void loadRoundData(int round){switch (round){case 1: break;case 2: speed = 1.5f;explosion = Instantiate (Resources.Load<GameObject> ("Prefabs/ParticleSystem2"), new Vector3(0, -100, 0), Quaternion.identity);action.setting (speed,explosion);break;case 3:speed = 1;explosion = Instantiate (Resources.Load<GameObject> ("Prefabs/ParticleSystem3"), new Vector3(0, -100, 0), Quaternion.identity);action.setting (speed,explosion);break;}} }用戶界面類: GUI中實(shí)現(xiàn)了重新開始功能,顯示生命值,得分以及游戲倒計(jì)時(shí)的功能。游戲倒計(jì)時(shí)即在場(chǎng)景控制器的update記錄消耗的時(shí)間然后取整,若大于0則顯示到屏幕上。通過(guò)接口函數(shù)就可以實(shí)現(xiàn)顯示生命值,得分等功能。然后可以調(diào)用場(chǎng)景控制器中的hit實(shí)現(xiàn)在屏幕上發(fā)射子彈摧毀飛碟。最后直接使用SceneManager.LoadScene(0)即實(shí)現(xiàn)重新開始。
using System.Collections; using System.Collections.Generic; using UnityEngine;public class UserGUI : MonoBehaviour {private IUserAction action;public int life = 6;GUIStyle bold_style = new GUIStyle();GUIStyle text_style = new GUIStyle(); GUIStyle text_style2 = new GUIStyle(); void Start (){action = SSDirector.GetInstance().CurrentSceneControllor as IUserAction;}void OnGUI (){text_style.normal.textColor = new Color(1,1,1, 1);text_style.fontSize = 16;text_style2.normal.textColor = new Color(1,1,1, 1);text_style2.fontSize = 100;if (action.isCounting ()) {GUI.Label(new Rect(Screen.width / 2 - 40, Screen.width / 2 - 300, 50, 50), action.getEmitTime().ToString(), text_style2);} else {if (Input.GetButtonDown("Fire1")){Vector3 pos = Input.mousePosition;action.Hit(pos);}GUI.Label(new Rect(10, 5, 200, 50), "score:", text_style);GUI.Label(new Rect(55, 5, 200, 50), action.GetScore().ToString(), text_style);GUI.Label(new Rect(10, 30, 50, 50), "hp:", text_style);for (int i = 0; i < life; i++){GUI.Label(new Rect(40 + 10 * i, 30, 50, 50), "X", text_style);}if (life == 0){GUI.Label(new Rect(Screen.width / 2 - 50, Screen.width / 2 - 300, 100, 100), "GameOver!", text_style);if (GUI.Button(new Rect(Screen.width / 2 - 60, Screen.width / 2 - 250, 100, 50), "Restart")){action.Restart();return;}action.GameOver();} }}public void ReduceBlood(){if(life > 0)life--;} }導(dǎo)演類:
using System.Collections; using System.Collections.Generic; using UnityEngine;public class SSDirector : System.Object {private static SSDirector _instance;public ISceneControllor CurrentSceneControllor{ get;set;}public static SSDirector GetInstance(){if (_instance == null) {_instance = new SSDirector ();}return _instance;} }其他設(shè)定:
預(yù)制設(shè)定如下:
有三種飛碟的預(yù)設(shè)以及每種飛碟對(duì)應(yīng)粒子效果的預(yù)設(shè)。
飛碟的設(shè)置:需要在每個(gè)飛碟上手動(dòng)掛載飛碟的參數(shù)腳本才能使用飛碟的參數(shù)。
粒子效果:持續(xù)實(shí)現(xiàn)1秒且不進(jìn)行循環(huán)即可實(shí)現(xiàn)爆炸效果。
腳本設(shè)置: 常見一個(gè)空游戲?qū)ο?#xff0c;在游戲?qū)ο笊蠏燧d場(chǎng)景控制類以及飛碟工廠類就可以運(yùn)行游戲了。
這樣游戲就完成了!
四.總結(jié)
在這次游戲設(shè)計(jì)中我體會(huì)到了MVC、動(dòng)作管理器的作用,有了這些基本的架構(gòu),我們游戲的實(shí)現(xiàn)更加容易,我們只需實(shí)現(xiàn)場(chǎng)景控制器以及各個(gè)模塊的控制器再通過(guò)MVC架構(gòu)將他們整合就可以得到一個(gè)游戲。
游戲演示:UFO小游戲
github地址:UFO
最后再次感謝師兄的博客供我參考!
總結(jié)
以上是生活随笔為你收集整理的Unity3d--飞碟游戏的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ESP32|基于ESP32制作的低成本、
- 下一篇: 用户情绪:愉悦和不爽