U
タブ 02キャラクターと戦闘9 レッスン — タップで開く
05 / 09 · 11 分
キャラクターと戦闘

弾、ダメージ、エフェクト

ここまでのプロジェクト

Assets/

Scripts/

SkillCaster.csProjectile.csSimplePool.cs

Prefabs/

P_Fireball

Data/

Skill_Slash / Dash / Ultimate

すでにあるものこのレッスンで追加

飛び道具とは、前方へ進み、何かに当たり、ダメージを与えて消える prefab のことです。四つの役割を、小さなスクリプト一つで。

アニメーション図

Knight はタイルの列に沿って発射し、人形が被弾し、弾は出てきた pool の中へ戻っていきます。

飛び道具

Projectile.cs
using UnityEngine;

[RequireComponent(typeof(Collider))]
public class Projectile : MonoBehaviour
{
    [SerializeField] private float speed = 18f;
    [SerializeField] private float lifeTime = 4f;
    [SerializeField] private GameObject hitVfx;

    private float damage;
    private GameObject owner;                 // do not hit the shooter

    public void Launch(GameObject shooter, float dmg)
    {
        owner = shooter;
        damage = dmg;
        Destroy(gameObject, lifeTime);        // clean up if it never hits
    }

    void Update()
    {
        transform.position += transform.forward * speed * Time.deltaTime;
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject == owner) return;

        if (other.TryGetComponent(out CharacterStats target))
            target.TakeDamage(damage);

        if (hitVfx != null)
            Instantiate(hitVfx, transform.position, Quaternion.identity);

        Destroy(gameObject);
    }
}
  1. 1

    GameObject▸3D Object▸Sphere を作成し、scale を 0.3、名前は P_Fireball にします。

  2. 2

    Collider で Is Trigger をオンにします——trigger は物理的に反発せず、重なりだけを報告します。

  3. 3

    Projectile.cs をアタッチし、発光する material を与え、Assets/Prefabs にドラッグします。

  4. 4

    シーンからは削除します。prefab は Hierarchy ではなく Project に置くものです。

スキルから発射する

add to SkillData and SkillCaster
// in SkillData.cs
public GameObject projectilePrefab;

// in SkillCaster.TryCast
if (s.projectilePrefab != null)
{
    GameObject go = Instantiate(s.projectilePrefab, castPoint.position, castPoint.rotation);
    go.GetComponent<Projectile>().Launch(gameObject, s.damage);
}

オブジェクトプール —— 連射のカクつきを止める

Instantiate と Destroy はメモリを確保します。毎秒 20 発撃てばガベージコレクションが引っかかるようになります。プールなら同じオブジェクトをずっと使い回せます。

SimplePool.cs
using System.Collections.Generic;
using UnityEngine;

public class SimplePool : MonoBehaviour
{
    [SerializeField] private GameObject prefab;
    [SerializeField] private int prewarm = 20;

    private readonly Queue<GameObject> pool = new Queue<GameObject>();

    void Awake()
    {
        for (int i = 0; i < prewarm; i++)
        {
            GameObject go = Instantiate(prefab, transform);
            go.SetActive(false);
            pool.Enqueue(go);
        }
    }

    public GameObject Get(Vector3 pos, Quaternion rot)
    {
        GameObject go = pool.Count > 0 ? pool.Dequeue() : Instantiate(prefab, transform);
        go.transform.SetPositionAndRotation(pos, rot);
        go.SetActive(true);
        return go;
    }

    public void Release(GameObject go)
    {
        go.SetActive(false);
        pool.Enqueue(go);
    }
}
  • 弾の中の Destroy(gameObject) を、pool.Release(gameObject) を呼ぶ側に置き換えます。

  • 既製版がよければ、Unity 同梱の UnityEngine.Pool.ObjectPool<T> も使えます。

  • 毎秒数回以上生成されるものは何でもプールします:弾、ヒットスパーク、ダメージ数字。

伝わりやすい VFX

  • Particle System の Stop Action → Destroy をオンにすると、一回きりのエフェクトは自分で後始末します。

  • 着弾のスパークは 0.2~0.4 秒が目安です。もっと長いと画面がぐちゃぐちゃになります。

  • ほんの少しのカメラシェイク(0.1 秒)と 0.05 秒のヒットストップがあれば、どんなパーティクルより当たり感が良くなります。

hit-stop
using System.Collections;

IEnumerator HitStop(float seconds)
{
    Time.timeScale = 0.05f;
    yield return new WaitForSecondsRealtime(seconds);   // Realtime! timeScale is frozen
    Time.timeScale = 1f;
}

// call it
StartCoroutine(HitStop(0.05f));

よくあるミス

  • 弾が撃った本人に当たる:Knight 自身のコライダーの内側で生成されるため、OnTriggerEnter が術者に対して発火します。体のすぐ前に置いた castPoint から生成し、transform.root が術者であるヒットは無視してください。

  • プールから戻ってきた弾は古い状態を引きずっています。OnEnable で位置・回転・すべてのタイマーをリセットしないと、二発目は即座に消えるか、あらぬ方向へ飛びます。

  • プールに切り替えた後も Destroy(gameObject, lifeTime) が残っていると、外れた弾がプールのオブジェクトごと破棄してプールが枯れていきます。Destroy は二か所とも pool.Release に置き換え、時間差の方は Invoke かコルーチンで呼んでください。

  • 速い弾は薄いコライダーをすり抜けます。30 m/s・30 FPS なら弾は 1 フレームで 1 メートル進み、壁を飛び越すことがあります。Rigidbody の MovePosition で動かして Collision Detection を Continuous にするか、速度を常識的な範囲に抑えてください。