3D游戏编程 作业五 枪打恶鬼(打飞碟)
前言
這次的作業個人感覺挺好玩的,我給做成了射擊游戲。同時我也在其中學到了unity的一些有趣應用, 下面先把大的框架給講一下,然后再著重講一下我個人的收獲吧。Asset文件:github
參考博客:學長博客
一、任務要求
游戲規則(自定義)
控制你的超進化版鼠標槍射擊飛行玩偶(飛碟),擊中玩偶或者放任玩偶下落都會有相應的得分,得分如表。游戲共十回合,隨回合數增加,玩偶數跟飛行速度增加。若得分為負,游戲失敗;若回合進行完畢,游戲勝利。
| 蝙蝠 | 5 | -2 |
| 惡鬼 | 10 | -2 |
| 軟泥怪 | 20 | -2 |
| 兔子 | -10 | 5 |
二、 項目展示
這里不得不提一下,我特別喜歡作業三的mvc框架,只需三個文件(Model、Controllor、UserGUI)就可以將結構講明白。所以我這次也是在那個框架的基礎上進行修改的。
1.mvc架構
Model 模型(其實把大部分類都給匯總了)
這里的導演類等都是以前作業內容就不再贅述
using System.Collections; using System.Collections.Generic; using UnityEngine;namespace mygame {public interface ISceneController //加載場景public interface IUserAction //用戶互動會發生的事件public class SSDirector : System.Object//導演類public class ScoreController : System.Object{//分數控制器public class DiskFactory : System.Object {//飛碟工廠public class DiskModel//飛碟模型public class Diskbehaviour : MonoBehaviour//飛碟行為public class GunModel//槍模型public class Gunbehaviour: MonoBehaviour//槍行為}View 交互界面
using System.Collections; using System.Collections.Generic; using UnityEngine; using mygame; public class UserGUI : MonoBehaviour {private IUserAction action;public int sign = 0;bool isShow = false;void Start(){action = SSDirector.GetInstance().CurrentScenceController as IUserAction;}void OnGUI(){//規則展示if (GUI.Button(new Rect(10, 10, 60, 30), "Rule", new GUIStyle("button"))){if (isShow)isShow = false;elseisShow = true;}if(isShow){GUI.Label(new Rect(Screen.width / 2 - 85, 10, 200, 50), "擊敗惡魔,保護小兔子");GUI.Label(new Rect(Screen.width / 2 - 150, 30, 350, 50), "子彈打中惡魔得分,讓惡魔逃跑丟分,打中小兔子丟分");GUI.Label(new Rect(Screen.width / 2 - 85, 50, 250, 50), "點擊屏幕進行射擊");}//游戲準備if (sign == 0){if (GUI.Button (new Rect (Screen.width / 2 - 80, Screen.height / 2, 160, 20), "開始游戲")){action.gamestart();}}//游戲中else if(sign == 1){string say = "當前回合回合數為" + action.getround().ToString() + ",分數為" + ScoreController.GetScoreController().getscore().ToString() +"分";GUI.Box (new Rect (Screen.width / 2 - 100, 70, 200,30), say);}//游戲結束else if (sign == 2||sign == 3){string say;if(sign == 2)say = "你輸了,一共堅持了" + action.getround().ToString() + "局";else say = "你贏了,一共得了" + ScoreController.GetScoreController().getscore().ToString() +"分";GUI.Box (new Rect (Screen.width / 2 - 100, Screen.height / 2 + 50, 200, 30), say);if (GUI.Button (new Rect (Screen.width / 2 - 80, Screen.height / 2, 160, 20), "重開")){action.gamestart();sign = 1;}}} }場記 Controllor
其實這里再寫一個游戲控制的類,或者把游戲規則丟進分數控制器里應該會好一點,但是本人有點懶,就把場景控制全丟這了
using System.Collections; using System.Collections.Generic; using UnityEngine; using mygame; public class Controllor : MonoBehaviour, ISceneController, IUserAction {public static System.Random rand;UserGUI user_gui;DiskFactory diskFactory;ScoreController scoreController;List<DiskModel> diskModels;public int round;GunModel gun;void Start (){//設置導演SSDirector director = SSDirector.GetInstance();//設置場景director.CurrentScenceController = this;//新建Viewuser_gui = gameObject.AddComponent<UserGUI>() as UserGUI;//設置分數控制器scoreController = ScoreController.GetScoreController();//設置飛碟工廠diskFactory =DiskFactory.getFactory();//設置飛碟模型diskModels = diskFactory.GetDiskModels();//設置隨機種子rand = new System.Random((int) System.DateTime.Now.Ticks & 0x0000FFFF);LoadResources();} 這里稍微講一下上面倒數第二行。這個隨機搞了我半天,因為這里的C#調用了unity的庫,會出現重載等情況, 導致一些常用的類諸如DateTime、Random不能跟平常一樣使用。這時候我們可以通過在類前面添加System來進 行調用。然后在使用C#隨機數時存在偽隨機數的情況,這里用時間做種子避免了。然后unity本身也是用random的,不過 用法跟原來得就不一樣了,有無偽隨機我沒有試過。最后,下面的代碼無縫銜接上文 public void LoadResources(){gun = new GunModel();}public void gamestart(){round = 0;scoreController.gamestart();gun.gamestart();user_gui.sign = 1;}public void gamestop(){diskFactory.recycleall();gun.gameend();}void Update(){if(user_gui.sign==1){checkdisk();//檢查是否分數為負數user_gui.sign = Check();if(user_gui.sign==1&&diskModels.Count==0){//當前回合結束,增加回合并發射飛碟round++;if(round<=10)sentdisk();}//檢查是否超過十局user_gui.sign = Check();if(user_gui.sign!=1)gamestop();}}public void checkdisk(){for(int i = diskModels.Count-1;i>=0;i--){if(diskModels[i].status()==0)continue;string effect;if(diskModels[i].status()==1){//掉出空間scoreController.addscore(diskModels[i].getdiegrade());effect = "disappear";}else{//被打中scoreController.addscore(diskModels[i].getgrade());if(diskModels[i].getkind()==3)effect = "death";else effect = "ok";}//網上下好的特效預設Destroy(Object.Instantiate(Resources.Load(effect, typeof(GameObject)),diskModels[i].getpos(),Quaternion.identity) as GameObject,3);//回收模型diskModels[i].destroy();DiskFactory.getFactory().recycleDisk(i);}}public void sentdisk(){int num = round/3+1;//飛碟數量int speed = round/2+1;//飛碟速度diskFactory.prepareDisks(num,rand);for(int i = 0;i< diskModels.Count;i++){diskModels[i].setdisk(speed,rand);}}public int Check(){//檢測游戲狀態if(scoreController.getscore()<0)return 2;if(round>10)return 3;return 1;}public int getround(){return round;} }2.分數控制器、飛碟工廠和飛碟模型
ScoreController 分數控制器
public class ScoreController : System.Object{//分數控制器private static ScoreController scoreController;int score;public static ScoreController GetScoreController () {if (scoreController == null) {scoreController = new ScoreController ();}return scoreController;}public int getscore(){return score;}public void addscore(int point){score += point;}public void gamestart(){score = 0;}}DiskFactory 飛碟工廠
這里的實現是參考學長博客的,個人覺得把基本的邏輯都實現了,但不是很清楚具體實現是不是這樣。
public class DiskFactory : System.Object {//飛碟工廠public DiskModel diskPrefab;private static DiskFactory diskFactory;List<DiskModel> usingDisks;List<DiskModel>[] uselessDisks;public static DiskFactory getFactory () {if (diskFactory == null) {diskFactory = new DiskFactory ();diskFactory.uselessDisks = new List<DiskModel>[4];diskFactory.usingDisks = new List<DiskModel>();for(int i = 0;i<4;i++){diskFactory.uselessDisks[i] = new List<DiskModel>();}}return diskFactory;}public void prepareDisks (int diskCount,System.Random r) {for (int i = 0; i < diskCount; i++) {int kind = r.Next()%4;//飛碟種類隨機if (uselessDisks[kind].Count == 0) {DiskModel disk = new DiskModel(kind);usingDisks.Add (disk);} else {DiskModel disk = uselessDisks[kind][0];uselessDisks[kind].RemoveAt (0);usingDisks.Add (disk);}}}public void recycleall(){for(int i = usingDisks.Count-1;i>=0;i--)recycleDisk(i);}public void recycleDisk (int index) {uselessDisks[usingDisks[index].getkind()].Add (usingDisks[index]);usingDisks.RemoveAt (index);}public List<DiskModel> GetDiskModels(){return usingDisks;}}DiskModel 飛碟模型
這里首先開了個靜態數組,存儲了累死累活測出來的可行的位置、施加力、扭矩力對應數組。
public class DiskModel{GameObject disk;static Vector3[] position = {new Vector3(-20,10,20),new Vector3(15,5,10),new Vector3(-20,0,0),new Vector3(20,20,15),new Vector3(0,0,10)};static Vector3[] force = {new Vector3(5,5,-4),new Vector3(-8,5,0),new Vector3(8,8,5),new Vector3(-5,0,-1.5f),new Vector3(0,8,0)};static Vector3[] rota = {new Vector3(5,5,-5),new Vector3(10,0,1),new Vector3(-3,5,4),new Vector3(1,-12,-8),new Vector3(-3,-6,4)};int grade;int diegrade;int kind;public int statu;Diskbehaviour diskbehaviour;public DiskModel(int k){kind = k;string kstr = "disk";kstr += kind.ToString();disk = Object.Instantiate(Resources.Load(kstr, typeof(GameObject))) as GameObject;if(kind== 0){grade = 5;diegrade = -2;}else if(kind == 1){grade = 10;diegrade = -2;}else if(kind == 2){grade = 20;diegrade = -2;}else if(kind == 3){grade = -10;diegrade = 5;}statu = 0;diskbehaviour = disk.AddComponent(typeof(Diskbehaviour)) as Diskbehaviour;diskbehaviour.setdisk(this);}這里對飛碟運動的處理是我這次很受益的地方,本來我已經寫好了自制的move類了,但是因為數據的原因,會顯得很僵硬。然后回頭一看學長博客,只能說這個力的運用秒不可言。其中扭矩力的添加,單純是我覺得物體轉起來會更3D一些
public void setdisk(int sp,System.Random r){//改變飛碟速度int rp = r.Next()%position.GetLength(0);disk.transform.position = position[rp];Rigidbody rigidbody;rigidbody = disk.GetComponent<Rigidbody>();//啟動剛體rigidbody.WakeUp();rigidbody.useGravity = true;//添加瞬間力float disksp = 1;for(int i = 1;i<sp;i++)disksp *= 1.1f;rigidbody.AddForce(force[rp]*Random.Range(5, 8)*disksp/5, ForceMode.Impulse);//添加旋轉力rigidbody.AddTorque(rota[rp] * 10);}public int getgrade(){return grade;}public int getdiegrade(){return diegrade;}public int getkind(){return kind;}public Vector3 getpos(){return disk.transform.position;}public int status(){if(disk.transform.position.y<-3)statu = 1;return statu;}public void destroy(){statu = 0;disk.GetComponent<Rigidbody>().Sleep();disk.GetComponent<Rigidbody>().useGravity = false;disk.transform.position = new Vector3(0f, -99f, 0f);}}最后這里對飛碟剛體的處理更讓我大為嘆服,把物體的剛體運算關掉,再放到看不到的角落,確實是個好方法
3.槍與子彈的實現
這一部分,我學會了如何讓槍口隨鼠標移動,子彈發射,與子彈碰撞這三點。個人覺得這應該是游戲制作中挺還是實用的。
GunModel 槍模型
public class GunModel{GameObject gun;Gunbehaviour gunbehaviour;public GunModel(){gun = Object.Instantiate(Resources.Load("gun", typeof(GameObject)),new Vector3(1,0,-9),Quaternion.identity) as GameObject;gunbehaviour = gun.AddComponent(typeof(Gunbehaviour)) as Gunbehaviour;}public void gamestart(){gunbehaviour.setaction(1);}public void gameend(){gunbehaviour.setaction(0);gun.transform.rotation = Quaternion.identity;}}Gunbehaviour 槍行為
public class Gunbehaviour: MonoBehaviour{public Vector3 mousePos;int action = 0;float firesp = 100;GameObject bullet;GameObject fire;void Start(){bullet = Object.Instantiate(Resources.Load("bullet", typeof(GameObject)),new Vector3(0f, -99f, -99f),Quaternion.identity) as GameObject;bullet.GetComponent<Rigidbody>().useGravity = false;fire = transform.GetChild(1).gameObject;}技巧一 :通過腳本來找到子對象
獲得對象的transform后可以通過GetChild(index)來找到對應下標的子對象,transform的gameobject可以直接找到對應對象。
技巧二 鼠標移動調動物體旋轉 :
這里我們可以通過Input.mousePosition來不斷獲得鼠標焦點位置,而通過記錄鼠標位置的變化,我們可以通過同步移動物體的rotation來實現調動旋轉的效果。其中-0.1f相當于靈敏度與方向,可自行調整
技巧三 子彈射擊 :
首先我們在槍口這里設置好空對象,這樣我們就可以通過找到預設子對象的位置來找到槍口。
然后是transform.TransformDirection,這個方法是將局部坐標轉為世界坐標,說人話就是你走路只管前后左右(你當前rotation的相對坐標),這個方法會自動幫你轉成東南西北(屏幕里看到的世界坐標,即絕對坐標)
子彈碰撞實現
兩者使用碰撞體
其中實現碰撞(要添加腳本的)勾選Is Trigger
腳本實現的函數
添加腳本如下
public class Diskbehaviour : MonoBehaviour{DiskModel disk;public void setdisk(DiskModel disk){this.disk = disk;}void OnTriggerEnter(Collider collider) {if(collider.tag == "Finish"){disk.statu = 2;}}}其中我們需要修改被碰撞(沒有腳本)的游戲對象的tag,如下
三、 展示效果
1.展示圖
2.展示視頻
槍打惡鬼 演示
總結
以上是生活随笔為你收集整理的3D游戏编程 作业五 枪打恶鬼(打飞碟)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Unity3d-打飞碟工厂模式
- 下一篇: Unity 3D-learning 打飞