Unity3D学习:射击小游戏——飞碟世界
游戲規則:游戲有三個關卡,每個關卡有四次發射機會,每次發射的飛碟大小顏色速度方向位置都不同,有50分初始分,每打中一個加10分,掉落一個在地減10分,第一二關都是90分過關,第三關70分過關,第一關每次發射一個飛碟,第二關兩個,第三關三個。空格發射飛碟,鼠標左鍵射擊飛碟。如圖(雖然UI還沒怎么優化過,將就一下了,還有沒有錄第三關也是為了讓gif短一點,因為上傳的gif有內存限制):
下面進入正題,怎么制作呢?
回收與再利用:首先這種涉及到類似于子彈啊,雨滴等物體的游戲,這種資源最好能回收與再利用,這樣就可以節約資源,省的一直創建新對象,別看創建一個對象對游戲影響不怎么大,在一些大型游戲中這是很重要的思想的,因為一個場景往往涉及到成千上萬的這種小物體,如果每一個都要創建一遍才能用,會導致卡頓,速度跟不上。
工廠模式:物體的生成都由一個工廠管理,需要什么只需要跟工廠說出你要什么,它會自己實現生成給你,里面如何生成不用你操心,實際上也有抽象的意思。就像這次的游戲,我數據設置好了,預設也傳給了工廠,所以在需要加載飛碟是,只用調用一個getDisk()就可以了,不需要操心怎么生成。當然回收與再利用也是在工廠中實現的,也就是回收倉庫也是在工廠里。
射線碰撞:用鼠標點擊屏幕,如何獲取我所點到的物體以及我的鼠標坐標呢?就是用從攝像機中發射出射線,然后鼠標點的位置作為方向,這樣就可以選中第一個被射擊中的物體,同時也可以記錄出鼠標所點位置。
下面是這游戲制作代碼的架構圖:
SceneController類:負責把各個類之間對接,作為內部設置與用戶界面的連接。
GameModel類:管理飛碟的基礎數據。
RoundController類:管理關卡數據,每一個關卡對應需要不同的飛碟數據,在這里會給飛碟根據不通關卡加載不同屬性。
Recorder類:管理記錄加分減分。
Userface類:管理用戶界面以及界面邏輯。
DiskFactory類:利用傳進來的飛碟預設根據需要生產飛碟存于倉庫,回收與再利用飛碟。
下面是代碼:
工廠類(看注釋應該就能看懂):
public class DiskFactory : System.Object
? ? {
? ? ? ? private static DiskFactory _instance;
? ? ? ? private GameObject prefabs_disk;
? ? ? ? public static List<GameObject> used = new List<GameObject>();
? ? ? ? public static List<GameObject> unused = new List<GameObject>();
? ? ? ? public static DiskFactory getInstance() ? ? ? ? ? ? //單例化工廠
? ? ? ? {
? ? ? ? ? ? if (_instance == null)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? _instance = new DiskFactory();
? ? ? ? ? ? }
? ? ? ? ? ? return _instance;
? ? ? ? }
? ? ? ? public void SetObject(GameObject x) ? ? //把預設傳入工廠
? ? ? ? {
? ? ? ? ? ? prefabs_disk = x;
? ? ? ? }
? ? ? ? public GameObject getDisk() //提供從工廠獲取飛碟的借口
? ? ? ? {
? ? ? ? ? ? GameObject t;
? ? ? ? ? ? if (unused.Count == 0)
? ? ? ? ? ? ? ? t = GameObject.Instantiate(prefabs_disk);
? ? ? ? ? ? else
? ? ? ? ? ? {
? ? ? ? ? ? ? ? t = unused[0];
? ? ? ? ? ? ? ? unused.Remove(t);
? ? ? ? ? ? }
? ? ? ? ? ? used.Add(t);
? ? ? ? ? ? return t;
? ? ? ? }
? ? ? ? public void free(GameObject t) ?//提供從外界回收飛碟到工廠的接口
? ? ? ? {
? ? ? ? ? ? t.GetComponent<Rigidbody>().velocity = Vector3.zero;
? ? ? ? ? ? t.transform.localScale = prefabs_disk.transform.localScale;
? ? ? ? ? ? t.SetActive(false);
? ? ? ? ? ? used.Remove(t);
? ? ? ? ? ? unused.Add(t);
? ? ? ? }
? ? }
接下來是SceneController類,它的工作實際上就是把各個類的功能合并起來,需要什么功能就它就調用某個類的功能來實現:
public class SceneController : System.Object, IQuery, IUserInterface
? ? {
? ? ? ? private static SceneController _instance;
? ? ? ? private RoundController _RC;
? ? ? ? private GameModel _gameModel;
? ? ? ? private Recorder _Recorder;
? ? ? ? public static SceneController getInstance() ? ? ? ? //單例化
? ? ? ? {
? ? ? ? ? ? if (_instance == null)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? _instance = new SceneController();
? ? ? ? ? ? }
? ? ? ? ? ? return _instance;
? ? ? ? }
? ? ? ? public void setdisk(GameObject t) ? ? ? ? ? ? ? //把飛碟預設傳入工廠
? ? ? ? {
? ? ? ? ? ? DiskFactory.getInstance().SetObject(t);
? ? ? ? }
? ? ? ? public int getRound()
? ? ? ? {
? ? ? ? ? ? return _RC.round;
? ? ? ? }
? ? ? ? public int getScore()
? ? ? ? {
? ? ? ? ? ? return _Recorder.score;
? ? ? ? }
? ? ? ? public void setGameModel(GameModel obj) { _gameModel = obj; }
? ? ? ? internal GameModel getGameModel() { return _gameModel; }
? ? ? ? public void setRecorder(Recorder obj) { _Recorder = obj; }
? ? ? ? internal Recorder getRecorder() { return _Recorder; }
? ? ? ? public void setRoundController(RoundController obj) { _RC = obj; }
? ? ? ? internal RoundController getRoundController() { return _RC; }
? ? ? ? public void emitDisk() { _gameModel.emitReady(); }
? ? ? ? public bool isShooting() { return _gameModel.shooting; }
? ? ? ? public void nextRound(int i) ? ? ? ? ? ?//進入下一關卡
? ? ? ? {
? ? ? ? ? ? if (i == 1 || i == 2)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? _Recorder.reset();
? ? ? ? ? ? ? ? _RC.round++;
? ? ? ? ? ? ? ? _RC.loadRoundData();
? ? ? ? ? ? }
? ? ? ? ? ? else if (i == 0) _RC.round = 0;
? ? ? ? }
? ? }
兩個用戶與SceneController的接口(在SceneController實現):
public interface IUserInterface ? ? ? ? //Scenecontroller與用戶界面的接口
? ? {
? ? ? ? void emitDisk();
? ? ? ? void setdisk(GameObject t);
? ? }
? ? public interface IQuery ? ? ? ? //Scenecontroller與用戶界面的接口
? ? {
? ? ? ? bool isShooting();
? ? ? ? int getRound();
? ? ? ? int getScore();
? ? }
以上三個部分我都放在一個自定義的namespace里面(name_game);
Userface用戶界面實現UI以及提供用戶接口邏輯:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using name_game;
public class UserInterface : MonoBehaviour {
? ? public int changes = 4; ? ? ? ? //發射機會
? ? public Text roundText; ? ? ? ? ?//顯示目前關卡數
? ? public Text scoreText; ? ? ? ? ?//顯示目前分數
? ? public Text mainText; ? ? ? ? ?//主顯示板,顯示規則以及提示
? ? public Text changeText; ? ? ? ? //顯示目前剩余發射機會
? ? public Camera ca;
? ? private int states = 0; ? ? ?//0為初始界面,1為規則界面,2為游戲中?
? ? private IUserInterface userInt; ? ? // 用戶接口 ?
? ? private IQuery queryInt; ? ? ?// 查詢接口 ?
? ? public GameObject disktem;
? ? public ParticleSystem _exposion; ? ?//爆炸粒子
? ? void Start()
? ? {
? ? ? ? userInt = SceneController.getInstance() as IUserInterface;
? ? ? ? queryInt = SceneController.getInstance() as IQuery;
? ? ? ? _exposion = GameObject.Instantiate(_exposion) as ParticleSystem;
? ? ? ? changes = 4;
? ? ? ? states = 0;
? ? ? ? userInt.setdisk(disktem);
? ? ? ? mainText.text = "歡迎來到飛碟世界,請按空格進入游戲";
? ? }
? ? void Update()
? ? {
? ? ? ? if ((queryInt.getRound() == 1 || queryInt.getRound() == 2 )&& changes == 0 && !queryInt.isShooting()) //判斷一二關的結果
? ? ? ? {
? ? ? ? ? ? if (queryInt.getScore() >= 90)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? mainText.text = "You win!(空格進入下一關)";
? ? ? ? ? ? ? ? if (Input.GetKeyDown("space"))
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? states = 1;
? ? ? ? ? ? ? ? ? ? changes = 4;
? ? ? ? ? ? ? ? ? ? SceneController.getInstance().nextRound(1);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? {
? ? ? ? ? ? ? ? mainText.text = "You lose!(空格重新開始)";
? ? ? ? ? ? ? ? if (Input.GetKeyDown("space"))
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? states = 0;
? ? ? ? ? ? ? ? ? ? changes = 4;
? ? ? ? ? ? ? ? ? ? SceneController.getInstance().nextRound(0);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (queryInt.getRound() == 3 && changes == 0 && !queryInt.isShooting()) //判斷第三關的結果
? ? ? ? {
? ? ? ? ? ? if (queryInt.getScore() >= 70) mainText.text = "You win!You have passed all arounds(空格重新開始游戲)";
? ? ? ? ? ? else mainText.text = "You lose!(空格重新開始)";
? ? ? ? ? ? if (Input.GetKeyDown("space"))
? ? ? ? ? ? {
? ? ? ? ? ? ? ? states = 0;
? ? ? ? ? ? ? ? changes = 4;
? ? ? ? ? ? ? ? SceneController.getInstance().nextRound(0);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? changeText.text = "Changes: " + changes.ToString();
? ? ? ? if (changes>0&&Input.GetKeyDown("space")) {
? ? ? ? ? ? if(states==0)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? states = 1;
? ? ? ? ? ? ? ? if(queryInt.getRound()==0) SceneController.getInstance().nextRound(1);
? ? ? ? ? ? ? ? mainText.text = "你有50初始分,每一關有四次發射機會,每次發射射出的飛碟數量以及速度還有大小顏色都不同,每一輪發射中每一個飛碟掉落在地都會扣10分,打中一個加10分,第一二關90分以上過關第三關70分以上過關,空格發射飛碟,鼠標左鍵射擊,請按空格進入第一關";
? ? ? ? ? ? } else if(states==1)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? states = 2;
? ? ? ? ? ? ? ? mainText.text = "";
? ? ? ? ? ? } else if(states==2) ? ? ? ? ? ?//這個狀態下才能發射
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if ((!queryInt.isShooting())&&changes!=0)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? userInt.emitDisk();
? ? ? ? ? ? ? ? ? ? changes--;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if(queryInt.isShooting()&&Input.GetMouseButtonDown(0)) ? ? ?//判斷是否射擊中目標,并且實現爆炸粒子效果
? ? ? ? {
? ? ? ? ? ? Ray ray = ca.ScreenPointToRay(Input.mousePosition);
? ? ? ? ? ? RaycastHit hit;
? ? ? ? ? ? if (Physics.Raycast(ray, out hit) && hit.collider.gameObject.tag == "disk")
? ? ? ? ? ? {
? ? ? ? ? ? ? ? _exposion.transform.position = hit.collider.gameObject.transform.position;
? ? ? ? ? ? ? ? _exposion.GetComponent<Renderer>().material.color = hit.collider.gameObject.GetComponent<Renderer>().material.color;
? ? ? ? ? ? ? ? _exposion.Play();
? ? ? ? ? ? ? ? hit.collider.gameObject.SetActive(false);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? roundText.text = "Round: " + queryInt.getRound().ToString();
? ? ? ? scoreText.text = "Score: " + queryInt.getScore().ToString();
? ? }
}
Recorder類:
public class Recorder : MonoBehaviour {
? ? public int score = 50; ? ? ? ? ?//記錄當前所得分數
? ? void Awake () {
? ? ? ? SceneController.getInstance().setRecorder(this);
? ? }
? ? public void scoreADisk() ? ? ? ?//得分
? ? {
? ? ? ? score += 10;
? ? }
? ? public void reset() ? ? ? ? ? ? //重置
? ? {
? ? ? ? score = 50;
? ? }
? ? public void failADisk() ? ? ? ? // 失分 ?
? ? {
? ? ? ? score -= 10;
? ? }
}
RoundController類:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using name_game;
public class RoundController : MonoBehaviour {
? ? private Color color;
? ? private Vector3 emitPos;
? ? private Vector3 emitDir;
? ? private float speed;
? ? public int round = 0;
? ? void Awake()
? ? {
? ? ? ? SceneController.getInstance().setRoundController(this);
? ? }
? ? void Start () {
? ? ? ? emitPos = new Vector3(0f, 5f, -6f);
? ? ? ? emitDir = new Vector3(10f, 15f, 70f);
? ? ? ? speed = 15;
? ? }
? ? public void loadRoundData() ? ? ? ? ? ? ? ? //加載每一關卡飛碟數據
? ? {
? ? ? ? if(round==1)
? ? ? ? {
? ? ? ? ? ? color = Color.black;
? ? ? ? ? ? SceneController.getInstance().getGameModel().setting(3, color, emitPos, emitDir.normalized, speed, 1);
? ? ? ? } else
? ? ? ? ? ? if(round==2)
? ? ? ? {
? ? ? ? ? ? color = Color.red;
? ? ? ? ? ? SceneController.getInstance().getGameModel().setting(2, color, emitPos, emitDir.normalized, speed, 2);
? ? ? ? } else if(round==3)
? ? ? ? {
? ? ? ? ? ? color = Color.green;
? ? ? ? ? ? SceneController.getInstance().getGameModel().setting(2, color, emitPos, emitDir.normalized, speed, 3);
? ? ? ? }
? ? }
}
GameModel類:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using name_game;
public class GameModel : MonoBehaviour
{
? ? public bool shooting=false; ? ? ? ? ? ? //標記飛碟飛行期間的狀態
? ? private int diskScale; ? ? ? ? ? ? ? ? ?//飛碟大小
? ? private Color diskColor; ? ? ? ? ? ? ? ?//飛碟顏色
? ? private Vector3 emitPosition; ? ? ? ? ? // 發射位置 ?
? ? private Vector3 emitDirection; ? ? ? ? ?// 發射方向 ?
? ? private float emitSpeed = 0; ? ? ? ? ? ? ? ?// 發射速度 ?
? ? private int emitNumber; ? ? ? ? ? ? ? ? // 發射數量 ?
? ? private bool emitEnable;
? ? private List<GameObject> disksToEmit = new List<GameObject>();
? ? void Awake()
? ? {
? ? ? ? SceneController.getInstance().setGameModel(this);
? ? }
? ? public void setting(int scale, Color color, Vector3 emitPos, Vector3 emitDir, float speed, int num)
? ? {
? ? ? ? diskScale = scale;
? ? ? ? diskColor = color;
? ? ? ? emitPosition = emitPos;
? ? ? ? emitDirection = emitDir;
? ? ? ? emitSpeed = speed;
? ? ? ? emitNumber = num;
? ? }
? ? public void emitReady() ? ? ? ? ? ? //判斷是否可以發射
? ? {
? ? ? ? if (!shooting)
? ? ? ? {
? ? ? ? ? ? emitEnable = true;
? ? ? ? }
? ? }
? ? void emitDisks() ? ? ? ? ? ? ? ?//從工廠生產或者取出飛碟并且裝入實現發射邏輯
? ? {
? ? ? ? for (int i = 0; i < emitNumber; ++i) ? ?//每一個飛碟發射前都隨機一次方向和發射位置
? ? ? ? {
? ? ? ? ? ? disksToEmit.Add(DiskFactory.getInstance().getDisk());
? ? ? ? ? ? disksToEmit[i].transform.localScale *= diskScale;
? ? ? ? ? ? disksToEmit[i].GetComponent<Renderer>().material.color = diskColor;
? ? ? ? ? ? disksToEmit[i].transform.position = new Vector3(Random.Range(emitPosition.x-1f, emitPosition.x+1f), emitPosition.y, emitPosition.z);
? ? ? ? ? ? disksToEmit[i].SetActive(true);
? ? ? ? ? ? emitDirection = new Vector3(Random.Range(emitDirection.x-0.3f, emitDirection.x + 0.3f), emitDirection.y, emitDirection.z);
? ? ? ? ? ? disksToEmit[i].GetComponent<Rigidbody>().velocity = emitDirection * Random.Range(emitSpeed, emitSpeed+5f);
? ? ? ? }
? ? }
? ? void freeDisk(GameObject adisk) ? ? ? ? ? ? //調用工廠來回收飛碟
? ? {
? ? ? ? DiskFactory.getInstance().free(adisk);
? ? ? ? disksToEmit.Remove(adisk);
? ? }
? ? void FixedUpdate()
? ? {
? ? ? ? if (emitEnable)
? ? ? ? {
? ? ? ? ? ? emitDisks(); // 發射飛碟 ?
? ? ? ? ? ? emitEnable = false;
? ? ? ? ? ? shooting = true;
? ? ? ? }
? ? }
? ? void Update()
? ? {
? ? ? ? for (int i = 0; i < disksToEmit.Count; ++i) ? ? ? ? //實現判斷是否射擊中
? ? ? ? {
? ? ? ? ? ? if (!disksToEmit[i].activeInHierarchy)
? ? ? ? ? ? { ?// 飛碟不在場景中 ?
? ? ? ? ? ? ? ? SceneController.getInstance().getRecorder().scoreADisk(); ?// 得分 ?
? ? ? ? ? ? ? ? freeDisk(disksToEmit[i]);
? ? ? ? ? ? }
? ? ? ? ? ? else if (disksToEmit[i].transform.position.y < 0)
? ? ? ? ? ? { ? ?// 飛碟在場景中但落地 ?
? ? ? ? ? ? ? ? SceneController.getInstance().getRecorder().failADisk(); ? // 失分 ?
? ? ? ? ? ? ? ? freeDisk(disksToEmit[i]);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (disksToEmit.Count == 0)
? ? ? ? {
? ? ? ? ? ? shooting = false;
? ? ? ? }
? ? }
}
還有一點就是要給飛碟的預設就如剛體組件,在Add Component->physics->Rigidbody就可以了
Text的話可以create一個Canvas,在里面創建并且調整各個提示框的位置以及大小
粒子系統就直接create->Particle System創建,然后樣式再根據自己需要調一下。這次的游戲就大功告成了~~~
總結
以上是生活随笔為你收集整理的Unity3D学习:射击小游戏——飞碟世界的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 交互入门——基于鼠标控制的射击飞碟小游戏
- 下一篇: 游戏开发之Unity学习(五)——鼠标打