Unity 2D 實作簡易子彈射擊功能
分類
說明
分享在 Unity 中如何實作子彈射擊的效果,以比較常用的功能來講解,這篇相當基礎,非常適合初學者或者對子彈射擊功能毫無頭緒的開發者。
實作簡述
此篇為 Unity 2D 所以範例都會以 2D 為例,為了方便我會以我開發打磚塊遊戲中的其中一個功能做教學,功能為球拍會固定頻率發射子彈,子彈是由下往上等速移動,並且偵測是否有跟磚塊發生碰撞,當然讀者們之後可以在這基礎上做變化。
需求分析
在開始實作之前,先告訴大家如何分析功能需求對於往後自己開發功能很有幫助
- 需要一張子彈圖片,這裡可以使用免費素材 UX Flat Icons [Free] 自己挑選一張喜歡的圖片
- 需要將子彈做成預製,以方便後續生成子彈
- 實作子彈等速移動
- 實作子彈碰撞處理
- 實作生成子彈功能,此範例為球拍固定頻率發射子彈
也許分析完後,有些讀者已經知道該怎麼做了,如果不清楚也別擔心,接下來會告訴大家每個步驟該如何實作。
子彈圖片
將圖片放在 Assets 資料夾裡,這裡沒有特別限制要放在哪個資料夾,你可以自訂一個 Images 或 Sprites 資料夾來放圖片,如果是上述下載的圖片,也可以放在預設的下載位置。
製作子彈遊戲物件
將圖片拉到場景中,並在 Hierarchy 中選中子彈遊戲物件,將其名稱改成 Bullet
調整圖片每單位像素,使圖片大小與遊戲場景匹配。
選擇 Assets 資料夾中子彈圖片,在 Inspector 中設定 Pixels Per Unit 調整合適的大小,然後按 Apply。
選擇遊戲場景中的子彈,並加入以下組件
- Box Collider 2D,你也可以用其他的碰撞器
- Rigidbody 2D
- 在 Assets/Scripts 資料夾建立一個 Bullet.cs 腳本加在子彈遊戲物件上
Box Collider 2D
首先編輯碰撞範圍
我用紅色框起來裡面的綠框就是碰撞範圍,依照你的需求調整碰撞範圍。
你可以看到我有勾選 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()
處理碰撞功能,當子彈碰到磚塊時,一樣將子彈摧毀,然後你可以做你想要的處理,在這裡只有簡單輸出訊息。
球拍生成子彈
因爲你們沒有球拍,一樣可以先拉一個立方體充當球拍,並在底下生成兩個空物件當作子彈發射位置,先將遊戲物件做來,如下圖所示。
ShootingPositionA 和 ShootingPositionB 兩個是空物件,只是作為球拍發射子彈的位置,可以更改它們的 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
,如下圖所示。
一杯咖啡的力量,勝過千言萬語的感謝。
支持我一杯咖啡,讓我繼續創作優質內容,與您分享更多知識與樂趣!