# ボールをどんどん生成

作ったプレハブを利用し、実行中にたくさんのボールを生成しましょう。

## ボール生成する：`BallShooter`

今度のスクリプトは：

- 作成したプレハブを Inspector で指定できるようにする。
- マウスの左ボタンを押したら、プレハブから実際に使うオブジェクトを生成。
- 最大数を制限する。

スクリプトの変数

Scripts フォルダーの中に新しい「BallShooter」を追加し、Inspector の変数を準備しましょう：

```c#
using UnityEngine;

// ボールを生成するスクリプト
public class BallShooter : MonoBehaviour
{
    // Unityでボールのプレハブを指定
    [SerializeField]
    private GameObject ballPrefab;
}
```

当然、プレハブ変数の「型」は「`GameObject`」になる。

### マウス処理＆ボール生成：`Update()`

フレーム毎に、マウスボタンのクリックがあったかどうかを確認し、クリックの場合は、プレハブから実物を生成し、実行中にシーンに追加しましょう：

```c#
void Update()
{
    // もしも左ボタンをクリックしたら…
    if (Input.GetMouseButtonDown(0))
    {
        // 「Instantiate」を使い、プレハブから実物（インスタンス）を作成
        // newBallは新しく作ったボールの参照である
        var newBall = Instantiate(ballPrefab); 
    }
}
```

ボールを生成するのは、プレーヤーなので、「player」オブジェクト（パドル）にアタッチして、Ball Prefabの中に、前ステップで作ったボールのプレハブを設定してください。

[![image.png](https://class.illogic.games/uploads/images/gallery/2026-04/scaled-1680-/83eimage.png)](https://class.illogic.games/uploads/images/gallery/2026-04/83eimage.png)

これを追加することにより、プレーヤーは移動だけではなく、ボール生成もできるようになった！

<details id="bkmrk-%EF%BC%91%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E2%87%92%EF%BC%91%E8%B2%AC%E4%BB%BB-%EF%BC%91%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E2%87%92%EF%BC%91%E8%B2%AC"><summary>１スクリプト⇒１責任</summary>

#### １スクリプト⇒１責任

スクリプトが長ければ、長いほど、バグの修正が辛くなり、ゲームの拡張性が狭まる。基礎ルールとして、「１つのスクリプトは１つの責任」の考えすると、大きな課題を解決しやすい細かい課題に分割し、それぞれが別々のスクリプトで実装する。

これで、機能を増やしたり、減らしたりするのは簡単（スクリプトを追加と削除だけ）。また、スクリプトが短くなるので、バグが発生した場合、すぐ解決できることもメリットである。

##### 考えられる例

<table border="1" style="border-collapse: collapse; width: 100.041%; border-width: 0px;"><colgroup><col style="width: 49.9383%;"></col><col style="width: 49.9383%;"></col></colgroup><tbody><tr><td style="border-width: 0px;">- 武器を打つ：ShootWeapon 
    - CPUプレーヤー、人間プレーヤーが関係なく、打つことが可能
- 体力を表示：HPGauge 
    - なんでものHPを表示（プレーヤー、敵、壊せるものなど）
- プレーヤーを追いかける：FollowPlayer 
    - 敵に付けると、プレーヤーを追いかける
    - 仲間に付けると、プレーヤーを追いかける

</td><td style="border-width: 0px;">- アイテム：Item 
    - アイテムを表す
- アイテムを拾う：ItemCollect 
    - アイテム（Item）を拾ったら、インベントリに追加
- アイテムを管理：Inventory 
    - アイテムどこから来たのか関係なく、アイテムを管理
- アイテムを購入：ItemBuy 
    - アイテム（Item）をショップで購入し、インベントリに追加

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

</details>この状態で実行して確認してみてください。

## 問題発見

いっぱいのボールが現れて楽しいが、いくつかの問題の確認ができた

1. ボールがたまに遅くなる？
2. プレーヤーの位置が関係なく、ボールがいつも同じ場所から現れる
3. ボールが多すぎる。

### 1. ボールが遅くなる？

超弾みしたのに、なんでボールが遅くなる？これは正しい物理の計算の影響であるため。ビリヤードのように、ある物体は別の物体を移動すると、速度を伝達する（<span class="HwtZe" lang="ja"><span class="jCAhz ChMk0b"><span class="ryNqvb">運動量の伝達</span></span></span>という）

<table border="1" id="bkmrk-%E5%9B%BA%E5%AE%9A%E7%89%A9%E4%BD%93%EF%BC%88%E5%A3%81%EF%BC%89%E3%81%A8%E3%81%B6%E3%81%A4%E3%81%8B%E3%82%8B%E3%81%A8%E3%80%81%E5%BC%B7%E3%81%8F%E8%B7%B3%E3%81%AD%E8%BF%94%E3%81%99" style="border-collapse: collapse; width: 100%; border-width: 0px;"><colgroup><col style="width: 43.6065%;"></col><col style="width: 56.471%;"></col></colgroup><tbody><tr><td class="align-center" style="border-width: 0px;">![image.png](https://class.illogic.games/uploads/images/gallery/2026-04/scaled-1680-/INrimage.png)

</td><td class="align-center" style="border-width: 0px;">![image.png](https://class.illogic.games/uploads/images/gallery/2026-04/scaled-1680-/eVYimage.png)

</td></tr><tr><td class="align-center" style="border-width: 0px;">固定物体（壁）とぶつかると、強く跳ね返すが…

</td><td class="align-center" style="border-width: 0px;">…移動可能な物体とぶつけると、速度が移る！

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

これを直すには、実行中に速度を調整し、指定した「speed」を守るようにする。「`BallMove`」スクリプトを調整し、フレーム毎は以下の処理にしましょう：

```c#
// これは「BallMove」スクリプトの更新処理
void Update()
{
    // 現在の速度ベクトルを取得。向きは保護したいが、
    // 長さ（速度）は必ず「speed」であることを保証
    var velocity = _rigidbody.linearVelocity;
    
    // まず、速度ベクトルの長さは「１」にする（単位ベクトル）
    velocity.Normalize();
    
    // そして、長さは「speed」にしよう
    velocity *= speed;
    
    // 速度を上書きする
    _rigidbody.linearVelocity = velocity;
}
```

### 2. ボールがいつも同じ場所から現れる

ブールがプレーヤーのいる場所から発射すれば自然な遊び方になるので、`BallShooter` のスクリプトの直そう。インスタンスを作った後に、適切な場合に配置：

```c#
// BallShooterのUpdateを更新：
void Update()
{
    // もしも左ボタンをクリックしたら…
    if (Input.GetMouseButtonDown(0))
    {
        // 「Instantiate」を使い、プレハブから実物（インスタンス）を作成
        // newBallは新しく作ったボールの参照である
        var newBall = Instantiate(ballPrefab); 
        
        // このスクリプトはパドルにアタッチされているので、
        // そのパドルの位置を取得
        var position = transform.position;
        
        // もう少し上に移動
        position.y += 0.5f;
        
        // ボールの位置を設定
        newBall.transform.position = position;
    }
}
```


### 3. ボールが多すぎる

このゲームでは、ボールは１個までの制限があるので、今の作りだとボールが多すぎる。これを修正するには、現在生きているボールの数を追跡すれば良いでしょう。

`BallShooter `を編集し、実行中に生成したボールの数を数えましょう。

```c#
using UnityEngine;

// ボールを生成するスクリプト
public class BallShooter : MonoBehaviour
{
    // Unityでボールのプレハブを指定
    [SerializeField]
    GameObject ballPrefab;

    // 現在生きているボールの数
    private int _ballCounter;
    
    void Update()
    {
        // もしも左ボタンをクリックしたら…
        // 「かつ」
        // ボールの数は１未満だったら
        if (Input.GetMouseButtonDown(0) && _ballCounter < 1)
        {
            // 「Instantiate」を使い、プレハブから実物（インスタンス）を作成
            // newBallは新しく作ったボールの参照である
            var newBall = Instantiate(ballPrefab); 
            
            // このスクリプトはパドルにアタッチされているので、
            // そのパドルの位置を取得
            var position = transform.position;
            
            // もう少し上に移動
            position.y += 0.5f;
            
            // ボールの位置を設定
            newBall.transform.position = position;

            // ボールを数える
            _ballCounter++;
        }
    }
}

```

### 4. ボールがなくなっても、次のボールを生成できない？

新しい問題が現れた！確か、ボール１個までの制限ができたが、ボールがなくなっても、次のボールを作れない！実は、ボールがなくなっていない！

実行中にシーンビューを確認しましょう：

[![image.png](https://class.illogic.games/uploads/images/gallery/2026-04/scaled-1680-/fprimage.png)](https://class.illogic.games/uploads/images/gallery/2026-04/fprimage.png)

カメラで見えないだけで、ヒエラルキーにも、画面にもボールがあることが確認できる。これを解決するには「トリガー」を使いましょう。画面の真下に透明なトリガーを作成し、ボールが通過したら、ボールを削除し、ボールが失ったことをパドルにお知らせしましょう。

まず、シーンをの設定。backgroundの中に、空のゲームオブジェクトを作成し、BoxCollider2Dを追加してください。サイズが、画面の下のすべてをカバーするようにしてください。

「Is Trigger」のチェックを忘れずに！

![image.png](https://class.illogic.games/uploads/images/gallery/2026-04/scaled-1680-/jftimage.png)

次「ボールが通過したら、ボール削除し、プレーヤーに通知する」処理を追加しましょう。新しい「BallDestroy」スクリプトを作成してください。

```c#
using UnityEngine;

// ボールを削除し、お知らせする
public class BallDestroy : MonoBehaviour
{
    // BallShooterに知らせる！
    [SerializeField] 
    private BallShooter ballShooter; 
    
    // トリガーの中に何か入ったら：
    private void OnTriggerEnter2D(Collider2D other)
    {
        // ゲームオブジェクトを廃止（削除）する
        Destroy(other.gameObject);

        // メソッドを呼び出し、知らせる
        ballShooter.BallDestroyed();
    }
}

```

これで実現できるが、`BallShooter` 側でお知らせを受け取るメソッド「`BallDestroyed`」をまだ作っていないので、追加しましょう：

```c#
using UnityEngine;

// ボールを生成するスクリプト
public class BallShooter : MonoBehaviour
{
    // 省略（変わりがない）

    // ボール廃止されたときに呼び出される
    // 他のスクリプトから呼び出されるので、「public」アクセスにしましょう
    public void BallDestroyed()
    {
        // 最低はゼロであることを確かめる
        if (_ballCounter > 0)
        {
            // 数を減らす
            _ballCounter--;
        }
    }
    
    void Update()
    {
        // 省略（変わりがない）
    }
}
```

最後、シーンのトリガーに「BallDestroy」スクリプトを追加し、Inspector での必要な連携を設定してください：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-04/scaled-1680-/yhrimage.png)

これで実行して、確認してみましょう！