オブジェクトを動かそう:位置・向き・Time.deltaTime【Unityスタートガイド6-8】

6-7で入力が受け取れるようになったので、いよいよキャラクターを動かしていきます!オブジェクトの位置の変え方、どのパソコンでも同じ速さで動かすためのTime.deltaTime、進む方向を向かせる方法まで、プレイヤーの移動に必要なことをまとめてご紹介します。

目次

この記事で学べること

  • transform.positionでオブジェクトの位置を変える方法
  • Vector3の足し算・掛け算と、よく使う方向(Vector3.forwardなど)
  • Time.deltaTimeで、FPSに関係なく同じ速さで動かす方法
  • 斜め移動が速くならないようにする方法(normalized)
  • 進む方向にオブジェクトを向かせる方法
  • Rigidbodyで動かす場合の書き方

前提知識

位置を変える:transform.position

位置を直接指定する

オブジェクトの位置は、transform.position(Vector3)で読み書きできます:

// 今の位置を読む
Vector3 currentPosition = transform.position;

// 指定した場所に移動させる(ワープ)
transform.position = new Vector3(1f, 0f, 0f);

transform.positionはワールド座標(シーン全体での位置)です。親から見た位置はtransform.localPositionで読み書きできます(3-2の記事で説明した、ワールド座標とローカル座標の違いです)。

少しずつ動かす

毎フレーム少しずつ位置を足していくと、オブジェクトが動いて見えます:

void Update()
{
    // 毎フレーム、右(X軸のプラス方向)に少しずつ動かす
    transform.position += new Vector3(0.01f, 0f, 0f);
}

Vector3のよく使う書き方

方向の決まった値

書き方値意味
Vector3.zero(0, 0, 0)原点、動かない
Vector3.right(1, 0, 0)右
Vector3.up(0, 1, 0)上
Vector3.forward(0, 0, 1)前(奥)

Vector3.left(左)、Vector3.down(下)、Vector3.back(後ろ)もあります。

計算

Vector3 a = new Vector3(1f, 2f, 0f);
Vector3 b = new Vector3(3f, 0f, 0f);

Vector3 sum = a + b;               // (4, 2, 0) それぞれを足す
Vector3 doubled = a * 2f;          // (2, 4, 0) それぞれに掛ける
float length = a.magnitude;        // 長さ(原点からの距離)
float distance = Vector3.Distance(a, b); // 2点の距離

「方向 × 速さ」で、どの向きにどれだけ進むかを表せます。

Time.deltaTime:どのパソコンでも同じ速さにする

1フレームごとに足すと、速さが変わってしまう

上の例の0.01fを毎フレーム足すやり方だと、

  • 60FPSのパソコン:1秒で0.6進む
  • 144FPSのパソコン:1秒で1.44進む

と、パソコンによって速さが変わってしまいます。

「1秒あたりの速さ × deltaTime」で解決

Time.deltaTimeは、前のフレームから何秒たったかです。

[SerializeField] private float moveSpeed = 5f;   // 1秒あたりに進む距離

void Update()
{
    transform.position += Vector3.right * moveSpeed * Time.deltaTime;
}
  • 60FPS:1フレーム約0.016秒 → 1フレームで約0.08進む × 60回 = 1秒で5
  • 144FPS:1フレーム約0.007秒 → 1フレームで約0.035進む × 144回 = 1秒で5

Updateで動かすときは、速さにTime.deltaTimeを掛ける。これだけ覚えておけばOKです。

スライム

自分のパソコンではちょうどいいのに、友だちのパソコンだと爆速に・・・

フェアリー

それはTime.deltaTimeの掛け忘れのサインだね!動きや回転の速さには、必ず掛けるクセをつけよう

Translate:向いている方向を基準に動かす

transform.Translate()でも動かせます。こちらはオブジェクトが向いている方向を基準に動きます。

// オブジェクトの「前」に進む(回転していれば、その向きの前)
transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);

// 向きに関係なく、ワールドの「前」に進む
transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime, Space.World);
書き方基準
transform.position += 方向ワールド(シーン全体)
transform.Translate(方向)オブジェクト自身の向き
transform.Translate(方向, Space.World)ワールド

車のように「向いている方向に前進」ならTranslate、見下ろし型のように「画面の上に進む」ならposition +=が分かりやすいです。

入力で動かす:プレイヤーの移動スクリプト

6-7で作った入力と組み合わせて、見下ろし型のプレイヤーを作ってみましょう。

準備

  1. Universal 3Dのプロジェクトで、Hierarchyで右クリック>3D Object>Planeを作る(床)
  2. Hierarchyで右クリック>3D Object>Capsuleを作り、名前をPlayerにする(Position Yを1にして床の上に置く)
  3. Main Cameraを、Playerを斜め上から見下ろす位置に置く(例:Position (0, 10, -8)、Rotation (50, 0, 0))
  4. 6-7のGenerate C# Classが済んでいることを確認

スクリプト

「PlayerMover」という名前でスクリプトを作り、Playerにアタッチします。

using UnityEngine;

public class PlayerMover : MonoBehaviour
{
    [SerializeField] private float walkSpeed = 4f;
    [SerializeField] private float runSpeed = 8f;
    [SerializeField] private float turnSpeed = 720f;   // 1秒あたりに回転する角度

    private InputSystem_Actions inputActions;

    void Awake()
    {
        inputActions = new InputSystem_Actions();
    }

    void OnEnable()
    {
        inputActions.Enable();
    }

    void OnDisable()
    {
        inputActions.Disable();
    }

    void Update()
    {
        // 入力(x:左右、y:上下)を、3Dの床の上の方向(x:左右、z:奥・手前)にする
        Vector2 input = inputActions.Player.Move.ReadValue<Vector2>();
        Vector3 direction = new Vector3(input.x, 0f, input.y);

        // 斜めに入力しても速くならないように、長さを1以下にそろえる
        if (direction.magnitude > 1f)
        {
            direction = direction.normalized;
        }

        // 入力がないときは何もしない
        if (direction == Vector3.zero)
        {
            return;
        }

        // Shiftを押している間は走る
        float speed = inputActions.Player.Sprint.IsPressed() ? runSpeed : walkSpeed;
        transform.position += direction * speed * Time.deltaTime;

        // 進む方向に、少しずつ向きを変える
        Quaternion targetRotation = Quaternion.LookRotation(direction);
        transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, turnSpeed * Time.deltaTime);
    }
}

再生して、WASDで動き、Shiftで速くなり、進む方向を向けば成功!

スクリプトのポイント

  • 入力の向きを3Dの向きに変える
    • Moveの入力はVector2(x:左右、y:上下)
    • 床の上を動く3Dゲームでは、上下の入力をZ軸(奥・手前)に使うのでnew Vector3(input.x, 0f, input.y)にします
    • 2Dゲームなら、そのままnew Vector3(input.x, input.y, 0f)でOKです
  • normalized(ノーマライズ)
    • 右と上を同時に押すと、方向は(1, 0, 1)になり、長さが約1.41になります。そのままだと斜めだけ1.4倍速くなってしまうので、長さを1にそろえます
    • ゲームパッドのスティックを少しだけ倒したときは長さが1より小さいので、そのときはそのまま使います(ゆっくり歩ける)
  • 条件 ? A : B
    • 条件がtrueならA、falseならBになる書き方です。ifで書くと次と同じ意味です
float speed;
if (inputActions.Player.Sprint.IsPressed())
{
    speed = runSpeed;
}
else
{
    speed = walkSpeed;
}
  • 向きを変える
    • Quaternion.LookRotation(direction):その方向を向いたときの回転
    • Quaternion.RotateTowards(今の回転, 目標の回転, 最大の角度):目標に向かって、決まった角度ずつ回す。一瞬で振り向かせたいときはtransform.rotation = targetRotation;だけでもOKです

Rigidbodyで動かす場合

壁にぶつかって止まる、押し返される、などの物理的な動きをさせたいときは、Rigidbodyを付けてRigidbodyに動かしてもらいます。

using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class PhysicsMover : MonoBehaviour
{
    [SerializeField] private float moveSpeed = 5f;

    private InputSystem_Actions inputActions;
    private Rigidbody rb;
    private Vector3 direction;

    void Awake()
    {
        inputActions = new InputSystem_Actions();
        rb = GetComponent<Rigidbody>();
    }

    void OnEnable()
    {
        inputActions.Enable();
    }

    void OnDisable()
    {
        inputActions.Disable();
    }

    void Update()
    {
        // 入力はUpdateで読む
        Vector2 input = inputActions.Player.Move.ReadValue<Vector2>();
        direction = Vector3.ClampMagnitude(new Vector3(input.x, 0f, input.y), 1f);
    }

    void FixedUpdate()
    {
        // 物理の動きはFixedUpdateで。上下の速さ(重力)はそのまま残す
        Vector3 velocity = direction * moveSpeed;
        velocity.y = rb.linearVelocity.y;
        rb.linearVelocity = velocity;
    }
}
  • rb.linearVelocity:Rigidbodyの速度(1秒あたりに進む量)。Unity6でvelocityから名前が変わりました。古い記事のrb.velocityは、今はrb.linearVelocityと読み替えてください
  • FixedUpdate:6-3で説明した、物理演算と同じ間隔で呼ばれるメソッド。Rigidbodyを動かす処理はここに書きます
  • 速度そのものを指定しているので、Time.deltaTimeは掛けません(Rigidbodyが時間に合わせて動かしてくれます)
  • Vector3.ClampMagnitude(方向, 1f):長さを1以下にする書き方で、上のnormalizedのif文と同じ役割です
  • [RequireComponent(typeof(Rigidbody))]:このスクリプトを付けると、Rigidbodyも自動で付けてくれます
  • キャラクターが転がってしまう場合は、RigidbodyのConstraints>Freeze RotationのX・Zにチェックを入れます

transform.positionで動かすと、壁の中にめり込んだりすり抜けたりすることがあります。壁や床との当たり判定が大事なキャラクターはRigidbodyで動かす、と覚えておきましょう。

よくあるエラーと対処法

エラー:動かない

  • 原因:6-7のinputActions.Enable()をしていない、速さの値が0になっている、またはスクリプトにエラーがある
  • 対処法:Inspectorの速さの値と、Consoleのエラーを確認する

エラー:パソコンによって速さが違う

  • 原因:Updateで動かしているのにTime.deltaTimeを掛けていない
  • 対処法:速さに* Time.deltaTimeを掛ける

エラー:斜めだけ速い

  • 原因:方向の長さを1にそろえていない
  • 対処法:normalizedまたはVector3.ClampMagnitude(方向, 1f)を使う

エラー:思った方向と違う方向に進む

  • 原因:Translateはオブジェクトの向きが基準。回転させたオブジェクトで使うと、ワールドの方向とずれる
  • 対処法:ワールドの方向で動かしたいときはtransform.position +=か、Translate(方向, Space.World)を使う

エラー:’Rigidbody.velocity’ is obsolete

  • 原因:Unity6ではvelocityの名前が変わった
  • 対処法:linearVelocityに書き換える

まとめ

  • 位置はtransform.position。少しずつ足すと動いて見える
  • Updateで動かすときは、速さ × Time.deltaTime
  • 斜め移動はnormalizedで長さをそろえる
  • 進む方向を向かせるのはQuaternion.LookRotationとRotateTowards
  • 物理的な動きが大事なときは、RigidbodyのlinearVelocityをFixedUpdateで設定する

次のステップ

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメント一覧 (1件)

コメントする

目次