【Unity】弾幕を張る

見て.弾幕です.

弾幕のgif

これもゲーム制作の副産物を整理したものです.

github.com

弾幕を張りたい

これは人の基本的な欲です.
しかしUnityなどのゲームエンジンでオブジェクトを大量に出すと重いです.
弾にRigidbodyが付いてる場合は特にヤバいです.*1
ドローコールすうも単純に増えるのでヤバいです.*2
そこで大量の弾の当たり判定と描画をまとめて行っています.

つかいかた

シーンにマネージャを置いて弾のデータを作って弾源に参照させる.

マネージャを置く

全ての弾幕を1つのMonoBehaviourから動かしてます.
BulletManagerをシーンに1つだけ配置します.

f:id:FriendSea:20191208163750p:plain

ここでは弾幕が衝突するレイヤーとかを選べる.
Vanish Effectは弾が消えたときに出る弾です.

弾源の設定

GameObjectにBulletSourceコンポーネントを付けてDataに弾のデータを参照すると弾幕ができます. 色は配列になっていて上から順に射出する弾の色が変わります.
OnFireイベントは射出音とかに使える.

f:id:FriendSea:20191208164007p:plain

弾のデータはメニューの
Assets/Create/Bullets/
の中から選んで用意しておきます.

弾のMaterialのシェーダには
Bullets/OpaqueBullets/Transparentを使います.

被弾するもの

IBulletColliderを継承したコンポーネントを付けるとOnBulletCollisionが呼ばる.

using UnityEngine;
using InstancedBullets;

public class BulletCollisionTarget : MonoBehaviour, IBulletCollider
{
    public void OnBulletCollision(BulletBase.Bullet bulletElement, BulletBase bulletData)
    {
        //被弾した!
    }
}

Colliderも要ります,Rigidbodyは要らない.
冒頭のgifではプレイヤーの被弾の他に,弾消しでアイテムが出るのもこれで書いてます.

いろんな弾幕

いろんな弾

なんか加速する弾や曲がる弾があります.
もっと複雑な動きをさせる場合はBulletBaseを継承したスクリプトを用意すれば
UpdateBulletに弾の挙動を書けます.

using UnityEngine;
using InstancedBullets;

[CreateAssetMenu(menuName = "Bullets/CustomBullet")]
public class CustomBullet : BulletBase
{
    protected override void UpdateBullet(Bullet bullet)
    {
        //弾1つに対するUpdate処理
    }
}

弾源を書く

インスペクタなどでBulletBaseを参照してAddBulletを呼ぶと弾を出せます.

using UnityEngine;
using InstancedBullets;

public class CustomBulletSource : MonoBehaviour
{
    [SerializeField]
    BulletBase data;

    private void FixedUpdate()
    {
        //画面奥に赤い玉を撃つ
        data.AddColorBullet(transform.position, Quaternion.identity, Vector3.forward, Color.red, transform, 1f);
        //色は指定しなかったら白, 親は指定しなかったらnull,スケールは指定しなかったら1.0
        data.AddBullet(transform.position, Quaternion.identity, Vector3.forward);
    }
}

やってること

当たり判定

弾の数だけClliderを用意するとしんどいのでPhysics.OverlapSphereNonAllocで行っています.
物に当たった弾は無条件で消えるようにしているので現状はグレイズなどは取れません.

描画

Graphics.DrawMeshInstancedを使って多数の弾をまとめて描画しています.
MaterialPropertyBlockを使って弾ごとに色を変えられるようにしています.

おしまい

今の所これで困ってないですがもうちょっと分かりやすくしたいです.
ParticleSystemみたいにコンポーネントのインスペクタで設定が完結するようにしたい.

*1:必要ない場合が多いですが地形に当たった弾が消えてほしい場合などは必要

*2:これはマテリアルをGPU Instanciongに対応させれば勝手にまとめてくれる