交互入门——基于鼠标控制的射击飞碟小游戏
生活随笔
收集整理的這篇文章主要介紹了
交互入门——基于鼠标控制的射击飞碟小游戏
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 游戲要求
- 游戲制作代碼
-
- 用戶交互接口
- 單例模板
- 飛碟工廠
- 動作相關類
-
- 飛碟飛行類:
- 飛行動作管理器
- Controller場景控制器
- 裁判類
- UI
- 游戲截圖
?
游戲要求
游戲內容要求:
自定義規則(游戲制作思路)
- 本次游戲設置了3個round,雖然round比較少,但是難度遞增明顯,而且有一定運氣成分,也就是說即使到了最后一個round,也有可能出現比較簡單的trial。
- 游戲難度主要由飛碟飛行速度(不同顏色代表不同屬性),飛碟同時出現個數決定,大致由round控制,但是帶有隨機性。
- 每個trial之間間隔1.5秒左右,且不帶提示信息。
- 飛碟得分分別為1分、2分、3分對應3種速度的飛碟。
游戲制作代碼
重復用到了之前實驗時候的一些類,這里就不再列出代碼了。
- 導演類,跟之前一樣負責管理場景控制器,是單實例
- 動作基類、動作管理器基類,跟之前一樣,只是子類的實現方式不同。
用戶交互接口
public interface Interaction {void hit(Vector3 pos);int GetScore();int getState();void changeState(int a);void reset(); }- ?
只要用于用戶點擊的時候判斷碰撞,記錄并顯示分數、記錄狀態、改變狀態(根據round、trial、漏掉的飛碟數等等決定),重置。這個需要場景控制器實現。
單例模板
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour {protected static T instance;public static T Instance { get { if (instance == null) { instance = (T)FindObjectOfType (typeof(T)); if (instance == null) { Debug.LogError ("An instance of " + typeof(T) +" is needed in the scene, but there is none."); } } return instance; } } }- ?
這個類可以用來實現各個類型的單例模式,使得每個類型有且僅有一個實例,與Director類似,不同的是這個類具有通用性,可以用于其他類型,只需更改模板的類型并且使用Instance的get函數來獲取實例。
飛碟工廠
首先需要定義一個存儲飛碟信息的結構,將其與飛碟實例綁定起來。
public class DiskInfo : MonoBehaviour {public Vector3 pos; //初始位置public Color color; //顏色,代表不同難度public float speed; // 初速度public Vector3 target; // 初速度方向public int hit = 0; // 是否已經被擊中一次,防止重復擊中而多計分 }- ?
下面就是工廠的類:
public class DiskFactory : MonoBehaviour {public GameObject disk = null;private List<DiskInfo> activeList = new List<DiskInfo>();private List<DiskInfo> freeList = new List<DiskInfo>();int rand1 = 6, rand2 = 9, rand3 = 13;public int difficulty = 0;public GameObject GetDisk(int round){GameObject newDisk = null;if (freeList.Count > 0){newDisk = freeList[0].gameObject;freeList.Remove(freeList[0]);}else{newDisk = Instantiate(Resources.Load<GameObject>("Prefabs/Disk"), Vector3.zero, Quaternion.identity);newDisk.AddComponent<DiskInfo>();}switch(round) {case 1: {difficulty = Random.Range(0, rand1);break;}case 2: {difficulty = Random.Range(0, rand2);break;}case 3: {difficulty = Random.Range(0, rand3);break;}}if (difficulty < rand1 - 1) {newDisk.GetComponent<DiskInfo>().color = Color.white;newDisk.GetComponent<DiskInfo>().speed = 5.0f;float RanX = Random.Range(-1f, 1f) < 0 ? -2 : 2;newDisk.GetComponent<DiskInfo>().target = new Vector3(RanX, 1, 0);newDisk.GetComponent<Renderer>().material.color = Color.white;}else if (difficulty < rand2 - 1) {newDisk.GetComponent<DiskInfo>().color = Color.red;newDisk.GetComponent<DiskInfo>().speed = 7.0f;float RanX = Random.Range(-1f, 1f) < 0 ? -2 : 2;newDisk.GetComponent<DiskInfo>().target = new Vector3(RanX, 1, 0);newDisk.GetComponent<Renderer>().material.color = Color.red;}else {newDisk.GetComponent<DiskInfo>().color = Color.black;newDisk.GetComponent<DiskInfo>().speed = 9.0f;float RanX = Random.Range(-1f, 1f) < 0 ? -2 : 2;newDisk.GetComponent<DiskInfo>().target = new Vector3(RanX, 1, 0);newDisk.GetComponent<Renderer>().material.color = Color.black;}activeList.Add(newDisk.GetComponent<DiskInfo>());return newDisk;}public void freeDisk(GameObject disk) {DiskInfo tmp = null;foreach (DiskInfo i in activeList){if (disk.GetInstanceID() == i.gameObject.GetInstanceID()){tmp = i;break;}}if (tmp != null) {tmp.gameObject.SetActive(false);tmp.hit = 0;freeList.Add(tmp);activeList.Remove(tmp);}} }- ?
- 每次外界訪問GetDisk函數的時候,都會傳入參數round,來決定生產(或者重用)哪一個類型的飛碟,這個帶有隨機性,難度隨round增大而增大。
- 需要維護兩個飛碟實例隊列,當飛碟被擊中或者落地的時候,可以重復使用該飛碟實例,將其加入空閑隊列,讓其在下一回合有需要的時候以新的形式(改變位置或者顏色等)出現,重新回到使用隊列。
- 根據隨機產生的難度,改變飛碟實例的屬性,并且將其加入使用隊列中。如果空閑隊列有對象,直接重用,否則新實例化一個(減少實例化帶來的性能損耗)。
動作相關類
除了以前的動作基類和動作管理器基類之外,還需要新建飛碟運動的子類去繼承
飛碟飛行類:
public class FlyAction : SSAction{public float g = 9.8f;public Vector3 to;//初速度方向public float v; //初速度public float v_down = 0;public float time;private FlyAction() {}public static FlyAction GetSSAction(Vector3 target, float speed) {FlyAction action = ScriptableObject.CreateInstance<FlyAction>();action.to = target;action.v = speed;return action;}public override void Update() {time += Time.fixedDeltaTime;this.transform.position += Vector3.down * (float)(v_down*Time.fixedDeltaTime+0.5*g*(Time.fixedDeltaTime)*(Time.fixedDeltaTime));this.transform.position += to * v * Time.fixedDeltaTime;v_down = time * g;if (this.transform.position.y <= -5) {this.destroy = true;if (this.transform.position.y > -15) {Singleton<Judger>.Instance.Miss();}this.callBack.SSActionEvent(this);}}public override void Start() {} }- ?
- 需要實現GetSSAction的方法,便于外界獲取動作對象。其中需要傳入兩個參數初速度和初速度方向,這就是飛行動作的關鍵(模擬重力的方向和大小是默認的)。
- 每次更新的時候都將物體移動一小段距離,這個移動根據兩個方向運動進行疊加,模擬一個類拋物線運動的過程。
- 當運動到某個位置(攝像機視角以下看不見的位置,就停止運動,并回調),由于被擊中后的飛碟也是設定到某個下方的位置,所以需要一個小小判斷來區分飛碟是被擊中還是自由落地的。
飛行動作管理器
public class FlyActionManager : ActionManager, ISSActionCallback {FlyAction UFOAction;Controller controller;private void Start(){controller = Director.getInstance().currentSceneController as Controller;controller.actionManager = this;}public void flyUFO(GameObject disk, Vector3 target, float speed) {UFOAction = FlyAction.GetSSAction(target, speed);this.RunAction(disk, UFOAction, this);}public void SSActionEvent(SSAction action){Singleton<DiskFactory>.Instance.freeDisk(action.gameObject);} }- ?
- 這個類做的不多,就是使得創建一個飛行動作實例,并且將其綁定到特定的對象上,執行動作。
- 回調函數則是在運動完成后,利用飛碟工廠回收飛碟實例。
Controller場景控制器
public class Controller : MonoBehaviour, SceneController, ISSActionCallback, Interaction {public FlyActionManager actionManager;public DiskFactory diskFactory;public Judger judger;public UserUI ui;public int trial = 10;public float time = 0;public int round = 1;public int n = 3;public int state = 0;public Queue<GameObject> diskQueue = new Queue<GameObject>();private void Awake(){Director director = Director.getInstance();director.currentSceneController = this;actionManager = gameObject.AddComponent<FlyActionManager>() as FlyActionManager;this.gameObject.AddComponent<DiskFactory>();this.gameObject.AddComponent<Judger>();diskFactory = Singleton<DiskFactory>.Instance;ui = gameObject.AddComponent<UserUI>() as UserUI;judger = Singleton<Judger>.Instance;}private void Update(){ if (state <= 0 || state == 2) {return;}if (trial == 0 && round >= n) {time += Time.deltaTime;if (time > 3) {changeState(-2);time = 0;}return;}if (trial == 0 && state == 1){state = 2;}if (trial == 0 && state == 3){round = (round + 1);if (round > n) {return;}trial = 10;state = 1;}if (time > 1.5){ if (Singleton<Judger>.Instance.checkGame() == false) {changeState(-1);return;}ThrowDisk();time = 0;}else{time += Time.deltaTime;}}public void ThrowDisk() {int tmp = Random.Range(0, round);int num = 0;if (tmp < 0.9) {diskQueue.Enqueue(diskFactory.GetDisk(round));num = 1;}else if (tmp < 2) {diskQueue.Enqueue(diskFactory.GetDisk(round));diskQueue.Enqueue(diskFactory.GetDisk(round));num = 2;}else{diskQueue.Enqueue(diskFactory.GetDisk(round));diskQueue.Enqueue(diskFactory.GetDisk(round));diskQueue.Enqueue(diskFactory.GetDisk(round));num = 3;}for(int i = 0; i < num; i ++) {GameObject disk = diskQueue.Dequeue();Vector3 position = new Vector3(0, 0, 0);float y = UnityEngine.Random.Range(-3f, 2f);position = new Vector3(-disk.GetComponent<DiskInfo>().target.x * 7, y, 0);disk.transform.position = position;disk.SetActive(true);actionManager.flyUFO(disk, disk.GetComponent<DiskInfo>().target,disk.GetComponent<DiskInfo>().speed);}trial --;}public void hit(Vector3 pos){Ray ray = Camera.main.ScreenPointToRay(pos);RaycastHit[] hits;hits = Physics.RaycastAll(ray);for (int i = 0; i < hits.Length; i++){RaycastHit hit = hits[i];if (hit.collider.gameObject.GetComponent<DiskInfo>() != null && hit.collider.gameObject.GetComponent<DiskInfo>().hit != 1){hit.collider.gameObject.GetComponent<DiskInfo>().hit = 1;judger.hit(hit.collider.gameObject);hit.collider.gameObject.transform.position = new Vector3(0, -20, 0);return;}}}public void loadResources() {}public void SSActionEvent(SSAction action) {}public int GetScore() {return Singleton<Judger>.Instance.getScore();}//游戲結束public int getState(){return state;}//游戲重新開始public void changeState(int a){state = a;}public void reset() {trial = 10;round = 1;time = 0;} }- ?
這個類的代碼比較多,稍微總結一下:
- 初始狀態,將各個部件實例化并添加到自身
- Update狀態,每一幀都判斷當前狀態(游戲結束或進行中,round和trial進行到哪一步)并作出相應狀態變化。還有每隔一段事件就執行丟飛碟的函數,使得飛碟飛出。
- 丟飛碟函數,就是從工廠里獲取實例對象(數量根據round隨機),設置一下初始位置。然后執行它們的飛行動作。
- hit函數,只要處理用戶鼠標點擊的交互,判斷上是否點擊到了飛碟,需要與UI類協同。
- 狀態分為幾個:round結束,游戲結束(分為正常結束和中途死亡)、游戲運行中。不同狀態下對應不同的狀態轉換。
裁判類
public class Judger : MonoBehaviour {private int score;private int miss;void Start() {score = 0;miss = 0;}public int getScore() {return score;}public bool checkGame() {if (miss >= 10) {return false;}return true;}public void hit(GameObject disk) {if(disk.GetComponent<DiskInfo>().color == Color.white) {score += 1;}else if (disk.GetComponent<DiskInfo>().color == Color.red) {score += 2;}else {score += 3;}}public void Miss() {Debug.Log("Miss one!");miss += 1;}public void restart() {miss = 0;score = 0;} }- ?
- 充當計分和判斷輸贏的角色,分別記錄未打中飛碟數和分數
- 根據打中飛碟的顏色不同加不同分數
- 檢查當前未擊中數是否達到上限,宣布游戲結束
以上函數都需外界調用。根據不同情景來調用。
UI
public class UserUI : MonoBehaviour {private Interaction action;bool flag = true;GUIStyle style1;GUIStyle style2;GUIStyle style3;float time = 0;void Start (){action = Director.getInstance().currentSceneController as Interaction;style1 = new GUIStyle("button");style1.fontSize = 25;style2 = new GUIStyle();style2.fontSize = 35;style2.alignment = TextAnchor.MiddleCenter;style3 = new GUIStyle();style3.fontSize = 25;style3.alignment = TextAnchor.MiddleCenter;}private void OnGUI() {if (action.getState() == -1) {GUI.Label(new Rect(Screen.width/2-50, Screen.height/2-105, 100, 50), "Game Over!", style2);GUI.Label(new Rect(Screen.width/2-50, Screen.height/2-55, 110, 40), "Your Score: "+Singleton<Judger>.Instance.getScore().ToString(), style3);if (GUI.Button(new Rect(Screen.width/2-70, Screen.height/2, 150, 70), "Play again", style1)){Singleton<Judger>.Instance.restart();action.reset();action.changeState(1);}return;}else if (action.getState() == -2) {GUI.Label(new Rect(Screen.width/2-50, Screen.height/2-105, 100, 50), "Finished!", style2);GUI.Label(new Rect(Screen.width/2-50, Screen.height/2-55, 110, 40), "Your Score: "+Singleton<Judger>.Instance.getScore().ToString(), style3);if (GUI.Button(new Rect(Screen.width/2-70, Screen.height/2, 150, 70), "Restart", style1)){Singleton<Judger>.Instance.restart();action.reset();action.changeState(1);}return;}if (Input.GetButtonDown("Fire1")){Vector3 pos = Input.mousePosition;action.hit(pos);}GUI.Label(new Rect(5, 5, 100, 50), "Score: " +Singleton<Judger>.Instance.getScore().ToString(), style3);if (flag) {GUI.Label(new Rect(Screen.width/2-50, Screen.height/2-95, 100, 50), "Hit UFO!", style2);if(GUI.Button(new Rect(Screen.width/2-70, Screen.height/2, 150, 70), "Play", style1)) {flag = false;action.changeState(1);}}if (!flag && action.getState() == 2){GUI.Label(new Rect(Screen.width/2-50, Screen.height/2-95, 100, 50), "Next Round!", style2);time += Time.deltaTime;if (time > 3.5) {action.changeState(3);time = 0;}}} }- ?
獲取Controller中的狀態,并且對應顯示不同的內容。
- round結束,游戲未結束時,顯示NextRound字樣
- 游戲剛開始(初始),顯示游戲名字。判斷是否按下開始按鈕才開始游戲。
- 游戲結束(分為中途死亡和正常結束)顯示對應的字樣,并設置按鈕使其重玩。重新開始同時重置裁判類和狀態。
邏輯:
- 判斷用戶鼠標點擊的位置,執行Controller的hit函數進一步判斷是否擊中目標。
游戲截圖
實驗到此結束!
總結
以上是生活随笔為你收集整理的交互入门——基于鼠标控制的射击飞碟小游戏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Unity3D学习:飞碟游戏进化版
- 下一篇: Unity3D学习:射击小游戏——飞碟世