Skip to content

第5回 弾を撃つ ​

この回でつくるもの:Z キーかスペースキーを押すと、自機の機首から光る弾がまっすぐ右へ飛んでいくようにします。押しっぱなしで連射もできます。

この回で学ぶこと ​

  • 光るマテリアル(Emission)を作る
  • プレハブ(Prefab)を作って、スクリプトからたくさん出す(Instantiate)
  • 決まった時間がたったらオブジェクトを消す(Destroy)
  • Input Actions に新しいキー(バインド)を追加する
  • if 文と「タイマー」で連射の間隔を決める

前回までのふりかえり ​

前回は、自機が画面の外へはみ出さないようにして、上下に動くと機体が傾くようにしました。 これで PlayerController は完成です。Player の中身は Player > Bank > Model と Player > Muzzle になっています。 今回は Muzzle(機首の先の目印)から弾を出します。

1. 弾を作る ​

1-1. 球(Sphere)を置く ​

  1. Hierarchy ウィンドウの何もないところを右クリックします。
  2. 3D Object > Sphere を選びます。
  3. できた Sphere を選んだまま、名前を PlayerBullet に変えます(F2 キーか、ゆっくり2回クリック)。
  4. Inspector の Transform で Position を (0, 0, 0) にします。
  5. Rotation が (0, 0, 0) になっていることを確かめます。
  6. Scale を (0.6, 0.15, 0.15) にします。横に細長い弾になります。

1-2. 当たり判定をいったん外す ​

Sphere には最初から Sphere Collider(当たり判定の形)が付いています。当たり判定は第8回でまとめて設定するので、今は外しておきます。

  1. Hierarchy で PlayerBullet を選びます。
  2. Inspector の Sphere Collider の右はしにある ⋮(縦の点3つ)をクリックします。
  3. Remove Component を選びます。

先生へ

第8回で Rigidbody と一緒に Sphere Collider(Is Trigger ON)を付け直します。今のうちに外しておくと、第8回の手順が「付ける」だけで済み、説明がそろいます。

1-3. 光るマテリアルを作る ​

マテリアルは「物の表面の色や質感」を決めるアセットです。弾を光らせてみましょう。

  1. Project ウィンドウで Assets/_Game/Materials フォルダを開きます。
  2. 何もないところを右クリックして、Create > Material を選びます。
  3. 名前を Mat_PlayerBullet にします。
  4. Mat_PlayerBullet を選び、Inspector の Shader が Universal Render Pipeline/Lit になっていることを確かめます。
  5. Surface Inputs の Base Map の右にある色の四角をクリックし、黄色を選びます。
  6. 少し下にある Emission(発光)のチェックボックスをオンにします。
  7. Emission Map の右にある色の四角をクリックします。
  8. 出てきたカラーピッカー(色を選ぶ窓)で黄色を選び、下の Intensity(明るさ)を 2 くらいにします。
  9. カラーピッカーを閉じます。
  10. Project の Mat_PlayerBullet を、Scene ビューの PlayerBullet にドラッグ&ドロップします。

ヒント

Emission は「自分で光っているように見せる」設定です。今はただ明るい色に見えるだけですが、第19回で Bloom(光がにじむ効果)を入れると、ぼわっと光って見えるようになります。

ここで Ctrl+S(Macは Cmd+S)を押してシーンを保存しましょう。

2. Bullet スクリプト ​

2-1. スクリプトを作る ​

  1. Project ウィンドウで Assets/_Game/Scripts フォルダを開きます。
  2. 何もないところを右クリックして、Create > Scripting > MonoBehaviour Script を選びます。
  3. 名前を Bullet にして Enter を押します(ファイル名とクラス名は同じにします)。
  4. Bullet をダブルクリックしてエディタで開きます。

2-2. コードを書く ​

中身をすべて消して、次のコードを書きます。

csharp
using UnityEngine;

// 自機の弾
public class Bullet : MonoBehaviour
{
    public float speed = 15f;      // 速さ
    public float lifeTime = 2f;    // 何秒で消えるか

    void Start()
    {
        Destroy(gameObject, lifeTime);
    }

    void Update()
    {
        // 自分の向いている「右」へ進む(回転させると斜めに飛ぶ)
        transform.Translate(Vector3.right * speed * Time.deltaTime);
    }
}

書けたら Ctrl+S(Macは Cmd+S)でファイルを保存して、Unity にもどります。

2-3. コードの解説 ​

行意味
public float speed = 15f;弾の速さ。1秒に15進みます
public float lifeTime = 2f;弾が生まれてから消えるまでの秒数
Destroy(gameObject, lifeTime);「自分自身(gameObject)を、lifeTime 秒後に消して」というお願い
transform.Translate(...)今いる場所から、指定した分だけ動かす
Vector3.right右向き (1, 0, 0) の矢印
  • Destroy は、オブジェクトを消す命令です。2つ目に数字を書くと「その秒数がたってから消す」になります。
  • 弾を消さないと、画面の外へ飛んでいった弾がいつまでも残り、ゲームがだんだん重くなります。だから2秒で消しています。
  • Translate は、何も書かないと 自分の向き を基準に動きます。今は回転が (0, 0, 0) なので右へ飛びます。後の回で弾をななめに回転させて撃つときに、この性質が役に立ちます。

2-4. 弾に付けて試す ​

  1. Hierarchy で PlayerBullet を選びます。
  2. Inspector の一番下の Add Component をクリックします。
  3. 検索欄に Bullet と入力し、出てきた Bullet をクリックします。
  4. ▶ Play ボタンを押します。

弾が右へ飛んでいき、2秒ほどで Hierarchy から消えればOKです。 確認できたら、もう一度 ▶ を押して Play を止めます。

3. プレハブにする ​

3-1. プレハブとは ​

弾は、撃つたびに新しく作ります。そのために「弾の設計図」を作っておきます。これを プレハブ(Prefab) といいます。 プレハブがあれば、スクリプトから何個でも同じ弾を作れます。

3-2. プレハブを作る ​

  1. Project ウィンドウで Assets/_Game/Prefabs フォルダを開きます。
  2. Hierarchy の PlayerBullet を、Project ウィンドウの Prefabs フォルダの中へドラッグ&ドロップします。
  3. Project に青い箱のアイコンの PlayerBullet ができたことを確かめます。
  4. Hierarchy の PlayerBullet の文字も青くなっていることを確かめます。

3-3. シーンの弾を消す ​

設計図ができたので、シーンに置いてある弾は消してしまいます。

  1. Hierarchy の PlayerBullet を選びます。
  2. Delete キー(Macは Cmd+Delete)を押して消します。
  3. Project の Prefabs フォルダに PlayerBullet が残っていることを確かめます。
  4. Ctrl+S(Macは Cmd+S)でシーンを保存します。

よくあるまちがい

Project の方の PlayerBullet を消してしまうと、設計図がなくなってしまいます。消すのは Hierarchy の方 だけです。

4. Attack に Z と Space を追加する ​

4-1. Attack アクションを見る ​

テンプレートに入っている InputSystem_Actions には、最初から Attack(攻撃)というアクションがあります。でも、キーボードではマウスの左クリックや Enter キーくらいしか登録されていません。 シューティングでよく使う Z キーと Space キーを追加しましょう。

  1. Project ウィンドウで Assets フォルダを開きます。
  2. InputSystem_Actions をダブルクリックします。
  3. Input Actions の編集ウィンドウが開きます。
  4. 左の Action Maps で Player を選びます。
  5. まん中の Actions の中に Attack があることを確かめます。

4-2. Z キーを追加する ​

  1. Actions の Attack の右にある + ボタンをクリックします(マウスを乗せると出てきます)。
  2. 出てきたメニューから Add Binding を選びます。
  3. Attack の下に <No Binding> という行が増え、選ばれた状態になります。
  4. 右側の Binding Properties の Path の欄をクリックします。
  5. 出てきた小さな窓の左上にある Listen ボタンをクリックします。
  6. キーボードの Z キーを押します。
  7. 候補に出てきた Z [Keyboard] をクリックします。
  8. Path が Z [Keyboard] になったことを確かめます。
  9. その下の Use in control scheme で Keyboard&Mouse にチェックを入れます。

4-3. Space キーを追加する ​

  1. もう一度 Attack の右の + をクリックし、Add Binding を選びます。
  2. 新しい <No Binding> を選んだまま、Path の欄をクリックします。
  3. Listen をクリックして、Space キーを押します。
  4. Space [Keyboard] をクリックします。
  5. Use in control scheme で Keyboard&Mouse にチェックを入れます。

4-4. 保存する ​

  1. ウィンドウの上の方にある Save Asset ボタンをクリックします。
  2. Auto-Save にチェックが入っている場合は、自動で保存されるのでボタンを押さなくても大丈夫です。
  3. 編集ウィンドウを閉じます。

注意

保存しないで閉じると、追加したキーが消えてしまいます。閉じるときに「保存しますか?」と聞かれたら Save を選びましょう。

ヒント

Space キーは、最初から Jump(ジャンプ)アクションにも登録されています。このゲームでは Jump を使わないので、気にしなくて大丈夫です。

5. PlayerShooter スクリプト ​

5-1. スクリプトを作る ​

  1. Project ウィンドウで Assets/_Game/Scripts フォルダを開きます。
  2. 右クリックして Create > Scripting > MonoBehaviour Script を選びます。
  3. 名前を PlayerShooter にして Enter を押します。
  4. ダブルクリックしてエディタで開きます。

5-2. コードを書く ​

中身をすべて消して、次のコードを書きます。

csharp
using UnityEngine;
using UnityEngine.InputSystem;

// 弾を撃つスクリプト
public class PlayerShooter : MonoBehaviour
{
    public GameObject bulletPrefab;     // ふつうの弾
    public Transform muzzle;            // 弾が出る場所
    public float fireInterval = 0.15f;  // 連射の間隔(秒)

    float timer;
    InputAction attackAction;

    void Start()
    {
        attackAction = InputSystem.actions.FindAction("Attack");
    }

    void Update()
    {
        timer -= Time.deltaTime;

        if (attackAction.IsPressed() && timer <= 0f)
        {
            Shoot();
            timer = fireInterval;
        }
    }

    void Shoot()
    {
        Instantiate(bulletPrefab, muzzle.position, Quaternion.identity);
    }
}

書けたら保存して Unity にもどります。Console ウィンドウに赤いエラーが出ていないか確かめましょう。

5-3. コードの解説 ​

行意味
public GameObject bulletPrefab;撃つ弾のプレハブを入れる箱
public Transform muzzle;弾が出る場所(Muzzle)を入れる箱
public float fireInterval = 0.15f;次の弾が撃てるまでの時間(秒)
float timer;「あと何秒で撃てるか」を数えるタイマー
InputSystem.actions.FindAction("Attack")第3回の Move と同じように、Attack アクションを探してくる
attackAction.IsPressed()Attack のキーが 押されている間 ずっと true(はい)になる
Shoot();下に書いた Shoot の中身を実行する
Instantiate(...)プレハブから新しいオブジェクトを作る

if 文のおさらい

csharp
if (attackAction.IsPressed() && timer <= 0f)
{
    // ここは条件が「はい」のときだけ実行される
}
  • if (条件) は「もし〜なら」という意味です。条件が正しいときだけ { } の中が実行されます。
  • && は「かつ」という意味です。「キーが押されている」かつ「タイマーが 0 以下」のときだけ撃ちます。
  • <= は「以下」です。

タイマーのしくみ

  1. timer -= Time.deltaTime; で、毎フレーム、たった時間だけタイマーを減らします。
  2. 撃ったら timer = fireInterval; で、タイマーを 0.15 にもどします。
  3. 0.15 秒たつとタイマーが 0 以下になり、また撃てるようになります。

こうすると、押しっぱなしでも 1秒に約6〜7発の「ほどよい連射」になります。タイマーがないと、毎フレーム(1秒に60回以上)弾が出てしまいます。

関数(メソッド)を自分で作る

csharp
void Shoot()
{
    Instantiate(bulletPrefab, muzzle.position, Quaternion.identity);
}
  • Start や Update と同じように、自分で名前を付けた「命令のまとまり」を作れます。これを 関数(メソッド)といいます。
  • Shoot(); と書くと、Shoot の中身が実行されます。
  • 「撃つ」処理を1か所にまとめておくと、後の回で音を鳴らしたり、弾の種類を増やしたりするときに、ここだけを直せば済みます。

Instantiate の3つの材料

材料ここでは意味
1つ目bulletPrefab何を作るか(プレハブ)
2つ目muzzle.positionどこに作るか(Muzzle の位置)
3つ目Quaternion.identityどの向きで作るか(「回転なし」という意味)

6. Inspector で Bullet Prefab と Muzzle をつなぐ ​

6-1. PlayerShooter を Player に付ける ​

  1. Hierarchy で Player(いちばん上の親)を選びます。
  2. Inspector の一番下の Add Component をクリックします。
  3. PlayerShooter と入力して、Player Shooter をクリックします。

6-2. プレハブと Muzzle を入れる ​

  1. Player を選んだまま、Inspector の Player Shooter を見ます。
  2. Project ウィンドウの Prefabs フォルダにある PlayerBullet を、Bullet Prefab の欄へドラッグ&ドロップします。
  3. Hierarchy の Player の左の ▶ を開き、子の Muzzle を表示します。
  4. Hierarchy の Muzzle を、Inspector の Muzzle の欄へドラッグ&ドロップします。
  5. Fire Interval が 0.15 になっていることを確かめます。
  6. Ctrl+S(Macは Cmd+S)でシーンを保存します。

注意

Muzzle をドラッグするときに、Hierarchy で Muzzle をクリックしてしまうと Inspector の表示が Muzzle に切りかわってしまいます。Player を選んだまま、Muzzle を 押したまま 引っぱってきましょう。

6-3. 動作確認 ​

▶ Play ボタンを押して、次のようになればOKです。

  • Z キーか Space キーを押すと、機首の先から弾が右へ飛ぶ
  • 押しっぱなしにすると、一定のリズムで連射される
  • 自機を動かしながら撃っても、弾は自機の今いる場所から出る
  • 飛んでいった弾は、2秒ほどで Hierarchy から消える(PlayerBullet(Clone) が増えたり減ったりする)

ヒント

Play 中に Inspector で Fire Interval や、プレハブの Speed を変えると、すぐに結果を試せます。ただし Play を止めると、シーン上の値は元にもどります。気に入った値はメモしておき、Play を止めてから入れ直しましょう。

チャレンジ課題 ​

  • Fire Interval を 0.05 や 0.5 に変えて、連射の気持ちよさがどう変わるか試してみよう
  • PlayerBullet プレハブの Speed や Lifetime を変えてみよう(プレハブは Project でダブルクリックすると編集できます)
  • マテリアルの色を水色や赤に変えて、自分好みの弾にしてみよう
  • Muzzle の位置を動かして、機首の先ぴったりから弾が出るように調整してみよう

つまずきポイント ​

こんなときはここを確認
キーを押しても弾が出ないInput Actions で Z / Space を追加したあと Save Asset したか。Player に PlayerShooter が付いているか
Console に UnassignedReferenceException と出るPlayerShooter の Bullet Prefab と Muzzle の欄が空になっていないか
弾が自機の真ん中や変な場所から出るMuzzle の位置。Muzzle の Position の Z が 0 になっているか
弾が飛ばずにその場に止まるPlayerBullet プレハブに Bullet が付いているか。Speed が 0 になっていないか
弾がたて長や斜めになるPlayerBullet プレハブの Rotation が (0, 0, 0)、Scale が (0.6, 0.15, 0.15) か
弾が消えずにどんどん増えるBullet の Start に Destroy(gameObject, lifeTime); を書いたか

まとめ ​

  • 何度も作るものは プレハブ にして、Instantiate でスクリプトから作る
  • いらなくなったものは Destroy で消す。秒数を付けると時間がたってから消える
  • if と && で「キーが押されていて、しかも時間がたっていたら」という条件が書ける
  • タイマーを使うと、連射の間隔を決められる