Unity で一般的に使用される 2D 弾幕と銃器タイプの実装: ショットガン、ボス弾幕、弾丸のリバウンド、追跡爆弾などを実装できます。

記事ディレクトリ


皆さん、こんにちは。私は Cairo Xiao8 です。今日は、Unity でよく使用される弾幕タイプの実装について説明します。この機能を実装するにはさまざまな方法があります。エフェクトを実装するときは、最も簡単な方法を使用します。コードの実装、記事のサンプル プロジェクト ファイルは記事の最後にあります

リング弾幕実装
ホーミング爆弾

序文

この記事弾丸は常に前方向に移動します (赤軸は 2D の前方向、青軸は3D では前方向)、座標軸をローカルに切り替えて、現在のオブジェクトの方向を表示できます

 Bullet基本クラススクリプト

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

public class BaseBullet : MonoBehaviour
{
    public float Speed=1;
    public GameObject ExpFX;
    protected Rigidbody2D rb;
    protected Transform m_tansform;
    // Update is called once per frame
    private void Awake()
    {
        rb = gameObject.AddComponent<Rigidbody2D>();
        rb.gravityScale = 0;
        rb.drag= 0;
        rb.freezeRotation = true;
        rb.bodyType = RigidbodyType2D.Dynamic;
        rb.constraints = RigidbodyConstraints2D.FreezeRotation;
        m_tansform = transform;
    }
    protected virtual void Update()
    {
        m_tansform.position= m_tansform.position+ m_tansform.right*Speed*Time.deltaTime;
        //m_tansform.Translate(Vector3.right * Speed * Time.deltaTime);
    }
    public void Explode()
    {
        Destroy(Instantiate(ExpFX, m_tansform.position,Quaternion.identity),2f);
        Destroy(gameObject);
    }
}
  • スクリプト内の Update コードの両方の行で、弾丸を独自の赤い軸 (X 軸) に沿って移動させることができます。
  • オブジェクトの現在の軸を取得します
  1. transform.right 赤軸 (X 軸)
  2. transform.up 緑軸 (Y 軸)
  3. transform.forward 青軸 (Z 軸)
  • transform.right と Vector3.right の違い
  1. Vector3.right は常に new Vector3(1,0,0) と等しくなります。
  2. transform.right は、現在のオブジェクトの赤軸の方向であり、オブジェクトの回転の影響を受けます。 

transform.Translate メソッドが Vector3 型のみを渡す場合、メソッドは独自の軸上で移動します。

たとえば、transform.Translate(new Vector3(1,0,0)) は、オブジェクトがその X 軸方向に向かって 1 メートル移動することを意味します。

弾丸が互いに衝突しないようにするには、Bullet という名前の弾丸用の新しいレイヤーを設定する必要があります。レイヤーを設定した後、編集 -> プロジェクト設定 -> Physics2D で Bullet と Bullet の間の衝突のチェックを外す必要があります。 。

1. 様々な種類の弾と武器の実装

1. マウスの方向に弾を発射します

using UnityEngine;

public class Weapon : MonoBehaviour
{
    public GameObject FireFX;
    public GameObject Bullet;
    private Transform m_transform;
    
    // Start is called before the first frame update
    Camera cam;
    void Start()
    {
        cam=Camera.main;
        m_transform = transform;
    }

    // Update is called once per frame
    void Update()
    {
        
        Vector3 mousePos= cam.ScreenToWorldPoint(Input.mousePosition);
        Vector3 weaponDir=mousePos- m_transform.position;
        weaponDir.z = 0;
        weaponDir.Normalize();
        m_transform.right=weaponDir;
        //float zAngle = Mathf.Atan2(weaponDir.y, weaponDir.x) * Mathf.Rad2Deg;
        //m_transform.localEulerAngles=new Vector3(0,0,zAngle);
        if(Input.GetKeyDown(KeyCode.Space))
        {
            //GameObject fireFX = Instantiate(FireFX);
            //fireFX.transform.position= m_transform.position;
            //Destroy( fireFX ,2f);
            GameObject bullet= Instantiate(Bullet);
            bullet.transform.position= m_transform.position;
            bullet.transform.right= m_transform.right;
        }
    }
}

1. ローカル変数を使用してメイン カメラと変換をキャッシュします。変換を使用して変換を取得したり、Camera.main を使用してカメラを取得したりすると、パフォーマンスがさらに消費されます。

2. マウスの位置を画面空間からワールド空間に変換し、cam.ScreenToWorldPoint(Input.mousePosition) を使用して、現在のマウス画面位置を渡します。

注: 画面の位置は (0,0) で左下隅にあり、画面の解像度は (1920,1080) のように右上隅にあります。

3. ベクトルを計算し、2 つのオブジェクトの位置を減算し、その結果の方向は減算された位置を指します。例のコードは、マウスを指す武器の位置の方向を取得できます。

4. 武器の回転角度を計算し、武器の向きを調整し、弾を生成する際の弾の位置と回転を調整します。

注: オブジェクトの回転を変更するために、transform.right に値を直接割り当てる方法は、2D での使用にのみ適しています。他の軸が正しく回転しない可能性があるため、3D での使用はお勧めできません。

2. 弾丸が壁に当たって跳ね返る

using UnityEngine;

public class BounceBullet : BaseBullet
{
    public int BounceCount = 3;


    private void OnCollisionEnter2D(Collision2D collision)
    {

        
        if (collision.gameObject.CompareTag("Wall"))
        {
            --BounceCount;
            if (BounceCount <= 0)
            {
                Explode();
                return;
            }
            Vector2 outDir = Vector2.Reflect(m_tansform.right, collision.GetContact(0).normal);
            m_tansform.right = outDir;
        }
    }
    
}

  • Vector2.Reflect の最初のパラメータは現在の弾丸の方向に渡され、2 番目のパラメータは衝突の法線方向に渡され、反射後の新しいベクトルが返されます。
  • 衝突点の法線方向を取得するには、collision.GetContact(0).normal 
  • 衝突点の法線方向は OnCollisionEnter2D でのみ取得でき、OnTriggerEnter2D では衝突点の法線方向を取得できません。
  • 弾丸には Rigidbody2D コンポーネントが必要で、リジッド ボディ タイプはダイナミックです。そうでない場合、衝突関数はトリガーされません
  • ここでは、CompareTag メソッドを使用して、衝突するボディのタグを比較します。壁の場合は、バウンスします。CompareTag を使用すると、直接 gameObject.tag=="Wall" を使用するよりもパフォーマンスが節約されます。
  • 渡されるパラメータが正規化後のすべてのベクトルであることを確認する必要があります (ベクトルの法は 1)。
  • オブジェクトの向きを変更するには、transform.right に値を直接割り当てます。

3. 弾丸が敵を追跡する

簡易版、このように書くと弾が敵に向けられてしまい視覚効果が良くありません。

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

public class TargetingBullet : BaseBullet
{
    Transform target;
    private void Start()
    {
        target = GameObject.Find("Target").transform;
    }
    // Update is called once per frame
    protected override void Update()
    {
        Vector3 targetDir=target.position-transform.position;
        targetDir.Normalize();

        transform.right= targetDir;
        base.Update();
    }

}

 調整方法1:補間機能を利用して弾丸を毎回一定の角度に調整する

using UnityEngine;

public class TargetingBullet : BaseBullet
{
    Transform target;
    public float TurnSpeed=2;
    float lifeTime=0;
    private void Start()
    {
        target = GameObject.Find("Target").transform;
    }
    // Update is called once per frame
    protected override void Update()
    {
        Vector3 targetDir=target.position-transform.position;
        targetDir.z = 0;
        targetDir.Normalize();
        lifeTime += Time.deltaTime;
        
        m_tansform.right= Vector3.Slerp(m_tansform.right,targetDir,Mathf.Clamp01(Time.deltaTime*(lifeTime)* TurnSpeed));
        base.Update();
    }
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("Enemy"))
        {
            Explode();
        }
    }
}

ライフタイムの役割: 弾丸が長く存在するほど、旋回速度は速くなります。旋回速度を上げないと、弾丸は速度が高くなっても回り続けます。旋回速度よりも敵は円を描くように回転し、敵を攻撃することはできません 

Vector3.Slerp:最初の 2 つのパラメータは 2 つの Vector3 タイプで渡され、3 番目のパラメータは補間値で渡されます。補間値が 0 の場合、最初のベクトルはが返され、補間値は 1 の場合は 2 番目のベクトルが返され、補間が 0 と 1 の間の場合は 2 つのベクトルの遷移値が返されます

4. ショットガン

public void FireShotGun(float angle,int bulletCount)
    {
        int mid = bulletCount >> 1;
        for(int i=0;i<bulletCount; i++)
        {
            Vector3 dir =  Quaternion.Euler(new Vector3(0,0, (i - mid) * angle))*m_transform.right;
            GameObject bullet = Instantiate(Bullet);
            bullet.transform.position = m_transform.position;
            bullet.transform.right = dir;
        }
    }

angle は各弾丸の角度、bulletCount は弾丸の数、例で渡されるパラメータは 10,4 です。

1. ビット演算を使用して弾丸数の中央値を計算します。buttonCount>>1 は buttonCount/2 に相当します。ビット演算の計算効率は除算よりも高くなりますが、ビット演算の優先順位は加算および減算よりも低くなります。追加があります括弧に注意してください

2. Quaternion.Euler によって渡される Vector3 型は、オイラー回転角に相当します。ベクトルに乗算された戻り値は、ベクトルが対応する軸上で回転された後のベクトルに等しくなります。クォータニオンが左側にあることに注意してください。乗算記号のベクトルが右側にある場合、位置を交換することはできません。上記のコードは、独自の z 軸を中心にある角度だけ回転した、transform.right のベクトルを計算します。

5.リング弾幕

public void FireCircle(int bulletCount,float angleOffset)
    {
        float angle=360/bulletCount;
        for (int i = 0; i < bulletCount; i++)
        {
            Vector3 dir = Quaternion.Euler(new Vector3(0, 0, i*angle+angleOffset)) * m_transform.right;
            GameObject bullet = Instantiate(Bullet);
            bullet.transform.position = m_transform.position;
            bullet.transform.right = dir;
        }
    }

 発射ごとに角度オフセットを増やすと、異なる角度オフセットが生成される可能性があります

他の種類の弾幕も継続的に更新されます... 

プロジェクトファイルのダウンロード:

弾丸と武器.unitypackage


要約する

この記事では、2D ゲームで一般的に使用される弾丸と武器の実装を紹介します。便宜上、transform.right に値を直接割り当てて回転を調整します。この操作では、transform.right の軸が指定された方向を向くようにすることしかできません。 3D の違和感の原因となる回転については、次の記事で数学とクォータニオン手法を使用して回転を計算する方法を詳しく紹介します。

おすすめ

転載: blog.csdn.net/m0_72922928/article/details/134676495