交互入门2——射击打靶游戏
文章目錄
- 射箭游戲設計與實現
-
- 游戲要求:
- 具體實現代碼
-
- 動作部分
- 碰撞檢測
- 工廠類生產箭
- Controller類
- UI類
- 游戲效果
射箭游戲設計與實現
游戲要求:
游戲內容要求:
靶對象為 5 環,按環計分;
箭對象,射中后要插在靶上
增強要求:射中后,箭對象產生顫抖效果,到下一次射擊 或 1秒以后
游戲僅一輪,無限 trials;
增強要求:添加一個風向和強度標志,提高難度
具體實現思路:
具體實現代碼
首先用到之前幾個游戲的一些基類:Director、SSAction、SSActionManager、Singleton等,這幾個類由于只是基類,真正的邏輯實現都在子類中,所以直接重用。
動作部分
先來說說游戲的動作部分,首先分析主要的運動對象:箭。。。沒了。所以與之前打飛碟的游戲類似,只需要實現將箭飛出去的動作,通過物理引擎的實現之前也提到過,只需要在出去的瞬間給它添加一個力就可以了。
其次是風力的作用,與飛出去的瞬間力不同,風力是需要持續作用的力,所以需要在FixedUpdate里持續添加。
從以上可以看出,此動作類主要的關鍵屬性有:運動方向、風力作用方向。
ArrowAction?代碼如下:
- ?
所以構造此類的時候需要的參數也就是兩個,分別對應兩個關鍵屬性。
此外,當箭超出一定范圍時,我們就可以認為它無法到達靶標,直接中止動作執行?;蛘弋斊涞竭_目標時,(名字會設置為ontarget)也可以中止動作。
動作管理器類也就是簡單的包裝一下動作類,并且使其執行,并且實現回調函數,這里的回調就是用弓箭工廠的方法將箭的對象釋放掉(加入到free的隊列去,以便重新使用):
ArrowActionManager
- ?
值得注意的一點是,由于箭未發出的時候,是需要在弓上停留的,也即是說不能有剛體的重力作用,否則就會掉下去。所以在運動管理器中,需要在執行射箭動作之前,恢復剛體的作用,這里采用的是運動學模式和物理模式切換的方式來實現。如果是新創建的實例,還沒有添加剛體部件則需要添加。
回調函數里主要是運動結束后執行的行為,運動結束主要有兩個狀態,上靶和未中靶,上靶的箭不能free掉,因為要保留來積分(模擬真實場景),所以還需要額外判斷當前的箭是脫靶了還是中靶的。
碰撞檢測
主要是利用碰撞體的Trigger,來檢測是否碰撞到了,如果碰到了就積分,不同的碰撞體不同分,然后將箭的名字(用來代表狀態)改成ontarget,這樣就防止別的碰撞器也重復積分。因為會實際上兩個碰撞器的距離很微小,所以能夠同時碰到多個碰撞器。
public class CollisionRev : MonoBehaviour {private void OnTriggerEnter(Collider other){ GameObject arrow = other.gameObject;if (arrow.name == "arrow") {string str = this.name;arrow.GetComponent<Rigidbody>().velocity = Vector3.zero;arrow.GetComponent<Rigidbody>().isKinematic = true;Singleton<Judger>.Instance.addScore(str);arrow.transform.position += Vector3.forward * 0.001f;arrow.name = "ontarget";Controller controller = Director.getInstance().currentSceneController as Controller;controller.hit(arrow);// if (controller.arrow == null)controller.getArrow();}} }- ?
工廠類生產箭
與前一個實驗的飛碟工廠很像,而且不需要添加屬性什么的,更加簡單。
只需要將空閑的或者剛創建的箭,初始化位置等就可以了。還有一個Free的方法,也是跟之前類似。
- ?
Controller類
public class Controller : MonoBehaviour, SceneController, Interaction {public ArrowActionManager actionManager;public ArrowFactory factory;public GameObject bow;public GameObject target;public GameObject arrow;public Judger judger;public Vector3 direction;public UI ui;public int state = 0;private int arrowNumber = 0;private Queue<GameObject> hit_arrow = new Queue<GameObject>();public Vector3 wind = Vector3.zero; private int[] direc = {1,-1,0};private void Start() {Director director = Director.getInstance();director.currentSceneController = this;factory = this.gameObject.AddComponent<ArrowFactory>();actionManager = this.gameObject.AddComponent<ArrowActionManager>() as ArrowActionManager;ui = this.gameObject.AddComponent<UI>();judger = this.gameObject.AddComponent<Judger>();// factory = Singleton<ArrowFactory>.Instance;loadResources();int x = Random.Range(0,3);int y = Random.Range(0,3);x = direc[x];y = direc[y];int level = Random.Range(1,5);wind = new Vector3(x, y, 0) * level;}public void loadResources() {bow = Instantiate(Resources.Load("Prefabs/bow", typeof(GameObject))) as GameObject;target = Instantiate(Resources.Load("Prefabs/target", typeof(GameObject))) as GameObject;arrow = factory.getArrow();}private void Update(){}public void moveArrowDirection(Vector3 to) {if (state <= 0) {return;}arrow.transform.rotation = Quaternion.LookRotation(to);bow.transform.rotation = Quaternion.LookRotation(to);direction = to;}public void reuse() {int tmp = hit_arrow.Count;for (int i = 0; i < tmp; i ++) {factory.freeArrow(hit_arrow.Dequeue());}arrowNumber = 0;}public void shoot(Vector3 force) {if (state > 0 && arrow != null) {// arrow = factory.getArrow();arrow.name = "arrow";actionManager.arrowFly(arrow, direction * 15, wind);arrowNumber ++;}}public void hit(GameObject arrow) {hit_arrow.Enqueue(arrow);}public void getArrow() {int x = Random.Range(0,3);int y = Random.Range(0,3);x = direc[x];y = direc[y];int level = Random.Range(1,5);wind = new Vector3(x, y, 0) * level;if (state == 1) {if (arrowNumber > 7) {setState(-1);}}arrow = factory.getArrow();}public void setState(int s) {state = s;}public void restart() {state = 0;arrowNumber = 0;}public int getState() {return state;}public string arrowState() {return arrow != null ? arrow.name : null;}public Vector3 getWind() {return wind;} }- ?
這里面主要是實現了射箭、獲取箭的函數,還有一部分與用戶交互的函數。
loadResources就是加載弓箭和靶子的資源。
射箭shoot主要是狀態的改變,之前已經說過,用名字來代表狀態,在射出的時候更改狀態為arrow表示在飛行中。
moveDirection函數主要是更改弓箭的朝向,這里是與鼠標的位置相關,也就是利用鼠標更改朝向,使得其能夠瞄準。
getArrow則是獲取弓箭,也就是下一次射箭的準備工作,需要把風向提前設置好,這里使用隨機的方式生成8個方向的風,風力等級也是隨機,有4個等級。
hit則是類似回調,將中箭的加入使用中的隊列,因為這部分箭不會自動收回(只收回了脫靶的)
還有一些獲取狀態的函數,游戲狀態、風力信息、弓箭狀態等。
UI類
UI主要是負責用戶的交互,所以需要獲取Controller的一些狀態來判斷用戶操作是否合法或者限制用戶的操作。
public class UI : MonoBehaviour {Interaction interaction;bool flag = true;GUIStyle style1;GUIStyle style2;GUIStyle style3;float time = 0;private void Start() {interaction = Director.getInstance().currentSceneController as Interaction;style1 = new GUIStyle("button");style1.fontSize = 25;style2 = new GUIStyle();style2.fontSize = 35;style2.alignment = TextAnchor.MiddleLeft;style3 = new GUIStyle();style3.fontSize = 15;style3.alignment = TextAnchor.MiddleLeft;}private void OnGUI(){ if (interaction.getState() == -1) {if (time < 2) {time += Time.deltaTime;GUI.Label(new Rect(Screen.width/2-70, Screen.height/2-135, 200, 30), "Preparing Arrow...", style2);} else {GUI.Label(new Rect(Screen.width/2-70, Screen.height/2-135, 200, 30), "Your Score:" + Singleton<Judger>.Instance.getScore().ToString(), style2);if (GUI.Button(new Rect(Screen.width/2-70, Screen.height/2 - 20, 180, 70), "Play again", style1)) {interaction.reuse();interaction.setState(1);Singleton<Judger>.Instance.restart();time = 0;}}}GUI.Label(new Rect(5, 5, 100, 30), "Score: " + Singleton<Judger>.Instance.getScore().ToString(), style3);Vector3 wind = interaction.getWind();int x = (int)wind.x;int y = (int)wind.y;string str1, str2, level;if (x < 0) str1 = "West";else if (x > 0)str1 = "East";else str1 = "";if (y < 0) str2 = "South";else if (y > 0)str2 = "North";else str2 = "";if (x == 0 && y == 0) {str1 = "No wind";}if (x != 0) {int tmp = x > 0 ? x : -x;level = tmp.ToString();} else if (y != 0) {int tmp = y > 0 ? y : -y;level = tmp.ToString();} else {level = "0";}GUI.Label(new Rect(5, 35, 200, 30), "Wind Direction: " + str1 + str2, style3);GUI.Label(new Rect(5, 65, 100, 30), "Wind Level: " + level , style3);if (flag) {GUI.Label(new Rect(Screen.width/2-60, Screen.height/2-135, 100, 50), "ShootArrow!", style2);if(GUI.Button(new Rect(Screen.width/2-70, Screen.height/2 - 20, 150, 70), "Play", style1)) {flag = false;interaction.setState(1);}}}private void Update(){if (interaction.getState() > 0) {Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);if (interaction.arrowState() == "ready") {interaction.moveArrowDirection(ray.direction);if (Input.GetButtonDown("Fire1")) {interaction.shoot(ray.direction);}}}} }- ?
這里主要是根據Controller的狀態(游戲未開始、進行中、結束)來顯示不同的界面,如果是進行中,還需要顯示分數、風力信息等,這里是通過Controller的風力向量,臨時計算風力信息,有點不太好。
還有最重要的一點是,獲取鼠標位置,并且設置相應的弓箭朝向。響應點擊事件來射箭,具體也就是調用Controller的接口來執行動作。
游戲效果
本次實驗到此結束!
總結
以上是生活随笔為你收集整理的交互入门2——射击打靶游戏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: my射飞碟小游戏
- 下一篇: Unity游戏设计之飞碟游戏