Unity 2D 實作簡易子彈射擊功能


建立時間: 2023年4月19日 02:11
更新時間: 2023年4月20日 00:42

說明

分享在 Unity 中如何實作子彈射擊的效果,以比較常用的功能來講解,這篇相當基礎,非常適合初學者或者對子彈射擊功能毫無頭緒的開發者。

實作簡述

此篇為 Unity 2D 所以範例都會以 2D 為例,為了方便我會以我開發打磚塊遊戲中的其中一個功能做教學,功能為球拍會固定頻率發射子彈,子彈是由下往上等速移動,並且偵測是否有跟磚塊發生碰撞,當然讀者們之後可以在這基礎上做變化。

需求分析

在開始實作之前,先告訴大家如何分析功能需求對於往後自己開發功能很有幫助

  1. 需要一張子彈圖片,這裡可以使用免費素材 UX Flat Icons [Free] 自己挑選一張喜歡的圖片
  2. 需要將子彈做成預製,以方便後續生成子彈
  3. 實作子彈等速移動
  4. 實作子彈碰撞處理
  5. 實作生成子彈功能,此範例為球拍固定頻率發射子彈

也許分析完後,有些讀者已經知道該怎麼做了,如果不清楚也別擔心,接下來會告訴大家每個步驟該如何實作。

子彈圖片

將圖片放在 Assets 資料夾裡,這裡沒有特別限制要放在哪個資料夾,你可以自訂一個 Images 或 Sprites 資料夾來放圖片,如果是上述下載的圖片,也可以放在預設的下載位置。

製作子彈遊戲物件

將圖片拉到場景中,並在 Hierarchy 中選中子彈遊戲物件,將其名稱改成 Bullet

調整圖片每單位像素,使圖片大小與遊戲場景匹配。
選擇 Assets 資料夾中子彈圖片,在 Inspector 中設定 Pixels Per Unit 調整合適的大小,然後按 Apply。

Setting Pixels Per Unit

選擇遊戲場景中的子彈,並加入以下組件

  1. Box Collider 2D,你也可以用其他的碰撞器
  2. Rigidbody 2D
  3. 在 Assets/Scripts 資料夾建立一個 Bullet.cs 腳本加在子彈遊戲物件上

Box Collider 2D

首先編輯碰撞範圍

Edit Collider

我用紅色框起來裡面的綠框就是碰撞範圍,依照你的需求調整碰撞範圍。

你可以看到我有勾選 Is Trigger,代表我將它作為觸發器,這會使遊戲物件穿越其他遊戲物件,因我只想讓子彈與磚塊碰撞,所以才做這個設定。

Rigidbody 2D

設定以下選項,這個比較單純,我就不附截圖。

  • Gravity Scale: 0
    • 使物體不會掉落
  • Freeze Rotation: 勾選 Z
    • 使物體不會旋轉

Bullet.cs

Bullet.cs

using UnityEngine;

/// <summary>
/// 子彈
/// </summary>
public class Bullet : MonoBehaviour
{
    private Rigidbody2D myRigidbody2D;

    /// <summary>
    /// 子彈速度
    /// </summary>
    [SerializeField]
    private float speed = 5f;

    // Start is called before the first frame update
    void Start()
    {
        myRigidbody2D = GetComponent<Rigidbody2D>();
        myRigidbody2D.velocity = Vector2.up * speed;
    }

    // Update is called once per frame
    void Update()
    {
        // 出界
        if (transform.position.y > 11f)
        {
            Destroy(gameObject);
        }
    }
}

在這裡,只有處理子彈等速移動,以及子彈出界自動摧毀功能,你需要注意子彈摧毀機制,如果子彈一直存在會一直佔用電腦資源。

將子彈做成預製

建立 Assets/Resources/Prefabs/ 資料夾,並將遊戲場景中的子彈拉進 Assets/Resources/Prefabs/ 資料夾,你會看到多了一個 Bullet.prefab 預製。

碰撞

碰撞的功能可以寫在另一個遊戲物件上,或者寫在 Bullet.cs 都是可以的,以下我將示範將碰撞寫在磚塊上,當然讀者沒有磚塊遊戲物件,所以可以拉個立方體遊戲物件來模擬。

BrickObject.cs

using UnityEngine;

/// <summary>
/// 磚塊
/// </summary>
public class BrickObject : MonoBehaviour
{
    // 這裡是碰撞
    private void OnCollisionEnter2D(Collision2D other)
    {
    }

    // 這裡是 trigger
    private void OnTriggerEnter2D(Collider2D other)
    {
        // 碰撞到子彈
        if (other.TryGetComponent<Bullet>(out var _))
        {
            Debug.Log("撞到子彈了");
            Destroy(other.gameObject);
        }
    }
}

這裡我只列出碰撞處理的方法,因為我有勾選 Is Trigger,所以我要在 OnTriggerEnter2D() 處理觸發功能,如果沒有勾選 Is Trigger 就要在 OnCollisionEnter2D() 處理碰撞功能,當子彈碰到磚塊時,一樣將子彈摧毀,然後你可以做你想要的處理,在這裡只有簡單輸出訊息。

球拍生成子彈

因爲你們沒有球拍,一樣可以先拉一個立方體充當球拍,並在底下生成兩個空物件當作子彈發射位置,先將遊戲物件做來,如下圖所示。

Paddle

ShootingPositionA 和 ShootingPositionB 兩個是空物件,只是作為球拍發射子彈的位置,可以更改它們的 Icon 讓它們可以在場景中被看到,但在遊戲場景中一樣看不到,如下圖所示。

Select Icon

在 Scripts 資料夾中新增 Paddle.cs 加入在 Paddle 遊戲物件中

Paddle.cs

using UnityEngine;

/// <summary>
/// 球拍
/// </summary>
public class Paddle : MonoBehaviour
{
    /// <summary>
    /// 子彈發射位置
    /// </summary>
    [SerializeField]
    private Transform[] shootingTransforms;

    /// <summary>
    /// 是否得到過子彈效果
    /// </summary>
    private bool isGettingBulletEffect = false;

    /// <summary>
    /// 子彈發射速率
    /// </summary>
    [SerializeField]
    private float firingRate = 1f;

    /// <summary>
    /// 上一次射擊時間
    /// </summary>
    private float lastShootingTime = 0;

    /// <summary>
    /// 子彈發射速率上限
    /// </summary>
    private readonly float maxFiringRate = 0.1f;

    void Start()
    {
        isGettingBulletEffect = true;
    }

    // Update is called once per frame
    void Update()
    {
        if (isGettingBulletEffect)
        {
            Shoot();
        }
    }

    /// <summary>
    /// 處理自動射擊
    /// </summary>
    private void Shoot()
    {
        if (Time.time - lastShootingTime < firingRate)
        {
            return;
        }
        lastShootingTime = Time.time;

        foreach (Transform shootingTransform in shootingTransforms)
        {
            GameObject bulletPrefab = Resources.Load<GameObject>("Prefabs/Bullet");
            GameObject bullet = Instantiate(bulletPrefab);
            bullet.transform.position = shootingTransform.position;
        }
    }

    /// <summary>
    /// 取得射擊效果
    /// </summary>
    public void GetShootingEffect()
    {
        if (!isGettingBulletEffect)
        {
            isGettingBulletEffect = true;

            return;
        }
        firingRate = Mathf.Max(firingRate - 0.1f, maxFiringRate);
    }
}

如上,我的遊戲中其實有一個隨機掉落的子彈效果,吃到後會觸發 GetShootingEffect(),吃到越多射擊速度越快,但這並不是子彈射擊必要處理的事情,所以我在 Start() 直接將 isGettingBulletEffect 設為 true 簡化範例,你可以在 Shoot() 看到取得子彈預製,並且生成子彈的方法。

最後需要在遊戲場景中設定 shootingTransforms,如下圖所示。

Set shooting transforms

觀看次數: 1396
bulletshootshootingunity子彈射擊
按讚追蹤 Enjoy 軟體 Facebook 粉絲專頁
每週分享資訊技術

一杯咖啡的力量,勝過千言萬語的感謝。

支持我一杯咖啡,讓我繼續創作優質內容,與您分享更多知識與樂趣!