3D游戏编程 作业五 枪打恶鬼(打飞碟)
前言
這次的作業(yè)個(gè)人感覺(jué)挺好玩的,我給做成了射擊游戲。同時(shí)我也在其中學(xué)到了unity的一些有趣應(yīng)用, 下面先把大的框架給講一下,然后再著重講一下我個(gè)人的收獲吧。Asset文件:github
參考博客:學(xué)長(zhǎng)博客
一、任務(wù)要求
游戲規(guī)則(自定義)
控制你的超進(jìn)化版鼠標(biāo)槍射擊飛行玩偶(飛碟),擊中玩偶或者放任玩偶下落都會(huì)有相應(yīng)的得分,得分如表。游戲共十回合,隨回合數(shù)增加,玩偶數(shù)跟飛行速度增加。若得分為負(fù),游戲失敗;若回合進(jìn)行完畢,游戲勝利。
| 蝙蝠 | 5 | -2 |
| 惡鬼 | 10 | -2 |
| 軟泥怪 | 20 | -2 |
| 兔子 | -10 | 5 |
二、 項(xiàng)目展示
這里不得不提一下,我特別喜歡作業(yè)三的mvc框架,只需三個(gè)文件(Model、Controllor、UserGUI)就可以將結(jié)構(gòu)講明白。所以我這次也是在那個(gè)框架的基礎(chǔ)上進(jìn)行修改的。
1.mvc架構(gòu)
Model 模型(其實(shí)把大部分類都給匯總了)
這里的導(dǎo)演類等都是以前作業(yè)內(nèi)容就不再贅述
using System.Collections; using System.Collections.Generic; using UnityEngine;namespace mygame {public interface ISceneController //加載場(chǎng)景public interface IUserAction //用戶互動(dòng)會(huì)發(fā)生的事件public class SSDirector : System.Object//導(dǎo)演類public class ScoreController : System.Object{//分?jǐn)?shù)控制器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(){//規(guī)則展示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), "擊敗惡魔,保護(hù)小兔子");GUI.Label(new Rect(Screen.width / 2 - 150, 30, 350, 50), "子彈打中惡魔得分,讓惡魔逃跑丟分,打中小兔子丟分");GUI.Label(new Rect(Screen.width / 2 - 85, 50, 250, 50), "點(diǎn)擊屏幕進(jìn)行射擊");}//游戲準(zhǔn)備if (sign == 0){if (GUI.Button (new Rect (Screen.width / 2 - 80, Screen.height / 2, 160, 20), "開(kāi)始游戲")){action.gamestart();}}//游戲中else if(sign == 1){string say = "當(dāng)前回合回合數(shù)為" + action.getround().ToString() + ",分?jǐn)?shù)為" + ScoreController.GetScoreController().getscore().ToString() +"分";GUI.Box (new Rect (Screen.width / 2 - 100, 70, 200,30), say);}//游戲結(jié)束else if (sign == 2||sign == 3){string say;if(sign == 2)say = "你輸了,一共堅(jiān)持了" + 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), "重開(kāi)")){action.gamestart();sign = 1;}}} }場(chǎng)記 Controllor
其實(shí)這里再寫(xiě)一個(gè)游戲控制的類,或者把游戲規(guī)則丟進(jìn)分?jǐn)?shù)控制器里應(yīng)該會(huì)好一點(diǎn),但是本人有點(diǎn)懶,就把場(chǎng)景控制全丟這了
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 (){//設(shè)置導(dǎo)演SSDirector director = SSDirector.GetInstance();//設(shè)置場(chǎng)景director.CurrentScenceController = this;//新建Viewuser_gui = gameObject.AddComponent<UserGUI>() as UserGUI;//設(shè)置分?jǐn)?shù)控制器scoreController = ScoreController.GetScoreController();//設(shè)置飛碟工廠diskFactory =DiskFactory.getFactory();//設(shè)置飛碟模型diskModels = diskFactory.GetDiskModels();//設(shè)置隨機(jī)種子rand = new System.Random((int) System.DateTime.Now.Ticks & 0x0000FFFF);LoadResources();} 這里稍微講一下上面倒數(shù)第二行。這個(gè)隨機(jī)搞了我半天,因?yàn)檫@里的C#調(diào)用了unity的庫(kù),會(huì)出現(xiàn)重載等情況, 導(dǎo)致一些常用的類諸如DateTime、Random不能跟平常一樣使用。這時(shí)候我們可以通過(guò)在類前面添加System來(lái)進(jìn) 行調(diào)用。然后在使用C#隨機(jī)數(shù)時(shí)存在偽隨機(jī)數(shù)的情況,這里用時(shí)間做種子避免了。然后unity本身也是用random的,不過(guò) 用法跟原來(lái)得就不一樣了,有無(wú)偽隨機(jī)我沒(méi)有試過(guò)。最后,下面的代碼無(wú)縫銜接上文 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();//檢查是否分?jǐn)?shù)為負(fù)數(shù)user_gui.sign = Check();if(user_gui.sign==1&&diskModels.Count==0){//當(dāng)前回合結(jié)束,增加回合并發(fā)射飛碟round++;if(round<=10)sentdisk();}//檢查是否超過(guò)十局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";}//網(wǎng)上下好的特效預(yù)設(shè)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;//飛碟數(shù)量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(){//檢測(cè)游戲狀態(tài)if(scoreController.getscore()<0)return 2;if(round>10)return 3;return 1;}public int getround(){return round;} }2.分?jǐn)?shù)控制器、飛碟工廠和飛碟模型
ScoreController 分?jǐn)?shù)控制器
public class ScoreController : System.Object{//分?jǐn)?shù)控制器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 飛碟工廠
這里的實(shí)現(xiàn)是參考學(xué)長(zhǎng)博客的,個(gè)人覺(jué)得把基本的邏輯都實(shí)現(xiàn)了,但不是很清楚具體實(shí)現(xiàn)是不是這樣。
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;//飛碟種類隨機(jī)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 飛碟模型
這里首先開(kāi)了個(gè)靜態(tài)數(shù)組,存儲(chǔ)了累死累活測(cè)出來(lái)的可行的位置、施加力、扭矩力對(duì)應(yīng)數(shù)組。
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);}這里對(duì)飛碟運(yùn)動(dòng)的處理是我這次很受益的地方,本來(lái)我已經(jīng)寫(xiě)好了自制的move類了,但是因?yàn)閿?shù)據(jù)的原因,會(huì)顯得很僵硬。然后回頭一看學(xué)長(zhǎng)博客,只能說(shuō)這個(gè)力的運(yùn)用秒不可言。其中扭矩力的添加,單純是我覺(jué)得物體轉(zhuǎn)起來(lái)會(huì)更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>();//啟動(dòng)剛體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);//添加旋轉(zhuǎn)力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);}}最后這里對(duì)飛碟剛體的處理更讓我大為嘆服,把物體的剛體運(yùn)算關(guān)掉,再放到看不到的角落,確實(shí)是個(gè)好方法
3.槍與子彈的實(shí)現(xiàn)
這一部分,我學(xué)會(huì)了如何讓槍口隨鼠標(biāo)移動(dòng),子彈發(fā)射,與子彈碰撞這三點(diǎn)。個(gè)人覺(jué)得這應(yīng)該是游戲制作中挺還是實(shí)用的。
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;}技巧一 :通過(guò)腳本來(lái)找到子對(duì)象
獲得對(duì)象的transform后可以通過(guò)GetChild(index)來(lái)找到對(duì)應(yīng)下標(biāo)的子對(duì)象,transform的gameobject可以直接找到對(duì)應(yīng)對(duì)象。
技巧二 鼠標(biāo)移動(dòng)調(diào)動(dòng)物體旋轉(zhuǎn) :
這里我們可以通過(guò)Input.mousePosition來(lái)不斷獲得鼠標(biāo)焦點(diǎn)位置,而通過(guò)記錄鼠標(biāo)位置的變化,我們可以通過(guò)同步移動(dòng)物體的rotation來(lái)實(shí)現(xiàn)調(diào)動(dòng)旋轉(zhuǎn)的效果。其中-0.1f相當(dāng)于靈敏度與方向,可自行調(diào)整
技巧三 子彈射擊 :
首先我們?cè)跇尶谶@里設(shè)置好空對(duì)象,這樣我們就可以通過(guò)找到預(yù)設(shè)子對(duì)象的位置來(lái)找到槍口。
然后是transform.TransformDirection,這個(gè)方法是將局部坐標(biāo)轉(zhuǎn)為世界坐標(biāo),說(shuō)人話就是你走路只管前后左右(你當(dāng)前rotation的相對(duì)坐標(biāo)),這個(gè)方法會(huì)自動(dòng)幫你轉(zhuǎn)成東南西北(屏幕里看到的世界坐標(biāo),即絕對(duì)坐標(biāo))
子彈碰撞實(shí)現(xiàn)
兩者使用碰撞體
其中實(shí)現(xiàn)碰撞(要添加腳本的)勾選Is Trigger
腳本實(shí)現(xiàn)的函數(shù)
添加腳本如下
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;}}}其中我們需要修改被碰撞(沒(méi)有腳本)的游戲?qū)ο蟮膖ag,如下
三、 展示效果
1.展示圖
2.展示視頻
槍打惡鬼 演示
總結(jié)
以上是生活随笔為你收集整理的3D游戏编程 作业五 枪打恶鬼(打飞碟)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Unity3d-打飞碟工厂模式
- 下一篇: Unity 3D-learning 打飞