扔飛鏢遊戲

日落西山紅霞飛~戰士打靶把營歸呀巴扎嘿。今天我製作一個簡單的打靶遊戲(扔飛鏢)

在製作之前首先要思考這個遊戲需要什麼對象,很簡單,一隻飛鏢、一個靶。

這裏我把飛鏢設置成了剛體,什麼是剛體?簡而言之,就是給這個物體加上了物理引擎,有了剛體,這個對象可以發生正常的碰撞,可以有質量、有阻力、有重力。這些屬性自然我們可以通過時時改變遊戲對象的Position和Rotation來模擬。但是這樣子的話我們要時刻計算這個對象每個點對應的狀態,計算開銷十分之大。然而剛體就幫我們完成了所有的一切。

值得注意的是,剛體中通過改變位置來改變狀態和通過施加力來改變位置這兩種方法不能共存,剛體中Is Kinematic屬性可以勾選是否爲運動學,在運動學狀態下,對象不再受力的作用,反之亦然。

剛體往往要配合碰撞器來使用,基礎的碰撞器有盒碰撞器、球碰撞器等等,他們有自己的中心位置、大小等屬性。比如一個球碰撞器:
這裏寫圖片描述
綠線範圍就是這個碰撞器的感應邊緣
碰撞器可以理解爲一個看不見的防禦網,一旦有物體進入這個網,都會根據物理引擎模擬出被撞飛的效果(當然誰撞飛既取決於質量,也取決於是否爲靜態碰撞器,詳情請查詢具體的碰撞器和剛體的知識)

下面說一下我設計的思路

靶我是通過五個球體通過修改Z值變成圓餅做成的
對象樹和做出來的效果如圖
這裏寫圖片描述
靶是一個空對象。這裏對靶不設置爲剛體,因爲我把它作爲了靜態碰撞器。由於射中不同的環分數不一樣,因此每個子對象都添加了一個碰撞器,這樣子就能根據飛鏢到底是撞到了哪個子對象從而可以計算相應的分數。

理想很豐滿,現實很骨感,想用網格碰撞器,但是面太多不能用,用球碰撞器又不能把檢測範圍做成圓餅
這裏寫圖片描述
這種範圍就像結界一樣,飛鏢無論多快都無法穿過,隔空就被撞飛了。

那該怎麼辦呢?後來我有了新思路,就保持這種球體碰撞器,不同的是把它們都設爲觸發器,飛鏢每穿過一個觸發器就把這個觸發器發送給靶,然後當飛鏢頭部剛好和靶重合的時候,把飛鏢的剛體設爲運動學,這樣一來可以使得飛鏢無法受到裏的作用而產生飛鏢卡在了靶上的效果,同時靶對象保存的最後一個觸發器就是飛鏢扎中的那個圓環。

那麼思路明確了我們就可以開始做了。首先要有靶和飛鏢吧,靶在上面已經做好了,那麼展示一下我的飛鏢(感覺像一個導彈)
這裏寫圖片描述

代碼也很簡單,搞了三個類

1.Arrow類

這個就是飛鏢的類
具體有如下幾個功能:

  • 在未射出時跟隨鼠標改變位置
  • 當扔出後給它施加力

便於重新開始,設置了Init函數用於初始化,IsFly判斷是否被扔出,time記錄飛行時間,爲了模擬真實仍飛鏢的情景,0.5秒內我給了飛鏢一個向上的力,之後飛鏢就只有重力和向前的力

代碼如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Arrow : MonoBehaviour {
    private int IsFly;
    private float time;
    private void Start()
    {
        Init();
    }

    public void Init()
    {
        IsFly = 0;
        transform.position = new Vector3(0, 0, -7);
        gameObject.GetComponent<Rigidbody>().isKinematic = false;
        gameObject.GetComponent<Rigidbody>().useGravity = false;
    }

    // Update is called once per frame
    void Update () {
        if (IsFly == 0)
        {
            Vector3 screenPos = Camera.main.WorldToScreenPoint(transform.position); // 目的獲取z,在Start方法  
            Vector3 mousePos = Input.mousePosition;
            mousePos.z = screenPos.z; // 這個很關鍵  
            Vector3 worldPos = Camera.main.ScreenToWorldPoint(mousePos);
            this.transform.position = worldPos;
            if (Input.GetButtonDown("Fire1"))
            {
                IsFly = 1;
                time = 0;
            }
        } else
        {
            time += Time.deltaTime;
        }
    }
    private void FixedUpdate()
    {
        if (IsFly == 1)
        {
            Rigidbody rigid = this.gameObject.GetComponent<Rigidbody>();
            if (rigid)
            {
                if (time < 0.5)
                {
                    rigid.AddForce(new Vector3(0, 5, 10));
                }
                else
                {
                    rigid.AddForce(new Vector3(0, 0, 15));
                    rigid.useGravity = true;
                }
            }
        }
    }
}

2.Target類

這個掛載在靶這個父對象身上,Catch判斷飛鏢是否被觸發器捕捉到(沒被捕捉到表示沒有紮在靶上),Score記錄得分,trigger記錄最後一個被觸發的觸發器,arrow則是飛鏢對象。
由於飛鏢移動較快,檢測可能沒那麼迅速,所以用了連續檢測。由於我把靶放在了(0,0,0)位置上,所以只要當飛鏢的位置的Z>0.05(飛鏢頭進入了靶)就可以把飛鏢設爲運動學。

代碼:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Target : MonoBehaviour {
    public int Catch;
    public int Score;
    private static Trigger trigger;
    public static Arrow arrow;

    // Use this for initialization
    private void Start()
    {
        print("hh");
        Init(); 
    }
    public void Init()
    {
        if (arrow == null)
            arrow = Instantiate<Arrow>(Resources.Load<Arrow>("Prefabs/飛鏢"));
        Catch = 0;
        Score = -1;
        trigger = null;
        arrow.Init();
    }

    public void Catched()
    {
        Catch = 1;
    }

    public static void SetTrigger(Trigger tri)
    {
        trigger = tri;
    }

    private void OnGUI()
    {
        if (Score != -1)
        {
            GUI.Label(new Rect(400, 80, 100, 30), "Your Score is " + Score);
            GUI.Label(new Rect(400, 100, 150, 30), "push blank to rebegin");
        }
    }

    // Update is called once per frame
    void Update () {
        if (Input.GetAxis("Jump") > 0)
            Init();
        if (Catch == 1)
        {
            if (arrow.transform.position.z > 0.05)
            {
                arrow.GetComponent<Rigidbody>().isKinematic = true;
                arrow.GetComponent<Rigidbody>().useGravity = false;
                print(trigger.gameObject.name);
                switch(trigger.gameObject.name)
                {
                    case "圈1":
                        Score = 5;
                        break;
                    case "圈2":
                        Score = 4;
                        break;
                    case "圈3":
                        Score = 3;
                        break;
                    case "圈4":
                        Score = 2;
                        break;
                    case "圈5":
                        Score = 1;
                        break;
                }
            }
            return;
        }
        if (arrow.transform.position.z > 0.5)
            Score = 0;
    }
}

3.Trigger類

首先我們要把每個碰撞器的 Is Trigger設爲true。它關聯到靶對象(Target類),當觸發器檢測到飛鏢,就把觸發器自身發送給Target

代碼:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Trigger : MonoBehaviour
{
    public Target target;
    // Use this for initialization


    // Update is called once per frame

    void OnTriggerEnter(Collider collider)
    {
        print("name : " + gameObject.name);
        target.Catched();
        Target.SetTrigger(this);
    }
}

好,簡陋版就做出來了

這裏寫圖片描述

拓展部份

可能這樣子這個遊戲很沒有挑戰性,那麼我們還可以製造風,東西南北風!簡單來說就是給飛鏢添加別的力—— 一切阻礙它正常移動的力,也可以說,阻力。這個也很簡單,就是時時給飛鏢添加一個向左、向右、向後的阻力,一切都可以靠自己DIY

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章