Unity游戏设计之飞碟游戏
編制一個(gè)射飛碟游戲。
具體要求如下:
1 假設(shè)有一支槍在攝像機(jī)位置(0,1,-10),在(0,0,0-10-20)放置三個(gè)小球作為距離標(biāo)記,調(diào)整視角直到小球在下中部
2 將鼠標(biāo)所在平面坐標(biāo),轉(zhuǎn)換為子彈(球體)射出的角度方向。子彈使用物理引擎,初速度恒定。(U3d 坐標(biāo)變換: http://www.cnblogs.com/tekkaman/p/3809409.html )
?
Vector3 mp = Input.mousePosition; //get Screen Position
print (mp.ToString());
?
Vector3 mp1 = cam.camera.ScreenToViewportPoint (mp);
mp1.z = 10; //距攝像頭 10 位置立面
mp1 = cam.camera.ViewportToWorldPoint (mp1);
print (mp1.ToString());
?
3 游戲要分多個(gè) round , 飛碟數(shù)量每個(gè) round 都是 n 個(gè),但色彩,大小;發(fā)射位置,速度,角度,每次發(fā)射數(shù)量按預(yù)定規(guī)則變化。
4 用戶按空格后,321倒數(shù)3秒,飛碟飛出(物理引擎控制),點(diǎn)擊鼠標(biāo),子彈飛出。飛碟落地,或被擊中,則準(zhǔn)備下一次射擊。
5 以下是一些技術(shù)要求:
? 子彈僅需要一個(gè),不用時(shí)處于 deactive 狀態(tài)
? 飛碟用一個(gè)帶緩存的工廠生產(chǎn),template 中放置預(yù)制的飛碟對(duì)象
? 程序類圖設(shè)計(jì)大致如下:
?
具體實(shí)現(xiàn):
腳本實(shí)現(xiàn)子彈射擊
腳本掛在在攝像機(jī)上
子彈射擊的思路:當(dāng)用戶點(diǎn)擊鼠標(biāo)時(shí),從攝像機(jī)到鼠標(biāo)創(chuàng)建一條射線,射線的方向即是子彈發(fā)射的方向,子彈采用剛體組件,因此發(fā)射子彈只需要給子彈施加一個(gè)力。子彈對(duì)象只有一個(gè),下一次發(fā)射子彈時(shí),必須改變子彈的位置(雖然有了剛體組件不建議修改transform,但也沒有其它方法改變子彈位置了吧)。為了不讓子彈繼承上一次發(fā)射的速度,必須將子彈的速度歸零重置。
子彈的擊中判斷:采用射線而不是物理引擎,因?yàn)槲锢硪嬖诟咚傥矬w碰撞時(shí)經(jīng)常不能百分百檢測(cè)得到。
?
完成飛碟工廠
創(chuàng)建新的命名空間Com.Mygame,單例類DiskFactory和SceneController都定義其中。飛碟工廠類的目的是管理飛碟實(shí)例,同時(shí)對(duì)外屏蔽飛碟實(shí)例的的提取和回收細(xì)節(jié),對(duì)于需要使用飛碟的其他對(duì)象,只能使用工廠類提供的3個(gè)函數(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 diskPrefab;public static DiskFactory getInstance() { if (_instance == null) { _instance = new DiskFactory(); diskList = new List<GameObject>(); } return _instance; }// 獲取可用飛碟id public int getDisk() { for (int i = 0; i < diskList.Count; ++i) { if (!diskList[i].activeInHierarchy) { return i; // 飛碟空閑 } } // 無空閑飛碟,則實(shí)例新的飛碟預(yù)設(shè)diskList.Add(GameObject.Instantiate(diskPrefab) as GameObject); return diskList.Count-1; }// 獲取飛碟對(duì)象 public GameObject getDiskObject(int id) { if (id >= 0 && id < diskList.Count) { return diskList[id]; } return null; }// 回收飛碟 public void free(int id) { if (id >= 0 && id < diskList.Count) { // 重置飛碟速度 diskList[id].GetComponent<Rigidbody>().velocity = Vector3.zero; // 重置飛碟大小 diskList[id].transform.localScale = diskPrefab.transform.localScale; diskList[id].SetActive(false); } }} } public class DiskFactoryBaseCode : MonoBehaviour {public GameObject disk; void Awake () { // 初始化預(yù)設(shè)對(duì)象 DiskFactory.getInstance().diskPrefab = disk; } }?
完成游戲場(chǎng)景
場(chǎng)景類是整個(gè)飛碟射擊游戲的核心類,主要負(fù)責(zé)飛碟動(dòng)作的處理。參考師兄的設(shè)計(jì):首先需要倒計(jì)時(shí)功能,可以通過幾個(gè)整型變量和布爾變量完成。另外需要飛碟發(fā)射功能,通過setting函數(shù)保存好飛碟的發(fā)射信息,每次倒計(jì)時(shí)完成后,通過emitDisks獲取飛碟對(duì)象,并通過發(fā)射信息初始化飛碟,再給飛碟一個(gè)力就可以發(fā)射了。而飛碟的回收在Update里完成,一種是飛碟被擊中(飛碟不在場(chǎng)景中)了,需要調(diào)用Judge獲得分?jǐn)?shù)。另一種是飛碟在場(chǎng)景中,但是掉在地上了,需要調(diào)用Judge丟失分?jǐn)?shù)。
?
using UnityEngine; using System.Collections; using System.Collections.Generic; using Com.Mygame; public class GameModel : MonoBehaviour {public float countDown = 3f;public float timeToEmit;private bool counting;private bool shooting;public bool isCounting() {return counting;}public bool isShooting() {return shooting;}private List<GameObject> disks = new List<GameObject> ();private List<int> diskIds = new List<int> ();private int diskScale;private Color diskColor;private Vector3 emitPosition;private Vector3 emitDirection;private float emitSpeed;private int emitNumber;private bool emitEnable;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;}public void prepareToEmitDisk() {if (!counting && !shooting) {timeToEmit = countDown;emitEnable = true;}}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].GetComponent<Renderer> ().material.color = diskColor;disks [i].transform.position = new Vector3 (emitPosition.x, emitPosition.y + i, emitPosition.z);disks [i].SetActive (true);disks [i].GetComponent<Rigidbody> ().AddForce (emitDirection * Random.Range (emitSpeed * 5, emitSpeed * 10) / 10, ForceMode.Impulse);}}void freeDisk(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 ();emitEnable = false;shooting = true;}}}// Use this for initializationvoid Start () {}// Update is called once per framevoid Update () {for (int i = 0; i < disks.Count; i++) {if (!disks [i].activeInHierarchy) {scene.getJudge ().scoreADisk ();freeDisk (i);} else if (disks [i].transform.position.y < 0) {scene.getJudge ().failADisk ();freeDisk (i);}}if (disks.Count == 0) {shooting = false;}} }?
場(chǎng)景控制器
場(chǎng)景控制類主要實(shí)現(xiàn)接口定義和保存注入對(duì)象。另外它有兩個(gè)私有變量round和point,分別記錄游戲正在進(jìn)行的回合,以及玩家目前的得分。
?
using UnityEngine; using System.Collections; using Com.Mygame; namespace Com.Mygame {// Com.Mygame內(nèi)添加public interface IUserInterface {void emitDisk();}public interface IQueryStatus {bool isCounting();bool isShooting();int getRound();int getPoint();int getEmitTime();}public class SceneController : System.Object, IQueryStatus, IUserInterface {private static SceneController _instance;private GameModel _gameModel;private SceneControllerBaseCode _baseCode;private int _round;private int _point;public static SceneController getInstance() {if (_instance == null) {_instance = new SceneController ();}return _instance;}public void setSceneControllerBaseCode (SceneControllerBaseCode obj) {_baseCode = obj;} internal SceneControllerBaseCode getSceneControllerBC() {return _baseCode;} public void setGameModel(GameModel obj) {_gameModel = obj;}// 當(dāng)前程序或派生類可用internal GameModel getGameModel() {return _gameModel;}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;}} } public class SceneControllerBaseCode : MonoBehaviour { private Color color; private Vector3 emitPos; private Vector3 emitDir; private float speed; void Awake() { SceneController.getInstance().setSceneControllerBaseCode(this); } void Start() {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); } }??
完善UserInterface
?
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;// Use this for initializationvoid Start () {bullet = GameObject.Instantiate (bullet) as GameObject;explosion = GameObject.Instantiate (explosion) as ParticleSystem;userInt = SceneController.getInstance () as IUserInterface;queryInt = SceneController.getInstance () as IQueryStatus;}// Update is called once per framevoid Update () {if (queryInt.isCounting ()) {mainText.text = ((int)queryInt.getEmitTime ()).ToString ();} else {if (Input.GetKeyDown (KeyCode.Space)) {userInt.emitDisk ();}if (queryInt.isShooting ()) {mainText.text = " ";} else {mainText.text = "Press space";}if (queryInt.isShooting() && Input.GetMouseButtonDown (0) && Time.time > nextFireTime) {nextFireTime = Time.time + fireRate;Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);bullet.GetComponent<Rigidbody> ().velocity = Vector3.zero;bullet.transform.position = transform.position;bullet.GetComponent<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.GetComponent<Renderer> ().material.color = hit.collider.gameObject.GetComponent<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() + "!";}} }?
補(bǔ)充游戲規(guī)則–Judge計(jì)分系統(tǒng)
游戲規(guī)則單獨(dú)作為一個(gè)類,有利于日后修改。這里需要處理的規(guī)則無非就兩個(gè),得分和失分。另外,得分需要判斷是否能晉級(jí)下一關(guān)。能就調(diào)用接口函數(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(); // 默認(rèn)開始第一關(guān) } // 擊中飛碟得分 public void scoreADisk() { scene.setPoint(scene.getPoint() + oneDiskScore); if (scene.getPoint() == disksToWin*oneDiskScore) { scene.nextRound(); } } // 掉落飛碟失分 public void failADisk() { scene.setPoint(scene.getPoint() - oneDiskFail); } }?
在場(chǎng)景控制器中添加相應(yīng)裁判的代碼
?
// Com.Mygame內(nèi)添加public interface IjudgeEvent {void nextRound();void setPoint(int point);} // 類內(nèi)部添加并且類繼承IjudgeEventprivate Judge _judge; public void setJudge(Judge obj) { _judge = obj; } internal Judge getJudge() { return _judge; }?
在GameModel中調(diào)用裁判計(jì)分功能
void Update () {for (int i = 0; i < disks.Count; i++) {if (!disks [i].activeInHierarchy) {scene.getJudge ().scoreADisk ();freeDisk (i);} else if (disks [i].transform.position.y < 0) {scene.getJudge ().failADisk ();freeDisk (i);}}if (disks.Count == 0) {shooting = false;}}?
設(shè)置關(guān)卡
在SceneControllerBaseCode中添加關(guān)卡信息,通過添加loadRoundData來完成每個(gè)關(guān)卡對(duì)游戲?qū)ο髮傩缘脑O(shè)置。
public void loadRoundData(int round) { switch(round) { case 1: // 第一關(guān) 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: // 第二關(guān) 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;case 3: // 第二關(guān) color = Color.yellow; 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, 3); break; } }?
游戲效果:
?
參考鏈接:
http://blog.csdn.net/simba_scorpio/article/details/51051241
?
轉(zhuǎn)載于:https://www.cnblogs.com/alala713/p/6892067.html
總結(jié)
以上是生活随笔為你收集整理的Unity游戏设计之飞碟游戏的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 交互入门2——射击打靶游戏
- 下一篇: 改进飞碟(Hit UFO)游戏