ダークモード
第4回 画面からはみ出さない
この回でつくるもの:自機が画面の外に出ていかないように、動ける範囲を決めます。さらに、上下に動くときに機体がくるっと傾く演出を加えます。
この回で学ぶこと
- 自機の位置を
Mathf.Clampで範囲の中におさめる方法 - 動ける範囲を Inspector で調整して、Game 画面で確かめる方法
Quaternion.Eulerで角度を作り、Quaternion.Lerpでなめらかに回す方法Transform型の変数に、Inspector からオブジェクトをつなぐ方法if文とnull(からっぽ)のチェック
前回までのふりかえり
第3回では、スクリプト PlayerController を作り、Input System の Move アクションで自機を上下左右に動かせるようにしました。 速さは Inspector の Speed で変えられます。でも、自機は画面の外まで動けてしまいます。
1. はみ出す問題を確認する
1-1. 画面の外まで行ってみる
- Unity Hub から SpaceShooter を開き、
Assets/_Game/Scenesの Game シーンが開いていることを確認します。 - ▶ ボタンを押します。
- Game ビューを1回クリックします。
- → キー(または D)を押しつづけます。
- 自機が画面の右の端をこえて、見えなくなってしまうことを確認します。
- ▶ ボタンを押して止めます。
シューティングゲームで自機が画面の外に行ってしまったら、遊べませんね。 自機が動ける範囲を決めて、その外には出られないようにしましょう。
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. コードを書く
- Project ウィンドウの
Assets/_Game/Scriptsにある PlayerController をダブルクリックして開きます。 ★の付いた行を書き足して、次のようにします。
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; // ★追加
}
}- Ctrl+S(Macは Cmd+S)でファイルを保存します。
- 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. 動作確認
- ▶ ボタンを押します。
- Game ビューを1回クリックします。
- 上下左右の端まで動かしてみて、自機が画面の中で止まればOK。
- ▶ ボタンを押して止めます。
3. 範囲を Inspector で調整する
3-1. Inspector に出た4つの値
- Hierarchy で Player を選びます。
- Inspector の Player Controller (Script) に、Min X、Max X、Min Y、Max Y が増えていることを確認します。
3-2. Game 画面を見ながら調整する
選んだ宇宙船の大きさによって、ちょうどよい値は少しちがいます。Game ビューを見ながら調整しましょう。
- ▶ ボタンを押します。
- Game ビューを1回クリックし、自機をいちばん右まで動かします。
- 自機の機首が画面の端からはみ出していたら、Hierarchy で Player を選び、Inspector の Max X を
7.5のように小さくします。 - 逆に、端まで行けなくてすき間が大きいときは、Max X を
8.3のように大きくします。 - 同じように、左(Min X)、上(Max Y)、下(Min Y)も確かめて、ちょうどよい値を見つけます。
- 見つけた4つの値を、紙などに メモ します。
- ▶ ボタンを押して止めます。
- 止めると値が元に戻るので、メモした値を Min X、Max X、Min Y、Max Y に入れなおします。
- 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. コードを書く
- Project ウィンドウの PlayerController をダブルクリックして開きます。
★の付いた行を書き足して、次のようにします。
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); // ★追加
} // ★追加
}
}- Ctrl+S(Macは Cmd+S)でファイルを保存します。
- 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 でつなぎましょう。
- Hierarchy で Player を選びます。
- Inspector の Player Controller (Script) に、Bank と Bank Angle が増えていることを確認します。Bank の欄は None (Transform) になっています。
- Hierarchy の Bank(Player の子)を、Inspector の Bank の欄にドラッグして離します。
- Bank の欄が Bank (Transform) になったことを確認します。
- Ctrl+S(Macは Cmd+S)でシーンを保存します。
よくあるまちがい
Inspector の Bank の欄に、Player や Model を入れてしまうまちがいが多いです。必ず Bank を入れましょう。 Player を入れると、親ごと回ってしまいます。Model を入れると、第2回で直した機首の向きがくずれてしまいます。
4-7. 動作確認
- ▶ ボタンを押します。
- Game ビューを1回クリックします。
- ↑(または W)を押して、自機が上に動きながら傾けばOK。
- ↓(または S)を押して、反対側に傾けばOK。
- キーをはなすと、水平にもどることを確認します。
- ▶ ボタンを押して止めます。
4-8. 傾く向きを調整する
選んだモデルや向きの直し方によっては、傾く向きが思っていたのと逆になることがあります。
- Hierarchy で Player を選びます。
- 傾く向きが逆だと感じたら、Inspector の Bank Angle を
-30にします。 - 傾きが大きすぎる・小さすぎると感じたら、Bank Angle を
20や45などに変えて、好きな角度にします。 - ▶ ボタンを押して確かめ、止めてから決めた値を入れます。
- 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 は完成 です。次回はいよいよ弾を撃ちます。