Skip to content

第4回 画面からはみ出さない ​

この回でつくるもの:自機が画面の外に出ていかないように、動ける範囲を決めます。さらに、上下に動くときに機体がくるっと傾く演出を加えます。

この回で学ぶこと ​

  • 自機の位置を Mathf.Clamp で範囲の中におさめる方法
  • 動ける範囲を Inspector で調整して、Game 画面で確かめる方法
  • Quaternion.Euler で角度を作り、Quaternion.Lerp でなめらかに回す方法
  • Transform 型の変数に、Inspector からオブジェクトをつなぐ方法
  • if 文と null(からっぽ)のチェック

前回までのふりかえり ​

第3回では、スクリプト PlayerController を作り、Input System の Move アクションで自機を上下左右に動かせるようにしました。 速さは Inspector の Speed で変えられます。でも、自機は画面の外まで動けてしまいます。

1. はみ出す問題を確認する ​

1-1. 画面の外まで行ってみる ​

  1. Unity Hub から SpaceShooter を開き、Assets/_Game/Scenes の Game シーンが開いていることを確認します。
  2. ▶ ボタンを押します。
  3. Game ビューを1回クリックします。
  4. → キー(または D)を押しつづけます。
  5. 自機が画面の右の端をこえて、見えなくなってしまうことを確認します。
  6. ▶ ボタンを押して止めます。

シューティングゲームで自機が画面の外に行ってしまったら、遊べませんね。 自機が動ける範囲を決めて、その外には出られないようにしましょう。

1-2. 動ける範囲を考える ​

第1回で調べたとおり、見える範囲は x がおよそ -8.9〜8.9、y が -5〜5 です。 でも、自機の「真ん中」が端にくると、自機の半分が画面の外に出てしまいます。 自機の横幅は約 1.5 なので、そのぶん少し内側にします。

変数意味値
minX左の限界-8
maxX右の限界8
minY下の限界-4.5
maxY上の限界4.5

2. Mathf.Clamp で範囲におさめる ​

2-1. Mathf.Clamp とは ​

Mathf.Clamp(値, 最小, 最大) は、値を「最小〜最大」の間におさめてくれる命令です。

書き方結果理由
Mathf.Clamp(5, -8, 8)5範囲の中なのでそのまま
Mathf.Clamp(10, -8, 8)8大きすぎるので最大の 8 になる
Mathf.Clamp(-12, -8, 8)-8小さすぎるので最小の -8 になる

Clamp は「留め金で留める」という意味です。決めた範囲からはみ出さないように留めてくれます。

2-2. コードを書く ​

  1. Project ウィンドウの Assets/_Game/Scripts にある PlayerController をダブルクリックして開きます。
  2. ★ の付いた行を書き足して、次のようにします。
csharp
using UnityEngine;
using UnityEngine.InputSystem;

// 自機を動かすスクリプト
public class PlayerController : MonoBehaviour
{
    public float speed = 6f;          // 移動の速さ

    // 動ける範囲(第4回)                    // ★追加
    public float minX = -8f;                 // ★追加
    public float maxX = 8f;                  // ★追加
    public float minY = -4.5f;               // ★追加
    public float maxY = 4.5f;                // ★追加

    InputAction moveAction;

    void Start()
    {
        moveAction = InputSystem.actions.FindAction("Move");
    }

    void Update()
    {
        // 入力を読む(左右が x、上下が y。-1 から 1 の値)
        Vector2 input = moveAction.ReadValue<Vector2>();

        // 動かす
        Vector3 move = new Vector3(input.x, input.y, 0f);
        transform.position += move * speed * Time.deltaTime;

        // 画面からはみ出さないようにする                       // ★追加
        Vector3 pos = transform.position;                    // ★追加
        pos.x = Mathf.Clamp(pos.x, minX, maxX);              // ★追加
        pos.y = Mathf.Clamp(pos.y, minY, maxY);              // ★追加
        transform.position = pos;                            // ★追加
    }
}
  1. Ctrl+S(Macは Cmd+S)でファイルを保存します。
  2. Unity に戻り、読み込みが終わるまで待ちます。Console に赤いエラーが出ていないか確認します。

2-3. コードの解説 ​

行意味
public float minX = -8f; など動ける範囲を4つの変数にする。public なので Inspector で変えられる
Vector3 pos = transform.position;今の位置を pos という箱にコピーする
pos.x = Mathf.Clamp(pos.x, minX, maxX);x を minX〜maxX の間におさめる
pos.y = Mathf.Clamp(pos.y, minY, maxY);y を minY〜maxY の間におさめる
transform.position = pos;おさめた位置を、自機の位置にもどす

「動かす → はみ出していたら戻す」の順番 が大事です。動かしたあとに Clamp するので、画面に表示される前に、いつも範囲の中に戻っています。

ヒント

どうして transform.position.x = ... と直接書かないのでしょう? C# では transform.position の x だけを直接書きかえることができないルールになっています。だから一度 pos にコピーして、x と y を直してから、まとめて戻しているのです。

2-4. 動作確認 ​

  1. ▶ ボタンを押します。
  2. Game ビューを1回クリックします。
  3. 上下左右の端まで動かしてみて、自機が画面の中で止まればOK。
  4. ▶ ボタンを押して止めます。

3. 範囲を Inspector で調整する ​

3-1. Inspector に出た4つの値 ​

  1. Hierarchy で Player を選びます。
  2. Inspector の Player Controller (Script) に、Min X、Max X、Min Y、Max Y が増えていることを確認します。

3-2. Game 画面を見ながら調整する ​

選んだ宇宙船の大きさによって、ちょうどよい値は少しちがいます。Game ビューを見ながら調整しましょう。

  1. ▶ ボタンを押します。
  2. Game ビューを1回クリックし、自機をいちばん右まで動かします。
  3. 自機の機首が画面の端からはみ出していたら、Hierarchy で Player を選び、Inspector の Max X を 7.5 のように小さくします。
  4. 逆に、端まで行けなくてすき間が大きいときは、Max X を 8.3 のように大きくします。
  5. 同じように、左(Min X)、上(Max Y)、下(Min Y)も確かめて、ちょうどよい値を見つけます。
  6. 見つけた4つの値を、紙などに メモ します。
  7. ▶ ボタンを押して止めます。
  8. 止めると値が元に戻るので、メモした値を Min X、Max X、Min Y、Max Y に入れなおします。
  9. Ctrl+S(Macは Cmd+S)でシーンを保存します。

ヒント

メモするかわりに、プレイ中に Player Controller (Script) の右はしの ︙ を押して Copy Component を選び、止めてから同じ ︙ で Paste Component Values を選ぶと、プレイ中の値をそのまま持ってこられます。

先生へ

第11回以降、残機やスコアの文字が画面の上に表示されます。そのとき文字と自機が重なるのが気になる場合は、Max Y を少し小さくするよう声をかけてください。

注意

Game ビューの比率が 16:9 になっていないと、横に見える広さが変わってしまいます。Game ビューの上のメニューが Full HD (1920x1080)(または 16:9 Aspect)になっているか確認しましょう。

4. 機体を傾ける ​

4-1. どんな動きにするの? ​

上に動くときは機体がくるっと傾き、下に動くときは反対に傾き、止まると水平にもどる……という演出を加えます。 これだけで、ぐっとかっこよくなります。

ここで、第2回で作った Bank の出番です。 Player(親)は回さずに、Bank だけを回します。Bank の子の Model もいっしょに回るので、見た目だけが傾きます。

4-2. 角度の作り方 Quaternion.Euler ​

Unity では、回転を Quaternion(クォータニオン)という形であつかいます。中身は難しいので、覚えなくて大丈夫です。 Quaternion.Euler(x, y, z) と書けば、ふつうの角度(度)から回転を作ってくれます。

書き方意味
Quaternion.Euler(30f, 0f, 0f)X 軸のまわりに 30 度回す
Quaternion.Euler(-input.y * bankAngle, 0f, 0f)上を押すと -30 度、下を押すと 30 度、押さないと 0 度

自機は右(X のプラス方向)を向いているので、X 軸のまわりに回すと、機首を中心にくるっと傾きます。

4-3. なめらかに回す Quaternion.Lerp ​

いきなり 30 度に変わると、カクッとして不自然です。 Quaternion.Lerp(今の回転, 目標の回転, 割合) を使うと、今の回転から目標の回転へ 少しずつ 近づけられます。

毎フレーム「目標までの残りの何割か」だけ回すので、最初は速く、目標に近づくとゆっくりになります。 10f * Time.deltaTime の 10 を大きくするとすばやく、小さくするとゆっくり傾きます。

4-4. コードを書く ​

  1. Project ウィンドウの PlayerController をダブルクリックして開きます。
  2. ★ の付いた行を書き足して、次のようにします。
csharp
using UnityEngine;
using UnityEngine.InputSystem;

// 自機を動かすスクリプト
public class PlayerController : MonoBehaviour
{
    public float speed = 6f;          // 移動の速さ

    // 動ける範囲(第4回)
    public float minX = -8f;
    public float maxX = 8f;
    public float minY = -4.5f;
    public float maxY = 4.5f;

    // 機体を傾ける演出(第4回)              // ★追加
    public Transform bank;            // 傾ける子オブジェクト      // ★追加
    public float bankAngle = 30f;     // 傾ける角度              // ★追加

    InputAction moveAction;

    void Start()
    {
        moveAction = InputSystem.actions.FindAction("Move");
    }

    void Update()
    {
        // 入力を読む(左右が x、上下が y。-1 から 1 の値)
        Vector2 input = moveAction.ReadValue<Vector2>();

        // 動かす
        Vector3 move = new Vector3(input.x, input.y, 0f);
        transform.position += move * speed * Time.deltaTime;

        // 画面からはみ出さないようにする
        Vector3 pos = transform.position;
        pos.x = Mathf.Clamp(pos.x, minX, maxX);
        pos.y = Mathf.Clamp(pos.y, minY, maxY);
        transform.position = pos;

        // 上下に動くときに機体を傾ける                                                    // ★追加
        if (bank != null)                                                               // ★追加
        {                                                                               // ★追加
            Quaternion target = Quaternion.Euler(-input.y * bankAngle, 0f, 0f);         // ★追加
            bank.localRotation = Quaternion.Lerp(bank.localRotation, target, 10f * Time.deltaTime);  // ★追加
        }                                                                               // ★追加
    }
}
  1. Ctrl+S(Macは Cmd+S)でファイルを保存します。
  2. Unity に戻り、読み込みが終わるまで待ちます。Console に赤いエラーが出ていないか確認します。

4-5. コードの解説 ​

行意味
public Transform bank;傾けるオブジェクト(Bank)を入れておく箱。Transform 型なので、Inspector からオブジェクトをドラッグしてつなげる
public float bankAngle = 30f;いちばん傾いたときの角度(度)
if (bank != null)「bank がからっぽ(null)でなければ」。つなぎわすれてもエラーで止まらないようにする
Quaternion.Euler(-input.y * bankAngle, 0f, 0f)上下の入力に合わせて、目標の角度を作る
bank.localRotation = Quaternion.Lerp(...)Bank の回転を、目標の角度に少しずつ近づける
  • if (条件) { ... } は「条件が正しいときだけ { } の中をやる」という文です。
  • != は「ちがう」という意味です。null は「何も入っていない」ことを表します。
  • localRotation は「親から見た回転」です。親の Player はいつも回転 0 なので、Bank だけが傾きます。

4-6. Bank をつなぐ ​

public Transform bank; の箱には、まだ何も入っていません。Inspector でつなぎましょう。

  1. Hierarchy で Player を選びます。
  2. Inspector の Player Controller (Script) に、Bank と Bank Angle が増えていることを確認します。Bank の欄は None (Transform) になっています。
  3. Hierarchy の Bank(Player の子)を、Inspector の Bank の欄にドラッグして離します。
  4. Bank の欄が Bank (Transform) になったことを確認します。
  5. Ctrl+S(Macは Cmd+S)でシーンを保存します。

よくあるまちがい

Inspector の Bank の欄に、Player や Model を入れてしまうまちがいが多いです。必ず Bank を入れましょう。 Player を入れると、親ごと回ってしまいます。Model を入れると、第2回で直した機首の向きがくずれてしまいます。

4-7. 動作確認 ​

  1. ▶ ボタンを押します。
  2. Game ビューを1回クリックします。
  3. ↑(または W)を押して、自機が上に動きながら傾けばOK。
  4. ↓(または S)を押して、反対側に傾けばOK。
  5. キーをはなすと、水平にもどることを確認します。
  6. ▶ ボタンを押して止めます。

4-8. 傾く向きを調整する ​

選んだモデルや向きの直し方によっては、傾く向きが思っていたのと逆になることがあります。

  1. Hierarchy で Player を選びます。
  2. 傾く向きが逆だと感じたら、Inspector の Bank Angle を -30 にします。
  3. 傾きが大きすぎる・小さすぎると感じたら、Bank Angle を 20 や 45 などに変えて、好きな角度にします。
  4. ▶ ボタンを押して確かめ、止めてから決めた値を入れます。
  5. Ctrl+S(Macは Cmd+S)でシーンを保存します。

ヒント

グラディウスのような横スクロールでは、「上に動くと機体のおなかが少し見える」くらいが自然に見えます。いろいろ試して、自分のお気に入りを見つけましょう。

チャレンジ課題 ​

  • Lerp の 10f を 3f や 30f に変えると、傾き方がどう変わるか試してみましょう。
  • 左右に動くときも少し傾けるには、どうしたらよいでしょう?(ヒント:Quaternion.Euler の y や z に input.x を使ってみる)
  • minX を -8f から -6f に変えると、画面の左のほうに「入れない場所」ができます。どんなゲームになりそうか考えてみましょう。

つまずきポイント ​

こんなときはここを確認
まだ画面からはみ出すInspector の Min X〜Max Y の値。コードの Clamp の行を 動かす行のあと に書いたか
自機がまったく動かなくなったMin X と Max X(または Min Y と Max Y)が逆や同じ値になっていないか
機体が傾かないInspector の Bank の欄に Bank をつないだか。Bank Angle が 0 になっていないか
機首の向きがおかしくなった・自機全体が回るBank の欄に Player や Model を入れていないか
傾く向きが逆Bank Angle をマイナス(例: -30)にする
調整した値が消えたプレイ中に変えていないか。止めてから入れなおして保存する

まとめ ​

  • Mathf.Clamp(値, 最小, 最大) で、自機の位置を画面の中におさめました。
  • 動ける範囲は public 変数なので、Game 画面を見ながら Inspector で調整できます。
  • Quaternion.Euler で目標の角度を作り、Quaternion.Lerp で Bank だけをなめらかに傾けました。
  • これで PlayerController は完成 です。次回はいよいよ弾を撃ちます。