Week07_LevelUp - M-634/unity-game-dev-tutorial GitHub Wiki

📘 Week07 - 成長・レベルアップシステム(経験値アイテム吸収+スキル強化)


🎯 今週の目的

  • 敵を倒すと経験値アイテム(Gem)が出現
  • プレイヤーが近づくと自動で吸収され、経験値が加算される
  • 経験値がたまるとレベルアップ
  • レベルに応じてスキル性能(発射間隔や速度)が強化される
  • DOTweenを使って吸収演出をなめらかにする

🛠 授業の流れ


🔧 1. DOTweenの導入

DOTween は Unity 向けのアニメーション/Tween ライブラリで、
「移動・回転・色変更などをスムーズに実装できる」強力なツールです。
今回は経験値アイテムがプレイヤーに吸い寄せられる演出に使用します。


📥 インストール手順(初回のみ)

  1. Unity メニューから
    Tools > Demigiant > DOTween Utility Panel を選択
  2. パネルが開いたら、右上の 「Setup DOTween...」 をクリック
  3. Unity が自動でライブラリを初期化・設定してくれます

🔸 パッケージ導入時に Assets > Plugins > DOTween フォルダが生成されます
🔸 もしメニューが表示されない場合は 公式サイト から再取得も可能


✏ スクリプトへの記述方法

DOTween を使うスクリプトの先頭で、以下のように名前空間を追加してください。

using DG.Tweening;

✅ 使用例(今回の授業で使う)

transform.DOMove(_player.position, 0.5f).SetEase(Ease.InOutSine);
  • DOMove():オブジェクトを指定位置に一定時間で移動させる
  • SetEase():加速や減速のカーブを設定(自然な動きになる)

🔗 参考リンク


💎 2. 経験値アイテムの作成(ExpPickup.cs)

using UnityEngine;
using DG.Tweening;

public class ExpPickup : MonoBehaviour
{
    [SerializeField] private int _expValue = 5;
    private Transform _player;
    private bool _isMoving = false;

    private void Start()
    {
        _player = GameObject.FindGameObjectWithTag("Player")?.transform;
    }

    private void Update()
    {
        if (_isMoving || _player == null) return;

        float distance = Vector2.Distance(transform.position, _player.position);
        if (distance < 3f)
        {
            _isMoving = true;
            transform.DOMove(_player.position, 0.5f).SetEase(Ease.InOutSine);
        }
    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag("Player"))
        {
            LevelManager levelManager = FindObjectOfType<LevelManager>();
            levelManager?.AddExp(_expValue);
            Destroy(gameObject);
        }
    }
}

💥 3. 敵を倒したときに経験値をドロップ(EnemyDamage.cs)

using UnityEngine;

public class EnemyDamage : MonoBehaviour
{
    [SerializeField] private int _hp = 3;
    [SerializeField] private GameObject _expPickupPrefab;

    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag("Bullet"))
        {
            _hp--;
            if (_hp <= 0)
            {
                Instantiate(_expPickupPrefab, transform.position, Quaternion.identity);
                Destroy(gameObject);
            }
        }
    }
}

📈 4. レベルと経験値の管理(LevelManager.cs)+ Singleton 化

レベル情報はゲーム中1つだけ存在し、常にどこからでも参照される必要があります。
このようなケースに適しているのが Singleton(シングルトン)パターン です。


❓ Singletonとは?

  • インスタンスが1つだけ存在することを保証する仕組み
  • 他のスクリプトから LevelManager.Instance のようにして簡単にアクセスできる
  • UIやスコア、ゲームマネージャーなどでもよく使われるパターン

✅ LevelManager.cs(Singletonバージョン)

using UnityEngine;
using TMPro;

public class LevelManager : MonoBehaviour
{
    [SerializeField] private TextMeshProUGUI _levelText;

    private int _level = 1;
    private int _exp = 0;
    private int _expToNext = 10;

    public int CurrentLevel => _level;

    public void AddExp(int amount)
    {
        _exp += amount;
        if (_exp >= _expToNext)
        {
            LevelUp();
        }
        UpdateUI();
    }

    private void LevelUp()
    {
        _level++;
        _exp = 0;
        _expToNext += 10;
        Debug.Log("Level Up! 現在のレベル: " + _level);
    }

    private void UpdateUI()
    {
        if (_levelText != null)
        {
            _levelText.text = $"Lv. {_level}";
        }
    }
}

🧠 5. スキル成長データの設定(AutoAttackSkillData.cs)

using UnityEngine;
using System.Collections.Generic;

[CreateAssetMenu(fileName = "NewSkill", menuName = "Skills/AutoAttackSkill")]
public class AutoAttackSkillData : ScriptableObject
{
    public GameObject bulletPrefab;
    public List<float> intervalPerLevel;
    public List<float> speedPerLevel;
    public Vector2 direction = Vector2.right;

    public float GetInterval(int level)
    {
        return intervalPerLevel[Mathf.Clamp(level - 1, 0, intervalPerLevel.Count - 1)];
    }

    public float GetSpeed(int level)
    {
        return speedPerLevel[Mathf.Clamp(level - 1, 0, speedPerLevel.Count - 1)];
    }
}

🔫 6. スキルのレベル連動処理(PlayerAutoAttack.cs)

using UnityEngine;
using System.Collections.Generic;

public class PlayerAutoAttack : MonoBehaviour
{
    [SerializeField] private List<AutoAttackSkillData> _skills = new();
    [SerializeField] private Transform _muzzle;

    private List<float> _timers = new();
    private LevelManager _levelManager;

    private void Start()
    {
        _levelManager = FindObjectOfType<LevelManager>();
        foreach (var _ in _skills)
        {
            _timers.Add(0f);
        }
    }

    private void Update()
    {
        int currentLevel = _levelManager?.CurrentLevel ?? 1;

        for (int i = 0; i < _skills.Count; i++)
        {
            _timers[i] += Time.deltaTime;

            if (_timers[i] >= _skills[i].GetInterval(currentLevel))
            {
                Shoot(_skills[i], currentLevel);
                _timers[i] = 0f;
            }
        }
    }

    private void Shoot(AutoAttackSkillData skill, int level)
    {
        GameObject bullet = Instantiate(skill.bulletPrefab, _muzzle.position, Quaternion.identity);
        Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
        rb.velocity = skill.direction.normalized * skill.GetSpeed(level);
    }
}

✅ 動作確認ガイド

  • ✅ 敵を倒すと宝石型の経験値アイテムが出現する
  • ✅ プレイヤーが近づくと DOTween で吸い寄せられるように動く
  • ✅ 接触すると経験値が加算され、アイテムは消える
  • ✅ レベルアップ時に Console に「Level Up!」が出力される
  • ✅ 発射間隔または弾速がレベルに応じて変化する
  • ✅ 画面上部に現在の Lv が表示されている

📝 課題

  • 経験値アイテムを吸収してレベルアップできるようにしよう
  • レベルアップ時にスキルの性能が変化するように調整しよう
  • DOTween を使った吸収演出をなめらかに実装しよう

🔗 参考リンク

⚠️ **GitHub.com Fallback** ⚠️