キャラクターを目的地に移動させる時に単純にスタート地点から目的地に移動させるやり方は今までの記事でやってきました。
しかし目的地までずっと同じスピードで移動し、目的地についたらピタッと止めてしまっていました。
操作性重視のゲームではこちらの方がいいですが、リアルな挙動のゲームを作成しようと思ったら歩きだす時や目的地付近に来たら歩くスピードを落としたい所です。
そんな時に使用するのが補間の機能です。
スタート地点と目的地が決まっていたらその間の点を補間して滑らかな動きにする事が出来ます。
この補間はこの記事で初めて扱うわけではなく今までの記事でもちょくちょく出てきていました。
補間に使う関数
補間に使う関数は
Vector3.MoveTowards ある点からある点へ移動させる
Quaternion.RotateTowards ある角度からある角度へ回転させる
Vector3.Lerp ある点からある点の直線線形の割合を求める
Vector3.Slerp ある点からある点の球面線形の割合を求める
これらについては

を参照してください。
補間関数を使って滑らかに移動させる
これらの補間関数を使って、移動を滑らかにしてみましょう。
またそれぞれの違いについて見てみます。
まずはCharacterControllerのMove機能を使ってキャラクターを動かす時に補間して動かすMoveスクリプトを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | using UnityEngine; using System.Collections; public class InterpolationMove : MonoBehaviour { public enum Mode { Normal, MoveTowards, Lerp, Slerp }; // キャラクターを動かすモード [SerializeField] private Mode mode; //キャラクターコントローラー private CharacterController cCon; // キャラクターの速度 private Vector3 velocity; // 前の速度 private Vector3 oldVelocity; // アニメーター private Animator animator; // 歩く速さ [SerializeField] private float moveSpeed = 2f; void Start () { cCon = GetComponent<CharacterController>(); animator = GetComponent<Animator>(); } void Update () { if(cCon.isGrounded) { // キャラクターコントローラのコライダが地面と接触してるかどうか velocity = Vector3.zero; velocity = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); if(mode == Mode.MoveTowards) { velocity = Vector3.MoveTowards(oldVelocity, velocity, moveSpeed * Time.deltaTime); } else if(mode == Mode.Lerp) { velocity = Vector3.Lerp(oldVelocity, velocity, moveSpeed * Time.deltaTime); } else if(mode == Mode.Slerp) { velocity = Vector3.Slerp(oldVelocity, velocity, moveSpeed * Time.deltaTime); } oldVelocity = velocity; if(velocity.magnitude > 0f){ animator.SetFloat("Speed", velocity.magnitude); transform.LookAt(transform.position + velocity); } else { // キーを押している度合いが小さい時 animator.SetFloat("Speed", 0f); } } velocity.y += Physics.gravity.y * Time.deltaTime; cCon.Move(velocity * Time.deltaTime); } } |
enum(列挙型)で補間モードを切り替えられるようにします。
モードはインスペクタで切り替えられるのでNormal、MoveTowards、Lerp、Slerpの4つの補間モードそれぞれで動くキャラクターを4人配置する事にします。
インスペクタで設定したモードで補間のやり方を変えています。
キーボードの方向キーの移動量を取得し、その移動量を前の値から新しい値へと補間した状態をvelocityに入れ直します。
その後oldVelocityに現在のvelocityを代入します。
後はシンプルなキャラクターを動かす処理が記述されているだけです。
ただ、今回は補間で滑らかな移動をさせる為にアニメーションの切り替えをvelocity.magnitudeが0.1fではなく0.01fより大きい時という条件に変更しておきます。
そうしないとアニメーションが終わった状態で移動してしまいます。
補間移動スクリプトをキャラクターに取り付ける
これでスクリプトが出来たのでStandardAssetのEthanをキャラクターとして配置し、Moveスクリプトを取りつけます。
その後Ethanを選択してCtrl+Dキーで3人コピーし合計4人のEthanを作成します。
↑のように名前を変えました。
それぞれのキャラクターに設置しているMoveスクリプトのmodeを別々に設定します。
↑のようにEthanだらけです!ヽ(^o^)丿ヽ(^o^)丿ヽ(^o^)丿ヽ(^o^)丿
それぞれのMoveスクリプトのmodeを別々にして、同時に動かし検証してみます。
それぞれの名前が対応するモードと同じになっており、上から順番になっています。
カメラがキャラクターを追いかけるようにMainCameraにFollowCameraスクリプト(StandardAssetに入っている)を設定し、真ん中のEthanをTargetに設定します。
補間移動の違いを確認する
それでは動かしてみましょう。
↑のような感じになります。
1番上のキャラクターは補間を使っていないので動きがキビキビした感じになります。
2番目のキャラクターはMoveTowardsで1番目の何もしていないキャラクターと比較的に同じ動きです。
3番目はLerpなので直線的な補間になります。
4番目はSlerpで球面的な補間になります。
キャラクターを使った移動で補間を使いましたが違いがちょっと解り辛いですね・・・(^_^;)
1 2 3 | transform.position = Vector3.Lerp(transform.position, target.position, Time.deltaTime); |
上のように直接位置を操作した方がサンプルとしてわかりやすかったかもしれません・・・(^_^;)
またはマウスクリックの位置に移動する機能とかだとわかりやすいかも?

のような機能ですね。
キャラクターの回転に補間を使用する
次はキャラクターを回転させるスクリプトRotateを作成しMoveの時と同じようにキャラクターに取りつけて確認してみます。
Rotateは以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | using UnityEngine; using System.Collections; public class InterpolationRotate : MonoBehaviour { public enum RotateMode { Normal, RotateTowards, Lerp, Slerp }; public RotateMode mode; // 速度 private Vector3 velocity; // キャラクターの角度 private Quaternion rotation; // 変更する角度 [SerializeField] private float rotateAngle = 45f; // 回転スピード [SerializeField] private float rotateSpeed = 1f; void Start () { rotation = transform.rotation; } void Update () { rotation = Quaternion.Euler (0f, transform.eulerAngles.y + Input.GetAxis("Horizontal") * rotateAngle, 0f); if (mode == RotateMode.RotateTowards) { transform.rotation = Quaternion.RotateTowards(transform.rotation, rotation, rotateSpeed * Time.deltaTime); } else if (mode == RotateMode.Lerp) { transform.rotation = Quaternion.Lerp(transform.rotation, rotation, rotateSpeed * Time.deltaTime); } else if (mode == RotateMode.Slerp) { transform.rotation = Quaternion.Slerp(transform.rotation, rotation, rotateSpeed * Time.deltaTime); } else { transform.rotation = rotation; } } } |
enumでRotateModeを宣言します。
MoveTowardsの代わりにRotateTowardsにします。
RotateTowardsはMoveTowardsの角度バージョンという感じです。
横方向のキーが押されていたら現在の角度に入力度合い×回転角度を足して角度を計算します。
Normal以外のモードはrotateSpeedの速さで現在の角度をrotationの角度に変化させます。
補間回転スクリプトをキャラクターに取り付ける
スクリプトが出来たので回転させるキャラクターを設置します。
上のような感じでEthanを量産体制に・・・・。
rotateSpeedは45にしました。
↑はさきほどと同じ絵のように見えますが違います・・・・この画像はいらなかったかも・・・。
実行する前にさきほどMainCameraに取りつけたFollowCameraを無効化しておいてください。
補間回転を確認する
それでは回転も確認してみましょう。
↑のようにNormalはグルグル回ってますね・・・、そりゃUpdate毎に40度ずつ回転しているから当たり前ですが・・・。
なんでTime.deltaTimeをかけてないんだろう・・・謎
んーMoveスクリプトの時より解り辛い結果ですね・・・・。
サンプルを間違えたかも・・・・(^_^;)
補間を検証してみて
今回は補間をわかりやすくしようと思って記事を書いてみましたが、違いがわかり辛いですね。(._.)
キャラクターを使った実践的な補間で、違いがわかりやすいサンプルを思いつかなかったのです。
とりあえず滑らかにキャラクターを動かしたい時は補間を使ってみて、思ってたのと違う時は補間のやり方を変えてみるというのもいいかもしれませんね。
まずはLerpを使った補間を使うだけでいいと思います。
細かいこだわりを求めていくなら他のメソッドを使っていけばいいと思います。