日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

3d学习笔记(四)——打飞碟小游戏

發布時間:2023/12/3 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 3d学习笔记(四)——打飞碟小游戏 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目

編寫一個簡單的鼠標打飛碟(Hit UFO)游戲

游戲內容要求:

  • 游戲有 n 個 round,每個 round 都包括10 次 trial;
  • 每個 trial的飛碟的色彩、大小、發射位置、速度、角度、同時出現的個數都可能不同。它們由該 round 的 ruler 控制;
  • 每個 trial的飛碟有隨機性,總體難度隨 round 上升;

游戲的要求:

  • 使用帶緩存的工廠模式管理不同飛碟的生產與回收,該工廠必須是場景單實例的!具體實現見參考資源 Singleton 模板類
  • 近可能使用前面MVC 結構實現人機交互與游戲模型分離
  • 如果你的使用工廠有疑問,參考:彈藥和敵人:減少,重用和再利用

實踐內容

游戲架構

RoundController:游戲的導演,總控制器,其中的shoot負責檢查是否擊中飛碟
RoundActionManager:動作管理者,負責管理動作的產生
UserGUI:負責渲染整個頁面的布局,主要是功能按鈕的實現
ScoreRecorder:負責分數的計算,根據飛碟的大小,速度,顏色,計算打中的得分
DiskDate:掛在飛碟預制上的組件,規定了飛碟的屬性
DiskFactory:負責生產不同大小,速度,顏色的飛碟

具體的源碼如下:

  • RoundController
using System; using System.Collections; using System.Collections.Generic; using UnityEngine;public enum State { WIN, LOSE, PAUSE, CONTINUE, START };public interface ISceneController {State state { get; set; }void LoadResources();void Pause();void Resume();void Restart(); }public class RoundController : MonoBehaviour, IUserAction, ISceneController {public DiskFactory diskFactory;public RoundActionManager actionManager;public ScoreRecorder scoreRecorder;private List<GameObject> disks;private int round;//第幾個回合private GameObject shootAtSth;GameObject explosion;//游戲狀態public State state { get; set; }//計時器, 每關60秒倒計時public int leaveSeconds;//用來計數,每秒自動發射一次飛碟public int count;IEnumerator DoCountDown(){while (leaveSeconds >= 0){yield return new WaitForSeconds(1);leaveSeconds--;}}void Awake(){SSDirector director = SSDirector.getInstance();director.setFPS(60);director.currentScenceController = this;LoadResources();diskFactory = Singleton<DiskFactory>.Instance;scoreRecorder = Singleton<ScoreRecorder>.Instance;actionManager = Singleton<RoundActionManager>.Instance;leaveSeconds = 60;count = leaveSeconds;state = State.PAUSE;disks = new List<GameObject>();}void Start(){round = 1;//從第一關開始LoadResources();}void Update(){LaunchDisk();Judge();RecycleDisk();}public void LoadResources(){Camera.main.transform.position = new Vector3(0, 0, -30);//explosion = Instantiate(Resources.Load("Prefabs/ParticleSys"), new Vector3(-40, 0, 0), Quaternion.identity) as GameObject;}public void shoot()//用戶在游戲狀態為開始或者繼續時,才能左鍵射擊{if (Input.GetMouseButtonDown(0) && (state == State.START || state == State.CONTINUE)){Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hit;if (Physics.Raycast(ray, out hit)){if ((SSDirector.getInstance().currentScenceController.state == State.START || SSDirector.getInstance().currentScenceController.state == State.CONTINUE)){shootAtSth = hit.transform.gameObject;//explosion.transform.position = hit.collider.gameObject.transform.position;//explosion.GetComponent<Renderer>().material = hit.collider.gameObject.GetComponent<Renderer>().material;//explosion.GetComponent<ParticleSystem>().Play();}}}}public void LaunchDisk()//每秒自動發射飛碟{if (count - leaveSeconds == 1){count = leaveSeconds;GameObject disk = diskFactory.GetDisk(round);//從飛碟工廠得到飛碟Debug.Log(disk);disks.Add(disk);//飛碟進入場景actionManager.addRandomAction(disk);//讓動作管理者設計軌跡}}public void RecycleDisk()//檢查需不需要回收飛碟{for (int i = 0; i < disks.Count; i++){if (disks[i].transform.position.z < -18){diskFactory.FreeDisk(disks[i]);//讓飛碟工廠回收disks.Remove(disks[i]);}}}public void Judge()//判斷游戲狀態,是否射中以及夠不夠分數進入下一回合{if (shootAtSth != null && shootAtSth.transform.tag == "Disk" && shootAtSth.activeInHierarchy)//射中飛碟{scoreRecorder.Record(shootAtSth);//計分diskFactory.FreeDisk(shootAtSth);//回收飛碟shootAtSth = null;//點擊的物體重置為空,避免計分出錯}if (scoreRecorder.getScore() > 500 * round)//每關500分才能進入下一關,重新倒數60秒{round++;leaveSeconds = count = 60;}if (round == 3) //只設計了兩關, 所以贏了{StopAllCoroutines();state = State.WIN;}else if (leaveSeconds == 0 && scoreRecorder.getScore() < 500 * round) //時間到,分數不夠,輸了{StopAllCoroutines();state = State.LOSE;}elsestate = State.CONTINUE;}public void Pause(){state = State.PAUSE;StopAllCoroutines();for (int i = 0; i < disks.Count; i++){disks[i].SetActive(false);//暫停后飛碟不可見}}public void Resume(){StartCoroutine(DoCountDown()); //開啟協程計時state = State.CONTINUE;for (int i = 0; i < disks.Count; i++){disks[i].SetActive(true);//恢復后飛碟可見}}public void Restart(){scoreRecorder.Reset();Application.LoadLevel(Application.loadedLevelName);SSDirector.getInstance().currentScenceController.state = State.START;}}
  • RoundActionManager
using System; using System.Collections; using System.Collections.Generic; using UnityEngine;public interface ISSActionCallback {void actionDone(SSAction source); }public class SSAction : ScriptableObject {public bool enable = true;public bool destroy = false;public GameObject gameObject { get; set; }public Transform transform { get; set; }public ISSActionCallback callback { get; set; }public virtual void Start(){throw new System.NotImplementedException();}public virtual void Update(){throw new System.NotImplementedException();} }public class MoveToAction : SSAction {public Vector3 target;public float speed;private MoveToAction() { }public static MoveToAction getAction(Vector3 target, float speed){MoveToAction action = ScriptableObject.CreateInstance<MoveToAction>();action.target = target;action.speed = speed;return action;}public override void Update(){this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);if (this.transform.position == target){this.destroy = true;this.callback.actionDone(this);}}public override void Start() { }}public class SequenceAction : SSAction, ISSActionCallback {public List<SSAction> sequence;public int repeat = -1; //-1表示無限循環,0表示只執行一遍,repeat> 0 表示重復repeat遍public int currentAction = 0;//當前動作列表里,執行到的動作序號public static SequenceAction getAction(int repeat, int currentActionIndex, List<SSAction> sequence){SequenceAction action = ScriptableObject.CreateInstance<SequenceAction>();action.sequence = sequence;action.repeat = repeat;action.currentAction = currentActionIndex;return action;}public override void Update(){if (sequence.Count == 0) return;if (currentAction < sequence.Count){sequence[currentAction].Update();}}public void actionDone(SSAction source){source.destroy = false;this.currentAction++;if (this.currentAction >= sequence.Count){this.currentAction = 0;if (repeat > 0) repeat--;if (repeat == 0){this.destroy = true;this.callback.actionDone(this);}}}public override void Start(){foreach (SSAction action in sequence){action.gameObject = this.gameObject;action.transform = this.transform;action.callback = this;action.Start();}}void OnDestroy(){foreach (SSAction action in sequence){DestroyObject(action);}} }public class SSActionManager : MonoBehaviour {private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();private List<SSAction> waitingToAdd = new List<SSAction>();private List<int> watingToDelete = new List<int>();protected void Update(){foreach (SSAction ac in waitingToAdd){actions[ac.GetInstanceID()] = ac;}waitingToAdd.Clear();foreach (KeyValuePair<int, SSAction> kv in actions){SSAction ac = kv.Value;if (ac.destroy){watingToDelete.Add(ac.GetInstanceID());}else if (ac.enable){ac.Update();}}foreach (int key in watingToDelete){SSAction ac = actions[key];actions.Remove(key);DestroyObject(ac);}watingToDelete.Clear();}public void RunAction(GameObject gameObject, SSAction action, ISSActionCallback whoToNotify){action.gameObject = gameObject;action.transform = gameObject.transform;action.callback = whoToNotify;waitingToAdd.Add(action);action.Start();}}public class RoundActionManager : SSActionManager, ISSActionCallback {public RoundController scene;public MoveToAction action1, action2;public SequenceAction saction;float speed;public void addRandomAction(GameObject gameObj){int[] X = { -20, 20 };int[] Y = { -5, 5 };int[] Z = { -20, -20 };// 隨機生成起始點和終點Vector3 starttPos = new Vector3(UnityEngine.Random.Range(-20, 20),UnityEngine.Random.Range(-5, 5),UnityEngine.Random.Range(50, 10));gameObj.transform.position = starttPos;Vector3 randomTarget = new Vector3(X[UnityEngine.Random.Range(0, 2)],Y[UnityEngine.Random.Range(0, 2)],Z[UnityEngine.Random.Range(0, 2)]);MoveToAction action = MoveToAction.getAction(randomTarget, gameObj.GetComponent<DiskData>().speed);RunAction(gameObj, action, this);}protected void Start(){scene = (RoundController)SSDirector.getInstance().currentScenceController;scene.actionManager = this;}protected new void Update(){base.Update();}public void actionDone(SSAction source){Debug.Log("Done");} }
  • UserGUI
using System.Collections; using System.Collections.Generic; using UnityEngine;public interface IUserAction {void shoot();//射擊動作 }public class UserGUI : MonoBehaviour {private IUserAction action;private float width, height;private string countDownTitle;void Start(){countDownTitle = "Start";action = SSDirector.getInstance().currentScenceController as IUserAction;}float castw(float scale){return (Screen.width - width) / scale;}float casth(float scale){return (Screen.height - height) / scale;}void OnGUI(){width = Screen.width / 12;height = Screen.height / 12;//倒計時GUI.Label(new Rect(castw(2f) + 20, casth(6f) - 20, 50, 50), ((RoundController)SSDirector.getInstance().currentScenceController).leaveSeconds.ToString());//分數GUI.Button(new Rect(580, 10, 80, 30), ((RoundController)SSDirector.getInstance().currentScenceController).scoreRecorder.getScore().ToString());if (SSDirector.getInstance().currentScenceController.state != State.WIN && SSDirector.getInstance().currentScenceController.state != State.LOSE&& GUI.Button(new Rect(10, 10, 80, 30), countDownTitle)){if (countDownTitle == "Start"){//恢復場景countDownTitle = "Pause";SSDirector.getInstance().currentScenceController.Resume();}else{//暫停場景countDownTitle = "Start";SSDirector.getInstance().currentScenceController.Pause();}}if (SSDirector.getInstance().currentScenceController.state == State.WIN)//勝利{if (GUI.Button(new Rect(castw(2f), casth(6f), width, height), "Win!")){//選擇重來SSDirector.getInstance().currentScenceController.Restart();}}else if (SSDirector.getInstance().currentScenceController.state == State.LOSE)//失敗{if (GUI.Button(new Rect(castw(2f), casth(6f), width, height), "Lose!")){SSDirector.getInstance().currentScenceController.Restart();}}}void Update(){//監測用戶射擊action.shoot();}}
  • ScoreRecorder
using System.Collections; using System.Collections.Generic; using UnityEngine;public class ScoreRecorder : MonoBehaviour {private float score;public float getScore(){return score;}public void Record(GameObject disk){//size越小、速度越快,分越高score += (100 - disk.GetComponent<DiskData>().size * (20 - disk.GetComponent<DiskData>().speed));//根據顏色加分Color c = disk.GetComponent<DiskData>().color;switch (c.ToString()){case "red":score += 50;break;case "green":score += 40;break;case "blue":score += 30;break;case "yellow":score += 10;break;}}public void Reset(){score = 0;} }
  • DiskDate
using System.Collections; using System.Collections.Generic; using UnityEngine;public class DiskData : MonoBehaviour {public float size;public Color color;public float speed; }
  • DiskFactory
using System.Collections; using System.Collections.Generic; using UnityEngine;public class DiskFactory : MonoBehaviour {private List<GameObject> used = new List<GameObject>();//存儲正在使用的飛碟private List<GameObject> free = new List<GameObject>();//存儲使用完了被回收的飛碟//顏色數組用于隨機分配顏色private Color[] color = { Color.red, Color.green, Color.blue, Color.yellow };//生產飛碟,先從回收部分取,若回收的部分為空,才從資源加載新的飛碟public GameObject GetDisk(int ruler){GameObject a_disk;if (free.Count > 0){a_disk = free[0];free.Remove(free[0]);}else{a_disk = GameObject.Instantiate(Resources.Load("Prefabs/Disk")) as GameObject;Debug.Log(a_disk);}switch (ruler){case 1:a_disk.GetComponent<DiskData>().size = UnityEngine.Random.Range(0, 6);//隨機大小a_disk.GetComponent<DiskData>().color = color[UnityEngine.Random.Range(0, 4)];//隨機顏色a_disk.GetComponent<DiskData>().speed = UnityEngine.Random.Range(10, 15);//不同關卡速度不同,同一關卡速度在一定范圍內a_disk.transform.localScale = new Vector3(a_disk.GetComponent<DiskData>().size * 2, a_disk.GetComponent<DiskData>().size * 0.1f, a_disk.GetComponent<DiskData>().size * 2);a_disk.GetComponent<Renderer>().material.color = a_disk.GetComponent<DiskData>().color;break;case 2:a_disk.GetComponent<DiskData>().size = UnityEngine.Random.Range(0, 4);a_disk.GetComponent<DiskData>().color = color[UnityEngine.Random.Range(0, 4)];a_disk.GetComponent<DiskData>().speed = UnityEngine.Random.Range(15, 20);a_disk.transform.localScale = new Vector3(a_disk.GetComponent<DiskData>().size * 2, a_disk.GetComponent<DiskData>().size * 0.1f, a_disk.GetComponent<DiskData>().size * 2);a_disk.GetComponent<Renderer>().material.color = a_disk.GetComponent<DiskData>().color;break;}a_disk.SetActive(true);used.Add(a_disk);return a_disk;}//回收飛碟public void FreeDisk(GameObject disk){for (int i = 0; i < used.Count; i++){if (used[i] == disk){disk.SetActive(false);used.Remove(used[i]);free.Add(disk);}}} }

難點說明

  • 工廠模式:主要是為了節省內存,提高效率,因此每次加載預制,在被擊中后不是銷毀,而且用一個鏈表儲存起來,在生產新的飛碟時,判斷該鏈表中是否存在可以用的飛碟,如果有則直接用,如果無才重新加載一個新的預制
  • 另外一個是利用射線組件,來實現點擊打中目標,具體實現代碼在RoundController文件中的shoot函數里面

總結

以上是生活随笔為你收集整理的3d学习笔记(四)——打飞碟小游戏的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。