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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

Unity3D学习笔记(6)—— 飞碟射击游戏

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

游戲規(guī)則:

???????? 游戲分多個回合,每個回合有N個飛碟,玩家按空格后,321倒數(shù)3秒,飛碟飛出,點擊鼠標,子彈飛出。飛碟落地或被擊中,則準備下一次射擊。每回合飛碟的大小、顏色、發(fā)射位置、發(fā)射角度、每次發(fā)射的數(shù)量可以變化。

游戲效果:


? ? ? ? 錄了好幾次,每次飛碟都打不中啊!_(:з」∠)_

游戲類圖:


代碼說明:

一、飛碟回收工廠類(DiskFactory)

? ? ? ? 創(chuàng)建新的命名空間Com.Mygame,單例類DiskFactory和SceneController都定義其中。飛碟工廠類的目的是管理飛碟實例,同時對外屏蔽飛碟實例的的提取和回收細節(jié),對于需要使用飛碟的其他對象,只能使用工廠類提供的3個函數(shù),分別是getDisk()、getDiskObject()、free()。

using UnityEngine; using System.Collections; using System.Collections.Generic; using Com.Mygame;namespace Com.Mygame {public class DiskFactory : System.Object {private static DiskFactory _instance;private static List<GameObject> diskList; // 飛碟隊列public GameObject diskTemplate; // 預設對象public static DiskFactory getInstance() {if (_instance == null) {_instance = new DiskFactory();diskList = new List<GameObject>();}return _instance;}// 獲取可用飛碟idpublic int getDisk() { for (int i = 0; i < diskList.Count; ++i) {if (!diskList[i].activeInHierarchy) {return i; // 飛碟空閑}}// 無空閑飛碟,則實例新的飛碟預設diskList.Add(GameObject.Instantiate(diskTemplate) as GameObject);return diskList.Count-1;}// 獲取飛碟對象public GameObject getDiskObject(int id) {if (id > -1 && id < diskList.Count) {return diskList[id];}return null;}// 回收飛碟public void free(int id) {if (id > -1 && id < diskList.Count) {// 重置飛碟速度diskList[id].rigidbody.velocity = Vector3.zero;// 重置飛碟大小diskList[id].transform.localScale = diskTemplate.transform.localScale;diskList[id].SetActive(false);}}} }public class DiskFactoryBC : MonoBehaviour {public GameObject disk;void Awake () {// 初始化預設對象DiskFactory.getInstance().diskTemplate = disk;} }

有幾點需要注意:

1)? 當且僅當請求隊列里的所有對象都在被使用(飛碟在場景中活躍)時,才會發(fā)生實例化,此時隊列會變長。

2)? getDisk返回的是可用飛碟在隊列里的index,這是為了方便free。

3)? free通過index找到飛碟在隊列中的位置,并將飛碟設置為不活躍的。注意,由于飛碟使用了剛體組件,回收時需要把速度重置,并且大小可能會被改變,也應該重置。


二、用戶界面類(UserInterface)

? ? ? ? 用戶界面有兩大功能,一是處理用戶鍵入,二是顯示得分和倒計時等。用戶鍵入有兩種:鼠標左鍵和空格。左鍵發(fā)射子彈,空格發(fā)射飛碟。顯示有三種:得分、回合和倒計時。

? ? ? ? 子彈射擊的思路:當用戶點擊鼠標時,從攝像機到鼠標創(chuàng)建一條射線,射線的方向即是子彈發(fā)射的方向,子彈采用剛體組件,因此發(fā)射子彈只需要給子彈施加一個力。子彈對象只有一個,下一次發(fā)射子彈時,必須改變子彈的位置(雖然有了剛體組件不建議修改transform,但也沒有其它方法改變子彈位置了吧)。為了不讓子彈繼承上一次發(fā)射的速度,必須將子彈的速度歸零重置。

? ? ? ? 子彈的擊中判斷:采用射線而不是物理引擎,因為物理引擎在高速物體碰撞時經(jīng)常不能百分百檢測得到。

? ? ? ? 發(fā)射飛碟的思路:調用用戶接口。

? ? ? ? 顯示的思路:得分和回合直接通過查詢接口獲得。倒計時顯示前通過查詢接口判斷是否正在倒計時,如果是,那么再通過查詢接口獲得倒計時時間。如果回合發(fā)生改變,則顯示新的回合,直到用戶按下空格。

using UnityEngine; using UnityEngine.UI; using System.Collections; using Com.Mygame;public class UserInterface : MonoBehaviour {public Text mainText; // 顯示主提示:倒計時、新回合public Text scoreText; // 顯示得分 public Text roundText; // 顯示回合private int round; // 當前回合public GameObject bullet; // 子彈public ParticleSystem explosion; // 爆炸粒子public float fireRate = .25f; // 開槍間隔public float speed = 500f; // 子彈速度private float nextFireTime; // 下一次開槍時間private IUserInterface userInt; // 用戶接口private IQueryStatus queryInt; // 查詢接口void Start() {bullet = GameObject.Instantiate(bullet) as GameObject;explosion = GameObject.Instantiate(explosion) as ParticleSystem;userInt = SceneController.getInstance() as IUserInterface;queryInt = SceneController.getInstance() as IQueryStatus;}void Update () {if (queryInt.isCounting()) {// 顯示倒計時mainText.text = ((int)queryInt.getEmitTime()).ToString();}else {if (Input.GetKeyDown("space")) {userInt.emitDisk(); // 發(fā)射飛碟}if (queryInt.isShooting()) {mainText.text = ""; // 射擊開始,隱藏主提示}// 發(fā)射子彈if (queryInt.isShooting() && Input.GetMouseButtonDown(0) && Time.time > nextFireTime ) {nextFireTime = Time.time + fireRate;Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); // 攝像機到鼠標射線bullet.rigidbody.velocity = Vector3.zero; // 子彈剛體速度重置bullet.transform.position= transform.position; // 子彈從攝像機位置射出bullet.rigidbody.AddForce(ray.direction*speed, ForceMode.Impulse);RaycastHit hit;if (Physics.Raycast(ray, out hit) && hit.collider.gameObject.tag == "Disk") {// 播放爆炸粒子特效explosion.transform.position = hit.collider.gameObject.transform.position;explosion.renderer.material.color = hit.collider.gameObject.renderer.material.color;explosion.Play();// 擊中飛碟設置為不活躍,自動回收hit.collider.gameObject.SetActive(false);}}}roundText.text = "Round: " + queryInt.getRound().ToString();scoreText.text = "Score: " + queryInt.getPoint().ToString();// 如果回合更新,主提示顯示新回合if (round != queryInt.getRound()) {round = queryInt.getRound();mainText.text = "Round " + round.ToString() + " !";}} }

三、場景控制器類(SceneController)

? ? ? ? 場景控制類主要實現(xiàn)接口定義和保存注入對象。另外它有兩個私有變量round和point,分別記錄游戲正在進行的回合,以及玩家目前的得分。

using UnityEngine; using System.Collections; using Com.Mygame;namespace Com.Mygame {public interface IUserInterface {void emitDisk();}public interface IQueryStatus {bool isCounting();bool isShooting();int getRound();int getPoint();int getEmitTime();}public interface IJudgeEvent {void nextRound();void setPoint(int point);}public class SceneController : System.Object, IQueryStatus, IUserInterface, IJudgeEvent {private static SceneController _instance;private SceneControllerBC _baseCode;private GameModel _gameModel;private Judge _judge;private int _round;private int _point;public static SceneController getInstance() {if (_instance == null) {_instance = new SceneController();}return _instance;}public void setGameModel(GameModel obj) { _gameModel = obj; }internal GameModel getGameModel() { return _gameModel; }public void setJudge(Judge obj) { _judge = obj; }internal Judge getJudge() { return _judge; }public void setSceneControllerBC(SceneControllerBC obj) { _baseCode = obj; }internal SceneControllerBC getSceneControllerBC() { return _baseCode; }// 操作接口public void emitDisk() { _gameModel.prepareToEmitDisk(); }// 查詢接口public bool isCounting() { return _gameModel.isCounting(); }public bool isShooting() { return _gameModel.isShooting(); }public int getRound() { return _round; }public int getPoint() { return _point; }public int getEmitTime() { return (int)_gameModel.timeToEmit + 1; }// 得分接口public void setPoint(int point) { _point = point; }public void nextRound() { _point = 0; _baseCode.loadRoundData(++_round); }} }

四、關卡類(SceneControllerBC)

? ? ? ? 關卡類保存了各個round飛碟的大小、顏色、發(fā)射位置、發(fā)射角度、發(fā)射數(shù)量等信息。它只有一個函數(shù)loadRoundData(),用來初始化游戲場景。

public class SceneControllerBC : MonoBehaviour {private Color color;private Vector3 emitPos;private Vector3 emitDir;private float speed;void Awake() {SceneController.getInstance().setSceneControllerBC(this);}public void loadRoundData(int round) {switch(round) {case 1: // 第一關color = Color.green;emitPos = new Vector3(-2.5f, 0.2f, -5f);emitDir = new Vector3(24.5f, 40.0f, 67f);speed = 4;SceneController.getInstance().getGameModel().setting(1, color, emitPos, emitDir.normalized, speed, 1);break;case 2: // 第二關color = Color.red;emitPos = new Vector3(2.5f, 0.2f, -5f);emitDir = new Vector3(-24.5f, 35.0f, 67f);speed = 4;SceneController.getInstance().getGameModel().setting(1, color, emitPos, emitDir.normalized, speed, 2);break;}} }

五、游戲規(guī)則類(Judge)

? ? ? ? 游戲規(guī)則單獨作為一個類,有利于日后修改。這里需要處理的規(guī)則無非就兩個,得分和失分。另外,得分需要判斷是否能晉級下一關。能就調用接口函數(shù)nextRound()。

using UnityEngine; using System.Collections; using Com.Mygame;public class Judge : MonoBehaviour {public int oneDiskScore = 10;public int oneDiskFail = 10;public int disksToWin = 4;private SceneController scene;void Awake() {scene = SceneController.getInstance();scene.setJudge(this);}void Start() {scene.nextRound(); // 默認開始第一關}// 擊中飛碟得分public void scoreADisk() {scene.setPoint(scene.getPoint() + oneDiskScore);if (scene.getPoint() == disksToWin*oneDiskScore) {scene.nextRound();}}// 掉落飛碟失分public void failADisk() {scene.setPoint(scene.getPoint() - oneDiskFail);} }

六、游戲場景類(GameModel)

? ? ? ? 場景類是整個飛碟射擊游戲的核心類,主要負責飛碟動作的處理。我是這樣設計的:首先需要倒計時功能,可以通過幾個整型變量和布爾變量完成。另外需要飛碟發(fā)射功能,通過setting函數(shù)保存好飛碟的發(fā)射信息,每次倒計時完成后,通過emitDisks獲取飛碟對象,并通過發(fā)射信息初始化飛碟,再給飛碟一個力就可以發(fā)射了。而飛碟的回收在Update里完成,一種是飛碟被擊中(飛碟不在場景中)了,需要調用Judge獲得分數(shù)。另一種是飛碟在場景中,但是掉在地上了,需要調用Judge丟失分數(shù)。

using UnityEngine; using System.Collections; using System.Collections.Generic; using Com.Mygame;public class GameModel : MonoBehaviour {public float countDown = 3f; // 飛碟發(fā)射倒計時總時間public float timeToEmit; // 飛碟發(fā)射倒計時剩余時間private bool counting; // 正在倒計時private bool shooting; // 正在射擊public bool isCounting() { return counting; }public bool isShooting() { return shooting; }private List<GameObject> disks = new List<GameObject>(); // 發(fā)射的飛碟對象列表private List<int> diskIds = new List<int>(); // 發(fā)射的飛碟id列表private int diskScale; // 飛碟大小private Color diskColor; // 飛碟顏色private Vector3 emitPosition; // 發(fā)射位置private Vector3 emitDirection; // 發(fā)射方向private float emitSpeed; // 發(fā)射速度private int emitNumber; // 發(fā)射數(shù)量private bool emitEnable; // 允許新的發(fā)射事件private SceneController scene;void Awake () {scene = SceneController.getInstance();scene.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;}// 準備下一次發(fā)射public void prepareToEmitDisk() {if (!counting && !shooting) {timeToEmit = countDown;emitEnable = true;}}// 發(fā)射飛碟void emitDisks() {for (int i = 0; i < emitNumber; ++i) {diskIds.Add(DiskFactory.getInstance().getDisk());disks.Add(DiskFactory.getInstance().getDiskObject(diskIds[i]));disks[i].transform.localScale *= diskScale;disks[i].renderer.material.color = diskColor;disks[i].transform.position = new Vector3(emitPosition.x, emitPosition.y+i, emitPosition.z);disks[i].SetActive(true);disks[i].rigidbody.AddForce(emitDirection*Random.Range(emitSpeed*5, emitSpeed*10)/10, ForceMode.Impulse);}}// 回收飛碟void freeADisk(int i) {DiskFactory.getInstance().free(diskIds[i]);disks.RemoveAt(i);diskIds.RemoveAt(i);}void FixedUpdate() {if (timeToEmit > 0) {counting = true;timeToEmit -= Time.deltaTime;} else {counting = false;if (emitEnable) {emitDisks(); // 發(fā)射飛碟emitEnable = false;shooting = true;}}}void Update () {for (int i = 0; i < disks.Count; ++i) {if (!disks[i].activeInHierarchy) { // 飛碟不在場景中scene.getJudge().scoreADisk(); // 得分freeADisk(i);}else if (disks[i].transform.position.y < 0) { // 飛碟在場景中但落地scene.getJudge().failADisk(); // 失分freeADisk(i);}}if (disks.Count == 0) {shooting = false;}} }

其他說明:

? ? ? ? 場景中預置的所有對象:

一、攝像機

? ? ? ? 可將所有腳本掛載在攝像機上。攝像機的參數(shù):

二、預設

? ? ? ? 子彈(Bullet):


? ? ? ? 飛碟(Disk):


? ? ? ? 粒子(Particle System):


? ? ? ? 地板材質:

三、文字

? ? ? ? 在場景編輯器里右鍵創(chuàng)建UI元素Text,調整 text 對齊位置和文字大小即可。


四、腳本設置

? ? ? ? 最后,將5個腳本掛載在組攝像機上,將預設拖入相應的腳本中:

? ? ? ? OK!游戲完成了!

? ? ? ? 我的制作順序是這樣子的,游戲開始的時候先不忙著寫腳本,把預設弄好,包括攝像機角度,粒子效果等。寫腳本先寫子彈射擊,然后寫飛碟工廠,再寫游戲場景,這時候就已經(jīng)有了一個小Demo可以玩了。然后添加場景控制類,完善用戶界面,使得可以顯示回合和分數(shù)等信息。最后寫關卡類和規(guī)則類。





總結

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

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