練習課題:負けることもある戦い
Assets/
Scripts/
everything from tab 01–02
すでにあるものこのレッスンで追加
Arena.unity にそのまま留まって、このタブのすべてを同時に動かします:Knight は移動してアニメーションし、3 つのスキルはクールダウン付き、3 体の Dummy はヘルスバーを掲げ、アーティファクトは 2 つ——Mage と Archer もキー 2 と 3 でちゃんとそこにいます。
再利用するもの
タブ 02 のすべてが、一つのシーンの中で同時に動き続けている状態です。
4 回に分けて組み立てる
1 回目 —— キャラクターを動かす
Knight にはこのタブのレッスン 01 で付けたモデル、CharacterController、CameraTarget がすでにあります。Assets/Prefabs で prefab のままになっているか確認してください。
CharacterController は体格に合わせてサイズを調整し、PlayerMovement に歩行 / 走行 / ジャンプと重力を持たせます。
Animator には Speed で駆動する Locomotion のブレンドツリーを持たせ、Jump と Attack も足します。
Knight に CharacterStats を載せ、maxHealth 200、attack 25、defense 8、moveSpeed 5。Mage と Archer は SimpleMover のままステータスなし——今はただの飾りです。
2 回目 —— 手触りのまったく違う 3 つのスキル
ScriptableObject のアセット 3 つ、まったく別種の役割が 3 つです。3 つとも球範囲にダメージを与えるだけなら、スキルは 1 つで名前だけが 3 つになります。
Skill_Slash Q cooldown 1.5 mana 5 damage 30 range 3 melee sphere in front
Skill_Bolt E cooldown 4.0 mana 20 damage 55 projectile prefab
Skill_Nova R cooldown 12 mana 45 damage 90 range 6 hits everything around// in SkillData.cs
public enum SkillShape { MeleeArc, Projectile, Nova }
public SkillShape shape = SkillShape.MeleeArc;
// in SkillCaster.TryCast, replace the single ApplyDamage call
switch (s.shape)
{
case SkillShape.MeleeArc:
HitSphere(transform.position + transform.forward * s.range * 0.5f, s.range, s);
break;
case SkillShape.Projectile:
GameObject go = Instantiate(s.projectilePrefab, castPoint.position, castPoint.rotation);
go.GetComponent<Projectile>().Launch(gameObject, s.damage);
break;
case SkillShape.Nova:
HitSphere(transform.position, s.range, s);
break;
}
void HitSphere(Vector3 center, float radius, SkillData s)
{
foreach (Collider hit in Physics.OverlapSphere(center, radius, enemyLayers))
if (hit.TryGetComponent(out CharacterStats target))
target.TakeDamage(s.damage);
}3 回目 —— Dummy に少しだけ反撃させる
Enemy レイヤーに 3 体の Dummy を置き、それぞれに CharacterStats、ワールド空間のヘルスバー、HitFlash を付けます。
maxHealth を 80、120、200 とバラバラにして、自分のダメージ数を実感できるようにします。
倒したら Dummy を無効化し、3 秒後に復活させて、テストを続けられるようにします。
using System.Collections;
using UnityEngine;
public class Respawner : MonoBehaviour
{
[SerializeField] private CharacterStats stats;
[SerializeField] private float delay = 3f;
[SerializeField] private GameObject[] toHide;
void OnEnable() => stats.OnDied += HandleDeath;
void OnDisable() => stats.OnDied -= HandleDeath;
void HandleDeath() => StartCoroutine(Revive());
IEnumerator Revive()
{
foreach (GameObject g in toHide) g.SetActive(false);
yield return new WaitForSeconds(delay);
stats.currentHealth = stats.finalMaxHp;
foreach (GameObject g in toHide) g.SetActive(true);
}
}4 回目 —— 効果が目に見えるアーティファクト 2 つ
Artifact_Ember:Attack を +40(固定値)。Artifact_Swift:MoveSpeed +25%、Attack +15%。
キー 7 と 8 に Equip / Unequip を割り当て、プレイ中に付け外しできるようにします。
スキルは base の attack ではなく finalAttack を読むこと——でないと装備しても何も変わりません。
// in SkillCaster
float outgoing = s.damage * (myStats.finalAttack / 100f + 1f);
target.TakeDamage(outgoing);完成の条件
- ✓Knight が歩き、Shift で走り、ジャンプして、アニメーションが実際の動きと合っている——しかもキー 2 と 3 は今も Mage と Archer に操作を渡せる。
- ✓Q、E、R は目に見えて違う動きをし、UI にもそれぞれ別々のクールダウンリングが出る。
- ✓マナ不足やクールダウン中のキャストは拒否される——プレイヤーにも理由が分かります。
- ✓ヒットのたびにダメージ数値、白いフラッシュ、減るヘルスバーが見える。
- ✓HP が 0 になった Dummy は消え、3 秒後に全 HP で復活する。
- ✓アーティファクトを装備すると Inspector の finalAttack が変わり、画面のダメージ数値も変わる。
- ✓外すとすべての数値が完全に元の値へ戻る——ズレが残りません。
- ✓あらゆるキーを 2 分間連打したあとも、Console にエラーが出ない。
狙うべき数値
| 項目 | 目標値 | 理由 |
|---|---|---|
| 120 HP の Dummy を倒すまでの時間 | 4–8 s | 4 秒以下だとスキルが無意味に、8 秒を超えるとだるく感じます。 |
| 入力 → 目に見える反応 | < 100 ms | これ以上遅いと、ボタンが壊れたのかと感じます。 |
| アーティファクトによるダメージ変化 | ≥ 20% | 見えないボーナスは報酬とは言えません。 |
| キャスト 1 回あたりの GC 割当 | 0 B | 弾もダメージ数字もプールし、Profiler で確認してください。 |
もっとやりたい人へ
1 体の Dummy に Chase 状態を持たせる:8 m 以内まで近づくと追いかけてくる。
回復する 4 つ目のスキルを追加——同じ SkillData をダメージマイナスで使い回します。
Nova が AddExplosionForce で Dummy を吹き飛ばすようにする。
アーティファクトのアイコンにカーソルを合わせると、BuildTooltip が作るツールチップを表示する。