# おまけ

# C#の「イベント」（event）

## 概念

ここまで、C＃で学んだ主な概念は

- メンバー変数：あるインスタンス（オブジェクト）のデータを保持するもの 
    - 体力
    - アイテムの一覧
    - 速度
    - 色
    - など
- メソッド：あるインスタンス（オブジェクト）が「できる」こと。データを処理する「関数」 
    - 走る
    - ジャンプ
    - 攻撃
    - フレーム毎の更新（Update）
    - 初期化（Start）
    - など
- クラス：インスタンスの「図形」であり、`new` (一般のC#) または `Instantiate`（Unity の `Monobehavior` / プレハブ）で具体化し、インスタンスになる。メンバー変数とメソッドのかたまりとも考えらる。 
    - プレーヤー
    - 敵
    - 罠
    - 武器
    - 回復アイテム
    - など

ここで、新しい C#の機能「イベント」を学ぶ。イベントは「<span style="color:rgb(22,145,121);">**あるインスタンスが通知できること**</span>」と考えらる。次の例について考えてみてください。

格闘ゲームを開発中に、「体力」（`HitPoints`）を実装しようとする。ここで：

- 人間プレーヤーの体力がゼロになると、プレーヤーの負けになる（ゲームオーバー）
- CPUプレーヤーの体力がゼロになると、プレーヤーの勝ちになる（ゲームクリア）

体力は変わらないので、Enemy と Player のゲームオブジェクトは全く同じ体力を使うので、同じ「`HitPoints`」のスクリプトをアタッチする

```c#
using UnityEngine;

// 体力クラス
public class HitPoints : MonoBehaviour
{
    // 体力の値
    private int hp;

    private void Start()
    {
        hp = 100;
    }

    // 現在の体力を返す
    public int GetHP()
    {
        return hp;
    }

    // ダメージを受ける
    public void Hit(int damage)
    {
        hp = Mathf.Max(0, hp -damage);
    }
}
```

こうすると、ゲーム監督クラス「GameManager」では、どれの勝ちなのか確認したいので、Update で定期的に問い合わせるしかない。

```c#
using UnityEngine;

// ゲーム監督
public class GameManager : MonoBehaviour
{
    // 人間プレーヤーの体力
    [SerializeField] 
    private HitPoints playerHP;

    // CPUプレーヤーの体力
    [SerializeField] 
    private HitPoints cpuHP;

    private void Update()
    {
        // プレーヤーの負け
        if (playerHP.GetHP() == 0)
        {
            Debug.Log("YOU LOSE!");
        }

        // CPUの負け
        if (cpuHP.GetHP() == 0)
        {
            Debug.Log("YOU WIN!");
        }
    }
}
```

この作り方で問題なく、ゲームが動くが、体力が**<span style="color:rgb(186,55,42);">変わっていないのに、何度も問い合わせるのは無駄な処理</span>**になる。逆に、体力が**<span style="color:rgb(22,145,121);">変わった瞬間</span>**にGameManagerに知らせ、**<span style="color:rgb(22,145,121);">処理すれば効率が良い</span>**！

<table id="bkmrk-update%E3%81%A7%E5%95%8F%E3%81%84%E5%90%88%E3%82%8F%E3%81%9B%E3%82%8B-%C2%A0-even" style="border-collapse:collapse;width:100%;border-width:0px;"><colgroup><col style="width:50%;"></col><col style="width:50%;"></col></colgroup><tbody><tr><td style="border-width:0px;">**Updateで問い合わせる**

<table style="border-collapse:collapse;width:100.374%;border-width:0px;background-color:rgb(255,255,255);"><colgroup><col style="width:99.7519%;"></col></colgroup><tbody><tr><td style="border-width:0px;">**![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/pv2image.png)**</td></tr><tr><td style="border-width:0px;">**![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/ax6image.png)**</td></tr><tr><td style="border-width:0px;">**![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/4animage.png)**</td></tr></tbody></table>

</td><td style="border-width:0px;">**event で連絡待ち**

<table style="border-collapse:collapse;width:100%;border-width:0px;background-color:rgb(255,255,255);"><colgroup><col style="width:99.7525%;"></col></colgroup><tbody><tr><td style="border-width:0px;">**[![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/cZCimage.png)](https://class.illogic.games/uploads/images/gallery/2026-06/cZCimage.png)**</td></tr><tr><td style="border-width:0px;">**[![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/iznimage.png)](https://class.illogic.games/uploads/images/gallery/2026-06/iznimage.png)**</td></tr><tr><td style="border-width:0px;">**![image.png](https://class.illogic.games/uploads/images/gallery/2026-06/scaled-1680-/4animage.png)**

</td></tr></tbody></table>

</td></tr></tbody></table>

## 仕組み

イベントを実装するには、２つのクラスが関連する：「イベントが発生する側」と「イベントを聞き取る側」

### イベントを発生する側

イベントを発生するクラスは「通知ができる」ことを**<span style="color:rgb(35,111,161);">宣言</span>**しないといけない。または、イベントの条件が満たしたら、イベントを**<span style="color:rgb(35,111,161);">発生</span>**しなければならない。

#### 宣言

クラスの先頭に「event」キーワードを使い、イベントを宣言する

<span style="text-decoration:underline;">書式（引数なし）</span>

```c#
public event System.Action イベント名;
```

<span style="text-decoration:underline;">書式（引数あり）</span>

```c#
public event System.Action<引数型1, 引数型2, ...> イベント名;
```

今回の格闘ゲームだと：

```c#
// 体力クラス
public class HitPoints : MonoBehaviour
{
    // 体力がゼロになった時に発生するイベント。
    // 引数はゼロになった体力コンポーネントの参照
    public event System.Action<HitPoints> ZeroHP;
        
    // 体力の値
    private int hp;

    // （後は同じ）
}
```

ここの例だと：

- 「`ZeroHP`」というイベントを宣言した。このイベントは体力がゼロになった瞬間に発生するべき
- 引数の型は「`HitPoints`」の参照である。これでどの体力コンポーネントゼロになったかを引数として渡すできる。

（※）引数は任意であり、必要なときに使ってください。不要なら「引数なし」の宣言方法を利用してください。

#### 発生

条件を満たしたら、イベントを発生しなければならない。これは、「`Invoke`」で行う。

<span style="text-decoration:underline;">書式（引数なし）</span>

```c#
イベント名?.Invoke();
```

<span style="text-decoration:underline;">書式（引数あり）</span>

```c#
イベント名?.Invoke(引数1, 引数2, ...);
```

今回の格闘ゲームだと：

```c#
// ダメージを受ける
public void Hit(int damage)
{
    hp = Mathf.Max(0, hp -damage);
   
    // 体力がゼロになったら、イベントを発生
    // 引数は「this」（自分の参照）
    if (hp == 0)
        ZeroHP?.Invoke(this);
}
```

ここの例だと：

- ダメージを喰ったら、体力がゼロになったのかを確認する
- もしゼロだったら、`ZeroHp` のイベントを発生する。引数は自分（`this`）の参照

### イベントを聞き取る側

イベントを発生するのはただ半分であり、聞き取る側もある。ここで、イベントに**<span style="color:rgb(35,111,161);">登録・登録解除</span>**とそのイベントを<span style="color:rgb(35,111,161);">**処理**</span>するメソッドを作成しなければならない。

#### 登録・登録解除

まず、定めたイベントを処理するため、先に登録しなければならない。また、イベントが不要になった場合、必ず登録解除してください。

<span style="text-decoration:underline;">書式：登録</span>

```c#
オブジェクト名.イベント名 += イベントを処理するメソッド;
```

<span style="text-decoration:underline;">書式：登録解除</span>

```c#
オブジェクト名.イベント名 -= イベントを処理するメソッド;
```

#### 処理

処理は上記の「イベントを処理するメソッド」で行う。**<span style="color:rgb(186,55,42);">引数はイベント宣言の引数を一致しなければならない</span>**。

今回の格闘ゲームだと：

```c#
using UnityEngine;

// ゲーム監督
public class GameManager : MonoBehaviour
{
    // 人間プレーヤーの体力
    [SerializeField] 
    private HitPoints playerHP;

    // CPUプレーヤーの体力
    [SerializeField] 
    private HitPoints cpuHP;

    private void Start()
    {
        // 人間、またはCPUプレーヤーの体力がゼロになるイベントに登録
        playerHP.ZeroHP += OnZeroHP;
        cpuHP.ZeroHP += OnZeroHP;
    }

    private void OnDestroy()
    {
        // ゲーム監督クラス廃止のとき、イベントに登録解除する
        playerHP.ZeroHP -= OnZeroHP;
        cpuHP.ZeroHP -= OnZeroHP;
    }
    
    // イベントを処理
    // sender: ゼロになった体力コンポーネント
    private void OnZeroHP(HitPoints sender)
    {
        // プレーヤーの負け
        if (sender == playerHP)
        {
            Debug.Log("YOU LOSE!");
        }

        // CPUの負け
        if (sender == cpuHP)
        {
            Debug.Log("YOU WIN!");
        }
    }
    
    // Updateは要らなくなった
    private void Update()
    {
    }
}
```

もちろん、別々の処理メソッドを作れるので、以下の書き方も可能：

```c#
using UnityEngine;

// ゲーム監督
public class GameManager : MonoBehaviour
{
    // 人間プレーヤーの体力
    [SerializeField] private HitPoints playerHP;

    // CPUプレーヤーの体力
    [SerializeField] private HitPoints cpuHP;

    private void Start()
    {
        // 人間、またはCPUプレーヤーの体力がゼロになるイベントを登録
        playerHP.ZeroHP += OnPlayerZeroHP;
        cpuHP.ZeroHP += OnCPUZeroHP;
    }

    private void OnDestroy()
    {
        // ゲーム監督クラス廃止のとき、イベントの登録解除を行う
        playerHP.ZeroHP -= OnPlayerZeroHP;
        cpuHP.ZeroHP -= OnCPUZeroHP;
    }

    // イベントを処理（プレーヤーの負け）
    // sender: ゼロになった体力コンポーネント
    private void OnPlayerZeroHP(HitPoints sender)
    {
        Debug.Log("YOU LOSE!");
    }

    // イベントを処理（CPUの負け）
    // sender: ゼロになった体力コンポーネント
    private void OnCPUZeroHP(HitPoints sender)
    {
        Debug.Log("YOU WIN!");
    }

    // Updateは要らなくなった
    private void Update()
    {
    }
}

```

## 練習

まず、[このプロジェクト](https://class.illogic.games/attachments/15)をダウンロードし、Unityに追加してください。このミニゲームでは、車が走行し、「ゴール」を通過すれば、`Debug.Log` でメッセージが表示される。

ここで、`event` を使用し、画面上でレースの結果を表示しましょう。

1. 発生する側：車の通過を検知する「CarDetector」にイベントを宣言し、車を通過したらイベントを発生
2. 聞き取る側：UIManager で CarDetector のイベントに登録し、車を通過したら、表示する

<details id="bkmrk-cardetector-%E3%81%AE%E7%AD%94%E3%81%88-%2F%2F-%E8%BB%8A"><summary>CarDetector の答え</summary>

```c#
// 車を検知する
public class CarDetector : MonoBehaviour
{
    // 車を検知したイベントを宣言
    // 引数：通過した車
    public event System.Action<RaceCar> CarDetected; 
    
    private void OnTriggerEnter(Collider other)
    {
        // RaceCarのコンポーネントがついている？
        RaceCar raceCar = other.GetComponent<RaceCar>();
        if (raceCar　== null)
            return;

        // 検知しました！
        CarDetected?.Invoke(raceCar);
    }
}
```

</details><details id="bkmrk-uimanager-%E3%81%AE%E7%AD%94%E3%81%88-%2F%2F-ui%E3%82%92"><summary>UIManager の答え</summary>

```c#
// UIを管理する
public class UIManager : MonoBehaviour
{
    // 車の順位を表示
    [SerializeField] private TextMeshProUGUI[] carPosition;

    // 車の通過を検知するため
    [SerializeField] private CarDetector detector;

    // 車の順位
    private int position = 0;
    
    private void Start()
    {
        // すべて非表示する
        foreach (var item in carPosition)
            item.gameObject.SetActive(false);
        
        // イベントに登録
        detector.CarDetected += OnCarDetected;
    }

    private void OnDestroy()
    {
        // イベントに解除
        detector.CarDetected -= OnCarDetected;
    }

    // ゲームを再開
    public void RestartGame() => 
        SceneManager.LoadScene("Racer");

    // 車が通過したイベントを受け取る
    private void OnCarDetected(RaceCar raceCar)
    {
        ShowCar(raceCar, position);
        position++;
    }

    // 車を定めた位置に表示
    private void ShowCar(RaceCar raceCar, int index)
    {
        var item = carPosition[index];
        item.gameObject.SetActive(true);
        item.color = raceCar.Color;
        item.text = raceCar.name;
    }
}

```

</details>もちろん、聞き取る側がクラス１つだけではなく、どのクラスでもイベントを受け取ることができる。紙吹雪のエフェクトを実装されているので、Confetti クラスでも CarDetector のイベントに登録し、車を通過したら、車の色の紙吹雪を飛ばすようにしましょう。

<details id="bkmrk-confetti-%E3%81%AE%E7%AD%94%E3%81%88-%2F%2F-%E7%B4%99%E5%90%B9%E9%9B%AA%E3%81%AE"><summary>Confetti の答え</summary>

```c#
// 紙吹雪のエフェクトを再生
public class Confetti : MonoBehaviour
{
    // パーティクルエフェクト
    [SerializeField] private ParticleSystem[] effects;

    // 車の検知
    [SerializeField] private CarDetector detector;

    private void Start()
    {
        // イベントに登録
        detector.CarDetected += OnCarDetected;
    }

    private void OnDestroy()
    {
        // 登録解除
        detector.CarDetected -= OnCarDetected;
    }

    // イベントを受け取る
    private void OnCarDetected(RaceCar raceCar)
    {
        Play(raceCar.Color);
    }

    // エフェクト再生
    private void Play(Color color)
    {
        foreach (var effect in effects)
        {
            var main = effect.main;
            main.startColor = color;
            effect.Play();
        }
    }
}
```

</details>

# Unityで待機ができる処理：非同期処理

## 概念

Unityのスクリプトでは、`Start()`、`Update()`、`OnTriggerEnter()` などの定めたときに実行するメソッドがある（初期の時、フレーム毎、トリガーに入った）

この機能が大変便利であるが、ゲームを開発中には、上記のメソッドを呼び出される瞬間ではなく、少し時間たってから実行したい場合もたくさんある。

例えば：

- 体力がゼロになった場合、すぐにゲームオーバーを表示するのではなく、主人公が倒れるアニメーションを再生し終わってからゲームオーバーを表示したい。
- ローディング画面を表示し、次のステージの準備ができてからシーンを切り替える。
- サイコロをランダムに回し、プレーヤーを「停止」を押してからすごろくのコマを進む。

この場合は「待機」が発生する。何かの条件を満たすまで待たないといけない。Unityでは２つの作戦がある：コルーチン と Awaitable.

### コルーチン

コルーチンの書き方は以下の通りである

```c#
private IEnumerator コルーチン名 (引数)
{
  // 処理
  // yield return 待機命令
  // 処理
  // yield return 待機命令
  // ...
}
```

普通のメソッドとよく似ているが、戻り値は必ず `IEnumerator` になる。そして、中には普通の処理を行い、待機命令を見つけたらコルーチンが一時停止し、待機が終わったら再開する流れになる。

なお、コルーチンを開始するのは、普通のメソッドとして呼び出すことではなく、以下の通りになる：

```c#
StartCoroutine(コルーチン名(引数));
```

また、待機命令は以下から選択ができる

<table class="align-center" id="bkmrk-%E5%91%BD%E4%BB%A4-%E5%8A%B9%E6%9E%9C-yield-return-n" style="border-collapse:collapse;width:74.0476%;height:122px;border-width:1px;"><thead><tr style="height:29.4667px;"><td class="align-center" style="width:56.4929%;height:29.4667px;">**命令**</td><td class="align-center" style="width:43.6171%;height:29.4667px;">**効果**</td></tr></thead><tbody><tr style="height:30.8px;"><td class="align-left" style="width:56.4929%;height:30.8px;">`yield return null;`

</td><td class="align-left" style="width:43.6171%;height:30.8px;">次のフレームまで待つ</td></tr><tr style="height:30.8px;"><td class="align-left" style="width:56.4929%;height:30.8px;">`yield return new WaitForSeconds(秒数);`</td><td class="align-left" style="width:43.6171%;height:30.8px;">定めた秒数を待機する</td></tr><tr style="height:30.8px;"><td class="align-left" style="width:56.4929%;height:30.8px;">`yield return StartCoroutine(他のコルーチン);`</td><td class="align-left" style="width:43.6171%;height:30.8px;">別のコルーチンが終わるまで待機する</td></tr></tbody></table>

この他にもいくつかの待機命令があるが、最も多く使うのは上記に３つである。

#### 例：READY → GO！

あるゲーム開始の時に、「READY」を２秒を表示した後、「GO!」を表示１秒表示し、その後ゲームを開始ができる処理を実装しようとしたら：

```c#
// ゲーム開始
public void StartGame()
{
	// コルーチンを開始
	StartCoroutine(ReadyGo());
}

// レディー → ゴー！を表示するコルーチン
private IEnumerator ReadyGo()
{
	// READY を表示
	Debug.Log("READY");
	
	// 2秒待機
	yield return new WaitForSeconds(2.0f);
	
	// GO! を表示
	Debug.Log("GO!");
	
	// 1秒待機
	yield return new WaitForSeconds(1.0f);

	// 実際にゲームを開始
	Debug.Log("ゲーム開始");
}
```

### Awaitable

Awaitable（アウェイタブル）は改善されたコルーチンといわれる。基本的に同じことするのが、書き方が少し難しくなる。

```c#
private async Awaitable 非同期処理 (引数)
{
  // 処理
  // await 待機命令
  // 処理
  // await 待機命令
  // ...
}
```

そして、開始するのは、普通のメソッドと同じである

```c#
非同期処理 (引数);
```

また、待機命令は以下の通りになる：

<table class="align-center" id="bkmrk-%E5%91%BD%E4%BB%A4-%E5%8A%B9%E6%9E%9C-await-awaitabl" style="border-collapse:collapse;width:74.0476%;height:122px;border-width:1px;"><thead><tr style="height:29.4667px;"><td class="align-center" style="width:56.4929%;height:29.4667px;">**命令**</td><td class="align-center" style="width:43.6171%;height:29.4667px;">**効果**</td></tr></thead><tbody><tr style="height:30.8px;"><td class="align-left" style="width:56.4929%;height:30.8px;">`await Awaitable.NextFrameAsync();`

</td><td class="align-left" style="width:43.6171%;height:30.8px;">次のフレームまで待つ</td></tr><tr style="height:30.8px;"><td class="align-left" style="width:56.4929%;height:30.8px;">`await Awaitable.<span class="sig-kw">WaitForSecondsAsync</span>(秒数);`</td><td class="align-left" style="width:43.6171%;height:30.8px;">定めた秒数を待機する</td></tr><tr style="height:30.8px;"><td class="align-left" style="width:56.4929%;height:30.8px;">`await 他の非同期処理(引数);`</td><td class="align-left" style="width:43.6171%;height:30.8px;">別のAwaitableが終わるまで待機する</td></tr></tbody></table>

#### 例：READY → GO！

コルーチンで作った処理をAwaitableにすると：

```c#
// ゲーム開始
public void StartGame()
{
	// 処理開始
	ReadyGo();
}

// レディー → ゴー！を表示する非同期メソッド
private async Awaitable ReadyGo()
{
	// READY を表示
	Debug.Log("READY");
	
	// 2秒待機
	await Awaitable.WaitForSecondsAsync(2.0f);
	
	// GO! を表示
	Debug.Log("GO!");
	
	// 1秒待機
	await Awaitable.WaitForSecondsAsync(1.0f);

	// 実際にゲームを開始
	Debug.Log("ゲーム開始");
}
```

# 夏休み・練習課題

義務ではないが、練習不足と思う学生にはおすすめ

目標

- Unityでの開発と調査能力を向上する
- 先生が教えていない課題もあるので、少し調べる必要な場合もある
- 迷わず、判断力、開発スピードをUPする
- 自分の今のゆるいところを明確し、固める

ポイント：

- 順番気にせず、答えれるのを先に答えて、難しい質問は最後に残す
- 最後に回答があるが、課題により、いくつかの解決方法がある。
- 下記の解決は、あくまで先生のやり方である。

### C# / スクリプト（言語、およびUnityのの基礎）

次の質問をC#で回答してください（1行〜5行程度）

例：２つの整数を足してください：

```c#
int sum = 20 + 15;
```

1\. ゲーム実行中に「enemyPrefab」プレハブから具体的なゲームオブジェクト「enemy」を作成してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-gameobject-enemy-"><summary>答え</summary>

```c#
GameObject enemy = Instantiate(enemyPrefab);
```

</details>2\. 「ball」というゲームオブジェクトからRigidbodyのコンポーネントを取得てください。

<details id="bkmrk-%E7%AD%94%E3%81%88-rigidbody-rb-%3D-ba"><summary>答え</summary>

```c#
Rigidbody rb = ball.GetComponent<Rigidbody>();
```

</details>3\. 取得したRigidbodyの速度は、X軸で毎秒5単位、Z軸で毎秒-1単位にしてください。また、Y軸の速度を変えないでください（元の通りに保持する）

<details id="bkmrk-%E7%AD%94%E3%81%88-vector3-velocity-"><summary>答え</summary>

```c#
Vector3 velocity = rb.linearVelocity;
velocity.x = 5;
velocity.z = -1;
rb.linearVelocity = velocity;
```

</details>4\. 取得したRigidbodyをY軸周りに90度回転してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-rigidbody%E3%81%A7%E5%8B%95%E3%81%8B%E3%81%99%E5%A0%B4%E5%90%88%E3%80%81t"><summary>答え</summary>

Rigidbodyで動かす場合、Transformをいじるのではなく、Rigidbodyのメソッドを使わないといけない。Rigidbodyを回転させるには「MoveRotation」を使用する。

```c#
rb.MoveRotation(Quaternion.Euler(0, 90, 0));
```

</details>5\. スペースキーを押したら、「ball」ゲームオブジェクトを削除する処理を書いてください。

<details id="bkmrk-%E7%AD%94%E3%81%88-if%28input.getkeydo"><summary>答え</summary>

```c#
if(Input.GetKeyDown(KeyCode.Space)
{
    Destroy(ball);
}
```

</details>6.「box」というゲームオブジェクトの位置を「x=1, y=0.5, z=0」にしてください。

<details id="bkmrk-%E7%AD%94%E3%81%88-box.transform.pos"><summary>答え</summary>

```c#
box.transform.position = new Vector3(1, 0.5f, 0);
```

</details>7\. 「box」というゲームオブジェクトの位置を「x=5」にし、「y」と「z」は元の数値のままにしてください。

<details id="bkmrk-%E7%AD%94%E3%81%88-vector3-pos-%3D-box"><summary>答え</summary>

```c#
Vector3 pos = box.transform.position;
pos.x = 5;
box.transform.position = pos;
```

</details>8.「box」というゲームオブジェクトの位置を現在の位置よりも、左の方に1単位に動かしてください。

<details id="bkmrk-%E7%AD%94%E3%81%88-%E5%B0%91%E3%81%AA%E3%81%8F%E3%81%A6%E3%82%82%E3%80%81%EF%BC%92%E3%81%A4%E3%81%AE%E3%82%84%E3%82%8A%E6%96%B9%E3%81%8C%E3%81%82%E3%82%8B-v"><summary>答え</summary>

少なくても、２つのやり方がある

```c#
Vector3 pos = box.transform.position;
pos.x -= 1;
box.transform.position = pos;
```

 または

```c#
box.transform.Translate(-1, 0, 0);
```

</details>9\. Unityで「1〜100」の間に乱数10個を生成してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-for-%28int-i-%3D-0%3B-i"><summary>答え</summary>

```c#
for (int i = 0; i < 10; i++)
{
  // 整数の場合は最大値を除く（1～100）
  int rnd_i = Random.Range(1, 101); 

  // 小数型の場合は、最大値を含む
  float rnd_f = Random.Range(1.0f, 100.0f);
}  
```

</details>10\. 「cat」と「mouse」の２つのゲームオブジェクトの間の距離を求めてください。

<details id="bkmrk-%E7%AD%94%E3%81%88-vector3-catpos-%3D-"><summary>答え</summary>

```c#
Vector3 catPos = cat.transform.position;
Vector3 mousePos = mouse.transform.position;
Vector3 direction = mousePos - catPos

float distance = direction.magnitude;
```

</details>11\. 「cat」と「mouse」の距離が「1.5」単位よりも小さければ、「mouse」は「cat」から「3」単位を離れる。

<details id="bkmrk-%E7%AD%94%E3%81%88-if-%28distance-%3C-1."><summary>答え</summary>

```c#
if (distance < 1.5f)
{
    mousePos = catPos + direction.normalized * 3.0f;
    mouse.transform.position = mousePos;
}
```

</details>12\. 4種類のTシャツのサイズ（Small、 Medium、 Large、 ExtraLarge）を表す列挙型「ShirtSize」を宣言してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-enum-shirtsize-%7B-"><summary>答え</summary>

```c#
enum ShirtSize
{
    Small,
    Medium,
    Large,
    ExtraLarge
}
```

</details>13\. ShirtSizeの列挙から「myShirt」の変数を宣言し、ラージサイズにする

<details id="bkmrk-%E7%AD%94%E3%81%88-shirtsize-myshirt"><summary>答え</summary>

```c#
ShirtSize myShirt = ShirtSize.Large;
```

</details>14\. Debug.Logを使い、Tシャツのサイズにより、日本語でサイズを表示してください：  
 Small：“小”、Medium：”並”、Large：”大”、ExtraLarge：”特大”

<details id="bkmrk-%E7%AD%94%E3%81%88-switch-%28myshirt%29-"><summary>答え</summary>

```c#
switch (myShirt)
{
    case ShirtSize.Small:
        Debug.Log("小");
        break;
    case ShirtSize.Medium:
        Debug.Log("中");
        break;
    case ShirtSize.Large:
        Debug.Log("大");
        break;
    case ShirtSize.ExtraLarge:
        Debug.Log("特大");
        break;
}
```

</details>15\. 入力クラス（Input）を使い、上の矢印キー（↑）を放せば、`Debug.Log`で”JUMP!”というメッセージを表示してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-if-%28input.getkeyu"><summary>答え</summary>

```c#
if (Input.GetKeyUp(KeyCode.UpArrow))
{
    Debug.Log("JUMP!");
}
```

</details>16\. 入力クラス（Input）を使い、InputManager で定義した ”Attack” アクションが行った場合、Debug.Logで”攻撃”を表示してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-if-%28input.getbutt"><summary>答え</summary>

```c#
if (Input.GetButtonDown("Attack"))
{
    Debug.Log("攻撃");
}
```

</details>### シーン、ゲームオブジェクト、コンポーネント

 次の質問を文章で回答してください：

17\. ゲームオブジェクトとコンポーネントの違いを説明してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%EF%BC%9A%E3%82%B7%E3%83%BC%E3%83%B3%EF%BC%88hie"><summary>答え</summary>

<span style="text-decoration:underline;">ゲームオブジェクト</span>：シーン（Hierarchy）の中にある１つのもの（例：プレーヤー、敵対、木、壁、スコアの表示など）

<span style="text-decoration:underline;">コンポーネント</span>：ゲームオブジェクトにアタッチするものであり、ゲームオブジェクトの機能を増やす（例：物理演算：Rigidbody、当たり判定：Collider、サウンド再生：AudioSource、カスタム機能：スクリプト）

</details>18\. Rigidbodyというコンポーネントはいつ使う？

<details id="bkmrk-%E7%AD%94%E3%81%88-%E3%81%82%E3%82%8B%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E7%89%A9%E7%90%86%E7%9A%84%E3%81%AB%E7%A7%BB"><summary>答え</summary>

あるゲームオブジェクトを物理的に移動させたいとき（重力、速度、加速度などで移動させる）</details>19\. Colliderというコンポーネントはいつ使う？

<details id="bkmrk-%E7%AD%94%E3%81%88-%C2%A0%E5%BD%93%E3%81%9F%E3%82%8A%E5%88%A4%E5%AE%9A%E3%81%AE%E5%87%A6%E7%90%86%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AB%E4%BD%BF%E3%81%86%E3%80%82%E8%A1%9D"><summary>答え</summary>

 当たり判定の処理のために使う。衝突モードとトリガーモード２種類がある

</details>20\. コライダーをトリガーモード（isTrigger）をチェックするとなにが変わる？

<details id="bkmrk-%E7%AD%94%E3%81%88-%C2%A0%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E8%A1%9D%E7%AA%81%E3%81%9B%E3%81%9A%E3%80%81%E3%82%B3"><summary>答え</summary>

 ゲームオブジェクトを衝突せず、コライダーをすり抜けるが、その範囲の中に他のゲームオブジェクトが入っているかどうか「IsTriggerXXX」で確認できる。

</details>21\. RigidbodyがないColliderを使える？なぜ？

<details id="bkmrk-%E7%AD%94%E3%81%88-%C2%A0%E4%BD%BF%E3%81%88%E3%82%8B%E3%80%82%E5%BD%93%E3%81%9F%E3%82%8A%E5%88%A4%E5%AE%9A%EF%BC%88%E8%A1%9D%E7%AA%81%EF%BC%89%E3%81%AE%E3%81%BF%E3%81%8C"><summary>答え</summary>

 使える。当たり判定（衝突）のみが発生し、動かないので、壁、床面などに最適

</details>22\. Rigidbodyの`linearVelocity`はなんですか？

<details id="bkmrk-%E7%AD%94%E3%81%88-%E3%83%AA%E3%82%B8%E3%83%83%E3%83%89%E3%83%9C%E3%83%87%E3%82%A3%E3%81%AE%E7%A7%BB%E5%8B%95%E9%80%9F%E5%BA%A6%E3%81%A7%E3%81%82%E3%82%8B%E3%80%82"><summary>答え</summary>

リジッドボディの移動速度である。</details>23\. Rigidbodyの`AddForce`で何ができる？`linearVelocity`と何が違う？

<details id="bkmrk-%E7%AD%94%E3%81%88-%E5%8A%9B%E4%B8%8E%E3%81%88%E3%81%A6%E7%A7%BB%E5%8B%95%E3%81%95%E3%81%9B%E3%82%8B%EF%BC%88%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7"><summary>答え</summary>

力与えて移動させる（ゲームオブジェクトを押したら、引っ張ったりする）。linearVelocityと異なって、Rigidbodyの重さ（質量）の影響が発生する。つまり、AddForceを使う場合、Rigidbodyが重ければ重いほど、動き辛くなるが、linearVelocityを変えると直接に速度が変わるので、重さが関係ない。

</details>24.「プレハブ」とは何ですか？はいつ使う？

<details id="bkmrk-%E7%AD%94%E3%81%88-%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%82%84%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%82%92%E3%82%A2%E3%82%BF%E3%83%83"><summary>答え</summary>

スクリプトやコンポーネントをアタッチし、独立に使えるゲームオブジェクトのかたまりである。プレハブは「図形」として使えることができ、実行中、またはUnityエディタで何度重複ができる。

</details>25\. 「Animation Clip」「Animator」と「Animation Controller」の連携を説明してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-animation-clip%E3%81%AF%EF%BC%91%E3%81%A4"><summary>答え</summary>

- Animation Clipは１つのアニメーションである（例：ジャンプ、走る、歩く、攻撃する）
- Animation Controllerは遷移を管理する（遷移グラフ）。条件（パラメータ）により、Animation Clip の切り替えができる。
- Animatorはゲームオブジェクトにアタッチするコンポーネントで、Animation Controller を設定できる。

</details>26\. UIを作るために、最も重要なコンポーネントを記述してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-canvas%E3%80%82%E3%82%AD%E3%83%A3%E3%83%B3%E3%83%90%E3%82%B9%E3%81%AE%E4%B8%AD%E3%81%AB%E3%80%81%E3%83%9C"><summary>答え</summary>

Canvas。キャンバスの中に、ボタン、テキスト、画像などを配置する。</details>27\. あるRigidbodyの重力が効かないでほしいとき、どうすれば良いでしょうか？

<details id="bkmrk-%E7%AD%94%E3%81%88-unity-%E3%81%AE-inspector"><summary>答え</summary>

Unity の Inspector で「Use Gravity」のチェックを外す

</details>28\. ベタベタの壁を作りたくて、何か衝突したら、弾みなく、衝突したオブジェクトを壁に張り付くような現象を作りたい。どうすれば良いでしょうか？

<details id="bkmrk-%E7%AD%94%E3%81%88-%EF%BC%92%E3%81%A4%E3%81%AE%E8%A7%A3%E6%B1%BA%E7%AD%96%E3%81%8C%E8%80%83%E3%81%88%E3%82%89%E3%82%8C%E3%82%8B%E3%80%82-%E3%82%B9%E3%82%AF%E3%83%AA"><summary>答え</summary>

２つの解決策が考えられる。

- スクリプトで衝突したときに、Rigidbodyが効かなくなる（isKinematic = true)
- PhysicsMaterialで「Bounciness」（弾み）を「０」にする、「Friction」（魔雑）「1」にする

最終的な効果が多少異なるが、べたべたの壁をエフェクトを作成できる。

</details>29\. SpriteRendererとは何でしょうか？

<details id="bkmrk-%E7%AD%94%E3%81%88-2d%E3%81%AE%E7%94%BB%E5%83%8F%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B%EF%BC%882d%E3%82%B2%E3%83%BC%E3%83%A0%E5%B0%82"><summary>答え</summary>

2Dの画像を表示する（2Dゲーム専用のコンポーネント）</details>30\. InspectorでスプライトをY軸で逆さまにしたいなら、どうすれば良い？

<details id="bkmrk-%E7%AD%94%E3%81%88-inspector%E3%81%A7%E3%80%8Csprite"><summary>答え</summary>

Inspectorで「Sprite Renderer」の「Flip Y」をチェックする

Transform の Scale Y を -1 にする手もあるが、コライダーに悪影響がある。

</details>31\. スプライトのモード「Single」と「Multiple」の違いは何でしょうか？

<details id="bkmrk-%E7%AD%94%E3%81%88-single%E3%81%AF%E3%80%8C%E5%8D%98%E4%BD%93%E3%80%8D%E3%81%A8%E3%81%84%E3%81%86%E6%84%8F%E5%91%B3%E3%81%A7"><summary>答え</summary>

Singleは「単体」という意味で、画像の中にスクリプトが１つしかない。Multipleは「複数」という意味であり、同じ画像に中ではたくさんのスプライトが入っている。この場合は、Sprite Editorで切り分けないといけない。</details>32\. シーンを切り替えるために、`LoadScene`を呼び出す前に、どこに追加すれば良いでしょうか？

<details id="bkmrk-%E7%AD%94%E3%81%88-file-%3E-build-prof"><summary>答え</summary>

File &gt; Build Profiles（ファイル &gt; ビルドプロフィール）画面で、「Scene List」の中に切り替えたいシーンを追加しないといけない。

</details>33\. 「タグ」は何に使える？

<details id="bkmrk-%E7%AD%94%E3%81%88-%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E7%89%B9%E5%AE%9A%E3%81%AB%E4%BD%BF%E3%81%86%E3%80%82%E3%80%8C"><summary>答え</summary>

ゲームオブジェクトの特定に使う。「xxxx.CompareTag("タグ")」を使うと、同じタグかどうかを確認できる。

</details>34\. TextMeshProというコンポーネントをなんのために使うのでしょうか？

<details id="bkmrk-%E7%AD%94%E3%81%88-ui%E3%81%A7%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AE%E3%82%B3%E3%83%B3"><summary>答え</summary>

UIでテキストを表示するためのコンポーネントである。

</details>35\. UI の「Anchor」（アンカー）の使い方について説明してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-%C2%A0ui%E3%81%AE%E8%A6%81%E7%B4%A0%EF%BC%88%E7%94%BB%E5%83%8F%E3%80%81%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%80%81%E3%83%9C%E3%82%BF"><summary>答え</summary>

 UIの要素（画像、テキスト、ボタンなど）を画面の角や中央から相対的に配置するためのものである。いろんな画面サイズを対応するため、UIの要素を絶対的ではなく、アンカーを使って、相対的に配置するべき。

</details>36.「Ray」（レイ）というものはなんでしょうか？使い方を説明してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-ray%E3%81%AF%E3%80%8C%E3%83%93%E3%83%BC%E3%83%A0%E3%80%8D%E3%81%AE%E3%82%88%E3%81%86%E3%81%AA%E3%82%82%E3%81%AE%E3%81%A7%E3%80%81"><summary>答え</summary>

Rayは「ビーム」のようなもので、スクリプトで当たり判定を確認できるためにつかう。Rayは「原点」（origin）と「向き」（direction）をもち、Physics.Raycastで何か当たるかどうかを確認できる。

使い道がいっぱいある。例えば、シューティングゲームで、弾が当たったかどうかを確認できるし、画面をクリックすると、3D空間で何を選択したのかを確認できる。

</details>### C# / スクリプト

次の質問を文章で回答してください：

37\. 「`FixedUpdate`」「`Update`」「`LateUpdate`」の違いを説明してください。それぞれいつ使うと思う？

<details id="bkmrk-%E7%AD%94%E3%81%88-update%EF%BC%9A%E3%82%B2%E3%83%BC%E3%83%A0%E3%81%AE%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E6%AF%8E%E3%81%AB"><summary>答え</summary>

- Update：ゲームのフレーム毎に実行する。普通の処理を行うところ。
- FixedUpdate：一定間隔の時間で実行する。物理にかかわる処理（Rigidbodyなど）にはおすすめ。
- LateUpdate：Updateの後に実行する。少し遅れて実行させたい処理はここに書くべき。

</details>38\. ゲームオブジェクトの「`Destroy`」と「`SetActive(false)`」の違いを説明してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-destroy%E3%81%AF%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88"><summary>答え</summary>

- `Destroy`はゲームオブジェクトを完全に削除する。ゲームから抜けて、２度も使えなくなる。
- `SetActive(false)`は一時的に無効にする。ゲームに残り、もう一度使いたいときには `SetActive(true)`で有効ができる。

</details>39\. 「`Time.deltaTime`」とは何でしょうか？

<details id="bkmrk-%E7%AD%94%E3%81%88-%E5%89%8D%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%81%8B%E3%82%89%E9%81%8E%E3%81%94%E3%81%97%E3%81%9F%E6%99%82%E9%96%93%E3%81%A7%E3%81%82%E3%82%8B%E3%80%82"><summary>答え</summary>

前フレームから過ごした時間である。タイマーや速度に依存する移動量などに使う数値。

</details>40\. 「`Time.timeScale = 0`」にすれば、ゲームはどうなる？

<details id="bkmrk-%E7%AD%94%E3%81%88-%E6%99%82%E9%96%93%E3%81%AB%E4%BE%9D%E5%AD%98%E3%81%99%E3%82%8B%E5%87%A6%E7%90%86%E3%81%8C%E6%AD%A2%E3%81%BE%E3%82%8B%E3%80%82%E5%8D%98%E7%B4%94%E3%81%AA"><summary>答え</summary>

時間に依存する処理が止まる。単純なポーズ（Pause）に使える。

</details>41\. 「`public`」と「`private`」の違いについて説明してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-%C2%A0%E3%80%8Cpublic%E3%80%8D%E3%81%A8%E3%80%8Cprivat"><summary>答え</summary>

 「`public`」と「`private`」はアクセス修飾子である。「`public`」のメソッドや変数が他のクラス（スクリプト）からアクセス（読み込みこと、書き込むこと）ができる。一方「`private`」のメソッドと変数は、宣言されたクラスのみでアクセスができ、他のクラスからアクセスができない。

</details>42\. 「`OnCollisionEnter`」と「`OnTriggerEnter`」の違いを説明してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-oncollisionenter%E3%81%AF"><summary>答え</summary>

- `OnCollisionEnter`は衝突が始まるときに呼び出される。（衝突モードのコライダー）
- `OnTriggerEnter`は「Is Trigger」のチェックしたコライダーの中に何か入ったときに呼び出される。（トリガーモードのコライダー）

</details>43\. 「`OnTriggerEnter`」「`OnTriggerStay`」「`OnTriggerExit`」の違いを説明してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-%E3%81%99%E3%81%B9%E3%81%A6%E3%81%AE%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%AF%E3%83%88%E3%83%AA%E3%82%AC%E3%83%BC%E3%83%A2%E3%83%BC%E3%83%89%E3%81%AB"><summary>答え</summary>

すべてのメソッドはトリガーモードになっているコライダーの時に発生する

- `OnTriggerEnter`：トリガーは他のコライダーの中に入った瞬間
- `OnTriggerStay`：トリガーは他のコライダーの中にあるとき
- `OnTriggerExit`：トリガーは他のコライダーの中から出た瞬間

</details>44\. 「`OnCollisionEnter`」の引数（Collision）は何に使えるでしょうか？

<details id="bkmrk-%E7%AD%94%E3%81%88-%C2%A0oncollisionxxxx%E3%81%AE"><summary>答え</summary>

 OnCollisionXXXXの「Collision」には衝突に関する情報が入っている。

たとえば、衝突してきたオブザーバーは「Collision.gameObject」であり、衝突した3D位置は「Collision.point」である。

詳細：[https://docs.unity3d.com/6000.4/Documentation/ScriptReference/Collision.html](https://docs.unity3d.com/6000.4/Documentation/ScriptReference/Collision.html)

</details>45\. 「`SerializeField`」どこで使う？なんのため？

<details id="bkmrk-%E7%AD%94%E3%81%88-%E9%9D%9E%E5%85%AC%E9%96%8B%EF%BC%88private%EF%BC%89%E3%81%AE%E3%83%A1%E3%83%B3%E3%83%90%E3%83%BC"><summary>答え</summary>

非公開（private）のメンバー変数を Unity の Inspector で変えられるようにする。</details>46\. 「event」というキーワードは何に使うのでしょうか？

<details id="bkmrk-%E7%AD%94%E3%81%88-c%23%E3%81%AE%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E5%AE%A3%E8%A8%80%E3%81%99%E3%82%8B%E3%81%A8%E3%81%8D%E3%81%AB%E4%BD%BF%E3%81%86"><summary>答え</summary>

C#のイベントを宣言するときに使う。イベントを作るには、「発生する側」と「受け取る側」の２つが必要。

発生する側：

- 宣言：`public event System.Action<引数1,引数2…> イベント名`
- 発生：`イベント名?.Invoke(数値１, 数値２…);`

受け取る側：

- 登録：`オブジェクト.イベント名 += 受け取るメソッド名`
- 受け取る：`void 受け取るメソッド名（引数1, 引数2, …）`

</details>### スクリプト作成

47\. 「Countdown」というスクリプトをを作成し、ゲームが起動してから3秒後にConsoleで「時間切れ」を表示してください（1回だけ）

<details id="bkmrk-%E7%AD%94%E3%81%88-using-unityengine"><summary>答え</summary>

```c#
using UnityEngine;

public class Countdown : MonoBehaviour
{
    // 残りの時間
    private float timer = 3.0f;
    
    // 時間が切れたか？
    private bool isTimeout = false;
    
    private void Update()
    {
        // 時間が切れたか？ → 何もしない
        if (isTimeout)
            return;
        
        // 時間を減らす
        timer -= Time.deltaTime;
        
        // まだ時間ある？ →　何もしない
        if (timer > 0)
            return;
            
        // 時間切れにする
        isTimeout = true;

        // コンソールで表示
        Debug.Log("時間切れ");
    }
}
```

</details>48\. 「Countdown」を編集し、「Timeout」というイベントを追加してください。時間が切れたら、イベントを発生してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-using-unityengine-1"><summary>答え</summary>

```c#
using UnityEngine;

public class Countdown : MonoBehaviour
{
    // タイムアウトイベントの宣言
    public event System.Action Timeout

    // 残りの時間
    private float timer = 3.0f;
    
    // 時間が切れたか？
    private bool isTimeout = false;
    
    private void Update()
    {
        // 時間が切れたか？ → 何もしない
        if (isTimeout)
            return;
        
        // 時間を減らす
        timer -= Time.deltaTime;
        
        // まだ時間ある？ →　何もしない
        if (timer > 0)
            return;
            
        // 時間切れにする
        isTimeout = true;
        
        // コンソールで表示
        Debug.Log("時間切れ");

        // イベント発生
        Timeout?.Invoke();
    }
}
```

</details>49.「CountdownWait」の新しいスクリプトを作成し、Unity上でCountdownとの紐づけできるようにしてください。そして、CountdownのTimeoutイベントを登録し、イベントを受け取ったら、「イベントを受け取った」を表示してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-using-unityengine-2"><summary>答え</summary>

```c#
using UnityEngine;

public class CountdownWait : MonoBehaviour
{
    // Unityで紐づけ
    [SerializeField]
    private Countdown countdown;

    private void Start()
    {
        countdown.Timeout += OnTimeout;
    }

    private void OnTimeout()
    {
        Debug.Log("イベントを受け取った");
    }
}
```

</details>### スクリプト解析

#### スクリプト A

以下のスクリプトを解析し、下にある質問を回答してください

```c#
public class ObjectPusher : MonoBehaviour
{
    public float speed = 10;

    void OnCollisionEnter(Collision other)
    {
       var rbody = GetComponent<Rigidbody>();
       var direction = other.transform.position - transform.position;
       rbody.linearVelocity = direction.normalized * speed;
    }
}
```

自分（スクリプトを持っているゲームオブジェクト）と相手（衝突してきたゲームオブジェクト）が衝突した場合は9行目で「`linearVelocity`」を変えているが…

50\. どちらのlinearVelocityが変わるでしょうか？（自分 or 相手）

<details id="bkmrk-%E7%AD%94%E3%81%88-%E3%80%8C%E8%87%AA%E5%88%86%E3%80%8D---%E5%8F%96%E5%BE%97%E3%81%97%E3%81%9Frigidb"><summary>答え</summary>

「自分」 - 取得したRigidbodyは、本スクリプトと同じゲームオブジェクトにあるので（7行目）</details>51\. どの方向にlinearVelocityが変わる？（自分の方 or 相手の方）

<details id="bkmrk-%E7%AD%94%E3%81%88-%E3%80%8C%E7%9B%B8%E6%89%8B%E3%80%8D---direction%E3%83%99"><summary>答え</summary>

「相手」 - directionベクトルは相手の方に向いている（方位 = 相手 - 自分）

</details>52\. 全体的に、このスクリプトは何をするのかを説明してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-%EF%BC%92%E3%81%A4%E3%81%AE%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E8%A1%9D%E7%AA%81%E3%81%97%E3%81%9F"><summary>答え</summary>

２つのゲームオブジェクトを衝突したら、自分が相手に向かって速度を設定する。

</details>#### スクリプト B

以下のスクリプトを解析し、下にある質問を回答してください

```c#
public class InstanceMaker : MonoBehaviour
{
    [SerializeField]
    private GameObject original;

    private GameObject [] copies = new GameObject[10];
    private int lastCopy = 0;

    void Update()
    {
        if (Input.GetKeyUp(KeyCode.Space))
        {
            if (lastCopy < 10)
            {
                copies[lastCopy] = Instantiate(original);
                lastCopy++;
            }
            else
            {
                for(int i = 0; i < 10; i++)
                {
                    Destroy(copies[i]);
                }
                lastCopy = 0;
            }
        }
    }
}
```

53\. このスクリプトを実行し、スペースキーを押したらどうなるのか説明してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-%E3%81%82%E3%82%8B%E3%83%97%E3%83%AC%E3%83%8F%E3%83%96%E3%81%8B%E3%82%89%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E3%82%B3%E3%83%94"><summary>答え</summary>

あるプレハブからオブジェクトをコピーし、シーンに追加する。ただし、11個目を作成しょうとしたら、作成したゲームオブジェクトをすべて削除する。

</details>#### スクリプト C

以下のスクリプトを解析し、下にある質問を回答してください

```c#
public class Player : MonoBehaviour
{
    private int HP = 100;   // ヒットポイント
    [SerializeField]
    private int MP = 100;   // マジックポイント

    void Start()
    {
       HP = 50;
    }

    void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag("fire"))
        {
            HP -= 5;
        }
    }

    void Update()
    {
        if (Input.GetKey(KeyCode.Q))
        {
            HP += 10 * Time.deltaTime;
            MP -=  5 * Time.deltaTime;
        }
    }
}
```

54\. ゲームが開始のとき、プレーヤーはHPとMPはそれぞれ何ポイントでしょうか？

<details id="bkmrk-%E7%AD%94%E3%81%88-mp%E3%81%AF100%E7%82%B9%E3%80%81hp%E3%81%AF50%E7%82%B9"><summary>答え</summary>

MPは100点、HPは50点

</details>55\. 11行目〜17行目でどんなことが行われていると思う？

<details id="bkmrk-%E7%AD%94%E3%81%88-%C2%A0%E3%83%88%E3%83%AA%E3%82%AC%E3%83%BC%E3%81%AB%E5%85%A5%E3%81%A3%E3%81%9F%E3%81%A8%E3%81%8D%E3%80%81%E5%85%A5%E3%81%A3%E3%81%A6%E3%81%8D%E3%81%9F"><summary>答え</summary>

 トリガーに入ったとき、入ってきたゲームオブジェクトのタグは「fire」だったら、HPが５点減る</details>56\. HPの回復処理を説明してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-%E3%80%8Cq%E3%80%8D%E3%82%AD%E3%83%BC%E3%82%92%E3%81%8A%E3%81%A3%E3%81%97%E3%81%B1%E3%81%AA%E3%81%97%E3%81%A7%E3%80%81%EF%BC%91%E7%A7%92%E5%BD%93"><summary>答え</summary>

「Q」キーをおっしぱなしで、１秒当たりにMPの5点を消費し、HPの10点を回復する

</details>57\. "Q”キーを５秒押すと、MPはどうなる？

<details id="bkmrk-%E7%AD%94%E3%81%88-mp%E3%81%AF25%E7%82%B9%E3%81%8C%E6%B8%9B%E3%82%8B"><summary>答え</summary>

MPは25点が減る

</details>58\. Inspectorで編集できる変数を指定してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-mp%E3%81%AFinspector%E3%81%A7%E7%B7%A8%E9%9B%86%E3%81%8C%E3%81%A7"><summary>答え</summary>

MPはInspectorで編集ができる

</details>59\. HPとMPは「ゼロ」以下にならないように、スクリプトの適切なところに編集してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-void-ontriggerent"><summary>答え</summary>

```c#
void OnTriggerEnter2D(Collider2D other)
{
    if (other.CompareTag("fire"))
    {
        HP -= 5;
        
        // 0以下にならないように
        HP = Mathf.Max(HP, 0); 
    }
}

void Update()
{
    if (Input.GetKey(KeyCode.Q))
    {
        HP += 10 * Time.deltaTime;
        MP -=  5 * Time.deltaTime;
        
        // 0以下にならないように
        MP = Mathf.Max(MP, 0); 
    }
}
```

</details>60\. “Q”を固定せず、Inspectorでキーを設定できるようにしてください。

<details id="bkmrk-%E7%AD%94%E3%81%88-%E3%81%BE%E3%81%9A%E3%80%81%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%AE%E9%A0%AD%E3%81%AB%EF%BC%9A-%2F%2F-%E5%9B%9E"><summary>答え</summary>

まず、スクリプトの頭に：

```c#
// 回復のキーをUnityで設定
[SerializeField]
private KeyCode restoreKey;
```

そして、Updateでif文の条件を変える：

```c#
if (Input.GetKey(restoreKey))
```

</details>61\. HPが「ゼロ」になったら、ゲームオブジェクトを削除してください。

<details id="bkmrk-%E7%AD%94%E3%81%88-void-ontriggerent-1"><summary>答え</summary>

```c#
void OnTriggerEnter2D(Collider2D other)
{
    if (other.CompareTag("fire"))
    {
        HP -= 5;
        if (HP <= 0)
            Destroy(gameObject);
    }
}
```

</details>