Unity ゲーム開発 生成AI

【ゲーム開発】AI活用で進める独学個人ゲーム開発 #2_自機の操作

はじめに

前回、いろいろ書きましたが今回からゲーム開発を実際に行っていきます。
現在のゲーム画面はこんな感じです。

本記事は下記のものを実装した記録です。
・自機の移動

やったことは以下の通りです。

# 実装内容 主なスクリプト断片・設定 目的/気付き
1 Player プレハブを作成
SpriteRenderer と BoxCollider2D を追加
- まずは見た目と衝突枠だけ用意
2 Transform.Translate で移動 float inputY = Input.GetAxis("Vertical");
transform.Translate(0, inputY * speed * Time.deltaTime, 0);
動くことは確認できたが、物理計算と分離されていて弾や敵との衝突挙動が不自然
3 Rigidbody2D を追加
Body Type を Dynamic に設定
Rigidbody2D
Gravity Scale = 0
物理ベースに乗せ直し、衝突処理を統一
4 FixedUpdate へ移植 void FixedUpdate() {
  float inputY = Input.GetAxis("Vertical");
  rb.velocity = new Vector2(rb.velocity.x, inputY * moveSpeed);
}
物理フレームで速度を与えることで
フレーム落ち時も挙動が安定
5 画面外へのはみ出し防止
カメラ境界を計算して Clamp
var pos = transform.position;
pos.y = Mathf.Clamp(pos.y, minY, maxY);
transform.position = pos;
UI や敵が見切れないように調整

自機の移動

Player プレハブを作成

Unityで2Dシューティングゲームを作りたいです。まずは自機を作るところから始めたいのですが、どうすればいいですか?

それは楽しそうですね!
まず、自機(Player)を作るための最初のステップをご案内します。

🛠 Step 1:Player プレハブを作成する
Hierarchy ウィンドウで右クリック → Create Empty
→ 名前を Player に変更します。

Player オブジェクトに以下のコンポーネントを追加しましょう:

コンポーネント 用途
SpriteRenderer 自機の見た目(画像)を表示
BoxCollider2D 敵や弾との当たり判定に使う枠

🎨 SpriteRenderer には、仮でも良いので画像を設定しておきましょう。
まだ用意がなければ、Unity に標準で入っている「Sprites > Square」などで代用できます。

🎁 例:ここまでできた Player の構成

オブジェクト名 コンポーネント
Player SpriteRenderer
BoxCollider2D

✅ これで画面上に「表示され、当たり判定も持つ」自機オブジェクトが完成します。

🚀 次は 移動処理弾の発射 処理を追加していきましょうか?

どこでなにをすればいいか解説してあります。
しかし、プレハブというものが当たり前みたいな顔をして出てきました。
気軽に聞いてみます

プレハブはどうやって作るんですか?

いい質問です!
プレハブ(Prefab)とは、「再利用できるオブジェクトのテンプレート」のことです。
ゲーム中に何度も使いたいオブジェクト(自機、弾、敵など)を一度作って、保存しておくのに使います。

🛠 プレハブの作り方(自機を例に)

Step 1:Hierarchyに Player を作る

  • Unity の Hierarchy ウィンドウで右クリック
  • 2D Object > Sprite を選択
  • 名前を Player に変更
  • 見た目の画像(スプライト)を設定
  • 必要に応じて以下のコンポーネントを追加:
    • BoxCollider2D(当たり判定)
    • Rigidbody2D(物理挙動や移動用)

Step 2:Project にドラッグしてプレハブ化

  • Project ウィンドウで Prefabs というフォルダを作成(任意)
  • Hierarchy の Player オブジェクトを Project にドラッグ
  • 青いキューブのアイコン「Player.prefab」が生成されればOK!

Step 3:プレハブの使い方

  • シーンに何度でもドラッグして配置できる
  • Instantiate(playerPrefab) で動的に生成できる
  • Prefab本体を修正すれば、すべてのインスタンスに一括反映

🎉 これで自機のプレハブが完成です!
このあとは「移動スクリプト」や「弾の発射スクリプト」を追加して、プレハブに組み込んでいきましょうか?

プレハブというものをこれで作ることができました。

Transform.Translate で移動

とりあえずで画面に配置してみた自機の移動を実装したいところです。
しかし、実装方法が想像もできないのでまずはChatGPTに質問してみます。

自機の移動方法を教えて下さい

はい、Unityで自機を上下に動かす基本的なスクリプト例を紹介します。

以下のような SimplePlayerMovement.cs を作成して、自機オブジェクトにアタッチしてください。

using UnityEngine;

public class SimplePlayerMovement : MonoBehaviour
{
    public float speed = 5f;

    void Update()
    {
        float moveY = Input.GetAxis("Vertical");  // ↑↓キー/WとSキーに対応
        transform.Translate(0, moveY * speed * Time.deltaTime, 0);
    }
}

🔧 ポイント
Input.GetAxis("Vertical") は、W/Sキーや↑/↓キーの入力を取得します。
transform.Translate により、自機を上下方向に移動させます。

⚠️ この方法は「物理演算(Rigidbody)」を使っていないため、衝突や重力との連携が必要な場合は Rigidbody2D を使う方法に切り替えるとよいです。

Unityの教本を読んでいたおかげでギリ分かります。
上下方向の入力をmoveYに代入してY軸の移動に利用しているっぽいですね。

しかし分からないものもあります。Time.deltaTimeとはなんでしょうか?

Time.deltaTimeの解説をお願いします

はい、わかりました!Time.deltaTime は Unity でゲームを安定して動かすための大事な概念です。

🕒 Time.deltaTime とは?
「前のフレームから今のフレームまでにかかった時間(秒数)」 を表します。

例えば:
・60FPS(1秒間に60フレーム)のとき → Time.deltaTime ≒ 0.0167秒
・30FPS のとき → Time.deltaTime ≒ 0.0333秒

🧠 なぜ必要なの?
PCによってフレームレート(処理の速さ)は異なります。
Time.deltaTime を使うと、どんな環境でも同じ速度で動くようにできます。

使わない場合(不安定な例)

transform.Translate(0, 1, 0);

これは毎フレーム「1」だけ動く処理です。
・60FPS → 1秒間に60回動いて「60」進む
・30FPS → 30回しか動かず「30」しか進まない
→ 高速なPCほど速く進んでしまい、バランスが崩れます。

Time.deltaTime を使うと…

transform.Translate(0, 1 * Time.deltaTime, 0);

60FPS:1回あたり ≒ 0.0167 → 合計で ≒ 1.0
30FPS:1回あたり ≒ 0.0333 → 合計で ≒ 1.0
どんなFPSでも約1秒で「1」進むようにできます!

🎯 まとめ
Time.deltaTime を使うことで、
・PCやスマホの性能差に関係なく
プレイヤーやオブジェクトの移動速度が一定になります。

→ Unityゲームでは「移動・アニメ・タイマー処理」などに欠かせない存在です!

FPSに応じてフレームの秒数を返してくれるものといった感じでしょうか。

transform.Translate(0, moveY * speed * Time.deltaTime, 0); というコードは
"上下のキーを入力すると一定の速度で動くようにしたもの"って感じですかね?
文章にすると楽ですが、素人がコードで1から書けと言われると時間がかかりそうです。

C#メモ
コード 意味
Time.deltaTime FPSに応じてフレームの秒数を返してくれる

こんな感じでC#のメモも残していきます。

ここで上下キーを押して確認してみます。

思ってたんとちがう。

画面上でも垂直方向に動いてほしかったのですが、水平に動いてしまいました。

これは自機のRotation(角度)を-90で設定しているせいな気がします。聞いてみましょう。

自機のプレハブをRotation-90で設定しているのですが、この状態でY方向の移動をさせると画面上では横方向に動きますか?

🔍 なぜそうなる?
これは、自機のローカルY軸が画面右を向いている(=Rotation -90°)ためです。
Unity の基本ルールとして、transform.Translate() はデフォルトで ローカル軸(Space.Self) に基づいて動きます。

つまり、スプライトが回転していると、移動の方向もその回転の影響を受けてしまいます。

あなたの設定

項目 解釈
自機の向き Rotation.z = -90° ローカルY軸が画面右方向を向いている
移動処理 transform.Translate(0, inputY * speed * Time.deltaTime, 0); ローカルY方向=画面右に動く

対処法(見た目は回転したまま、移動は画面の上下方向)

transform.Translate(0, inputY * speed * Time.deltaTime, 0, Space.World);
  • Space.World を指定すると、「ワールド座標系(画面基準)」で移動できます
  • これにより、自機が回転していても 画面の上下 方向へまっすぐ移動します

💡 補足
Unityの Transform 系関数(Translate / Rotate / LookAt など)は、Space.Self(ローカル)と Space.World(ワールド)を切り替えることで、移動や回転の基準を変更できます。

✅ この知識を活かせば、「回転したスプライトでも見た目を崩さず自然な操作感」を保てますよ!

合っていたようですね。空間依存の軸にすればいいという感じでしょうか。
指示に合った通りスクリプトを一部改変してみます。

transform.Translate(0, inputY * speed * Time.deltaTime, 0);

transform.Translate(0, inputY * speed * Time.deltaTime, 0, Space.World);

これで動かしてみます。

上下キーの入力で垂直方向に動きました!

これで最初は適当に画面に置いただけの自機が
プレハブを持っていて当たり判定もある自機になったようです。

ゲームをやっているとプレイヤーキャラが動くのは当たり前ですが、自分で作って自分の入力で動いているとそれもまた楽しいですね。

Rigidbody2D を追加

自機の基本的な動きができたと思っていたのですが、後になってこれだと駄目だということが分かりました。

というのも敵の実装を進めているときに"敵が自機にぶつかった時にお互いが消滅する"というようなスクリプトを作ったのですが、
"敵が自機にめり込んで何も起きない"という現象が起きました。

これは自機が当たり判定自体は持っているものの、Rigidbody2D を持っていないため、物理エンジンによる衝突処理が行われていないことが原因とのこと。

transformのみで2Dシューティングゲームの移動を実装した場合どうなりますか?

良い質問です!
transform(Transform.Translateやposition)だけで移動を実装すると、
見た目上はちゃんと動きますが、物理エンジンとの連携が取れず、さまざまな問題が発生します。

Transformのみの移動:メリットとデメリット

分類 内容
✅ メリット シンプル:1行で移動できる
軽量:物理演算が不要で処理が高速
❌ デメリット
  • 衝突しない:Colliderとぶつかってもすり抜ける
  • 自然な挙動がない:弾かれたり跳ね返ることがない
  • イベントが発生しないOnCollisionEnter2DOnTriggerEnter2D が動かない
  • Triggerも無効:アイテム取得などが判定できない

💡 補足
Unityでは、Rigidbody2D を持つオブジェクトだけが物理演算やイベントの対象になります。
→ プレイヤーや敵、弾など移動+衝突が重要なものには Rigidbody2D をつけて、velocityMovePosition を使うのがベストです。

✅ Transform だけで済ませるのは、「背景のスクロール」や「UI演出」など衝突が不要な移動に限るのがおすすめです。

こういう自機などは通常、当たった後に何かしらのイベントを起こすためにRigidbodyを付ける必要があるそうですね。
最初に2Dシューティングゲームの自機って言ったんだからそうしてといてよ!とも思いますが、まぁそんなもんかとも思います。
勉強になったんで良しとします。

Rigidbody2D を使うといいと聞いたんですが、どうやって設定するんですか?

とても良い気づきです!

transform.Translate は座標を直接動かすだけなので、
当たり判定や衝突が自然に処理されません。
Rigidbody2D を使うことで、Unityの物理演算に沿った動きができます。

🛠 Rigidbody2D の追加手順(自機に設定)

Step 1:Rigidbody2D を追加

  • Hierarchy で Player オブジェクトを選択
  • Inspector ウィンドウで Add Component をクリック
  • Rigidbody2D を検索して追加

Step 2:シューティング用の設定

項目 設定値 説明
Body Type Dynamic 通常の物理オブジェクト(動いて衝突もする)
Gravity Scale 0 重力を受けない(横スクロールや空中系ゲームでは不要)
Interpolate Interpolate 補間してカクつきを軽減(任意)
Collision Detection Continuous 高速移動時でも衝突を見逃さない(弾などに特に有効)

📌 Body Type の違い

タイプ 用途
Dynamic 動いて当たるオブジェクト(自機や敵など)
Kinematic 手動で動かすが、物理には影響されない(動く床など)
Static 完全に固定(背景や壁など)

設定すると何が変わる?
・物理エンジンによる衝突や重なりの判定ができるようになります
・移動には rb.velocityrb.MovePosition() を使うのが基本になります

🎯 これで「当たり判定が自然に効くプレイヤー」が完成します!

これで"敵が当たった時にやれてしまう"といった処理が働くようになったようです。

FixedUpdate へ移植

Rigidbody2DにするとTransformで座標を書き換えるのではなく、
オブジェクトに速度を与えて位置を変化させるのがいいと書いてありました。

というわけでそれも書き換えていきます。

Rigidbody2D を動かす処理

お願いします

了解しました!
ここでは、Rigidbody2D を使って自機を上下に移動させる方法を丁寧に解説します。

🎯 目的
・物理エンジンに従って安定した移動を実現
FixedUpdate()velocity を操作する

必要な構成
自機に以下のコンポーネントを追加してください:

  • SpriteRenderer
  • BoxCollider2D
  • Rigidbody2D(Body Type:Dynamic / Gravity Scale:0)

📜 移動スクリプト:PlayerMovement.cs

using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
public class PlayerMovement : MonoBehaviour
{
    public float moveSpeed = 5f;

    private Rigidbody2D rb;
    private float inputY;

    void Start()
    {
        // Rigidbody2D を取得
        rb = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        // 毎フレームの入力を取得(↑↓キー / W Sキー)
        inputY = Input.GetAxis("Vertical");
    }

    void FixedUpdate()
    {
        // Rigidbody2D に速度を与えて移動させる
        rb.velocity = new Vector2(0, inputY * moveSpeed);
    }
}

🔍 解説ポイント

処理 説明
Input.GetAxis("Vertical") キーボード入力(-1〜1)を取得
FixedUpdate() 物理演算タイミングでの処理(安定性UP)
rb.velocity = new Vector2(...) 速度ベースの自然な移動

🎮 動作確認手順

  1. Unityで Player オブジェクトを作成
  2. このスクリプトをアタッチ
  3. 再生して、↑↓またはW/Sキーで上下移動できるか確認

よくあるミスと対策

症状 原因と対処
動かない Rigidbody2D の Body Type が Dynamic か確認
下に落ちる Gravity Scale を 0 に設定
ガクガクする 移動処理は必ず FixedUpdate() で行う


C#メモ
コード 意味
[RequireComponent(typeof(Rigidbody2D))] アタッチしたオブジェクトに Rigidbody2D を強制的に付与する属性。



これを元にスクリプトを変更して動くことは確認できました。

Transform → Rigidbody2D.velocity に切り替えるメリット

しかし、そもそもなんでこれに書き換えると良いのでしょうか?

Transformでの移動からrb.velocityなどに書き換えましたがこれによるメリットはなんですか?

とても良い判断です!Transform での移動から Rigidbody2D.velocityMovePosition に切り替えることによる主なメリットは以下の4つです:

1. 衝突判定が正しく動作するようになる
OnCollisionEnter2DOnTriggerEnter2D などのイベントが発生
・Transformだとめり込み・無反応になる

2. 物理演算と連携できる
・加速度・跳ね返りなどのリアルな反応が表現可能

3. 他の物理オブジェクトとの相互作用が正しく働く
Rigidbody2D 同士の衝突・押しのけ・反発が自然になる

4. 拡張性が高い
・速度変化、パワーアップ、ノックバックなどの制御がしやすい
例:
・弾速アップ
・スピードアップ効果
・短時間のダッシュや反動

Transform移動のままだと…

問題点 内容
衝突判定なし OnTrigger などのイベントが発生しない
不自然な動き 他オブジェクトと干渉せず「すり抜け」になる
拡張性が低い 演出やバフ効果を付けにくい

とりあえず動いて何かが発生するものにはRigidbodyを付けなさいってことですね。
今後はChatGPTの方からTransformで制御するんやぞ!と言われた時にちゃんと注意できるように覚えておきましょう。

画面外へのはみ出し防止

移動させると自機が画面外に出ていってどこにいるか分からなくなります。
出ていかないようにするためにはどうすればいいか聞いてみます。

画面外へ自機が出ていかないようにしたいです。

了解しました!
ここでは、自機が画面外にはみ出さないように縦方向をClamp(制限)する方法をご紹介します。

🎯 目的
・自機が画面上や下へ飛び出さないように制限
Mathf.Clamp で Y座標を調整します

概要

  • カメラの上下端(画面サイズ)を計算
  • FixedUpdate() の最後に位置を制限

📜 コード:PlayerMovement.cs(制限付き)

using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
public class PlayerMovement : MonoBehaviour
{
    public float moveSpeed = 5f;
    public float padding = 0.5f; // 画面端との余白

    private Rigidbody2D rb;
    private float inputY;

    private float minY, maxY;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        CalculateVerticalLimits();
    }

    void Update()
    {
        inputY = Input.GetAxis("Vertical");
    }

    void FixedUpdate()
    {
        rb.velocity = new Vector2(0, inputY * moveSpeed);

        // Y位置を画面内に制限
        Vector2 pos = transform.position;
        pos.y = Mathf.Clamp(pos.y, minY, maxY);
        transform.position = pos;
    }

    void CalculateVerticalLimits()
    {
        Camera cam = Camera.main;
        float camHeight = cam.orthographicSize;
        float screenBottom = cam.transform.position.y - camHeight;
        float screenTop = cam.transform.position.y + camHeight;

        minY = screenBottom + padding;
        maxY = screenTop - padding;
    }
}

🔍 解説

処理 内容
Camera.main.orthographicSize カメラの縦サイズ(半分)
Mathf.Clamp(value, min, max) Y座標を範囲内に制限
padding 画面端にぴったり張り付かないように余白をつける

確認ステップ

  1. カメラが Orthographic モードであることを確認
  2. 上記スクリプトを自機にアタッチ
  3. 再生して、上下にはみ出さないことを確認

💡 補足
padding の値を大きくすると、画面端から少し余裕を持って止まります(例:0.3〜0.6)
・この方法は、カメラがスクロールするゲームでもそのまま応用できます

これで画面外に出ないようになりました。

おわりに

今回、自機の操作を作りました。知識不足で回り道をしましたが、なんだかんだ狙い通りの実装をすることはできました。
現在、自機がスライドするように移動していて不自然なので次回は自機にアニメーションを設定していきます。

  • この記事を書いた人

たげお

ソロゲームもオンラインゲームも対戦ゲームもソシャゲも楽しむ雑食ゲーマー。特筆して好きなジャンルはRPG、FPS。最近は格ゲーにお熱。 ゲーム好きが高じて自分で作りたくなった。

-Unity, ゲーム開発, 生成AI
-, , ,