Unity AR - 平面の上でキャラクターを歩かせる
前回までで AR シーンの中で平面検出ができるようになったので,今回はその平面の上でキャラクターを歩かせてみる. 歩くロジックはとても単純かつ雑なので,そんなに参考にはならないかもしれない.
概要
- 人型のモデルに Animator を設定して歩くアニメーションを再生する
- ARPlane の FilterMesh から頂点を取得して,そこに向かって並行移動させる
環境
- Unity 2020.1.17f1
- AR Foundation 4.1.1
- ARKit XR Plugin 4.1.1
- iOS 14.3
アニメーションを再生する
まずは通常のシーンのなかでキャラクターにアニメーションを設定して再生する. モデルは出来合いのものを使っていく. 今回は Unity 公式の Space Robot Kyle を使う.
シーンに配置してスケールを調整しておく.
机の上を動き回る小さなロボにしたいので,XYZ それぞれ調整して Transform でネストしておく.
ルートとなる Transform が (x,y,z) = (0,0,0)
のときにモデルが接地しているように気を付ける.
Kyle に含まれるのは 3D モデルとテクスチャのみで,アニメーションは設定されていないので,自前で設定していく. といっても Animation Clip から作っていると終わらないので,Asset Store から良さそうなものを探して使っていく. 今回は Basic Motions FREE Pack を使わせてもらう.
Kyle のモデルはデフォルトでは Rig が Legacy
になっているので,これを Humanoid
に変更する.
Apply を実行すればモデルを元に自動で rigging され,Animator に対応した Avatar が作られる.
Animation Type | Rigging Result |
---|---|
Fig. Rigging の結果
あとは Kyle の Animator コンポーネントに Basic Motions Pack に含まれる適当な Animator Controller を設定すれば Kyle をアニメーションさせることができる.
Fig. 歩くアニメーション
アニメーションの状態を遷移させる
せっかくなので Animator Controller を作って状態遷移を定義し,Basic Motions Pack に含まれるいくつかのアニメーションを使えるようにしてみる(追記:でも歩くアニメーション以外は結局使わなかったという).
ここでは,Idle
,Walking
,Running
,Running Backward
の4つの状態を定義する.
Fig. Animator (ステートマシン)
Parameters には velocity
を追加して,状態遷移の条件(Conditions)に使用する.
適当にスクリプトを書いて Inspector から velocity
を手動で変更できるようにしてシーンを実行してみる.
using UnityEngine;
[RequireComponent(typeof(Animator))]
public class Walker : MonoBehaviour
{
private static readonly int AnimatorVelocity = Animator.StringToHash("velocity");
[SerializeField] private Animator animator;
[SerializeField] private int velocity;
private void Update()
{
animator.SetInteger(AnimatorVelocity, velocity);
}
}
Fig. アニメーションの切り替え
キャラクターの位置を動かす
歩くというアニメーションは Animator で再生できるようになったので,そのアニメーションに合わせてそれらしい速度で平行移動させる. 平面(Plane)のメッシュを与えて,その中の頂点(Vertex)をひとつランダムで選び,そこに向かって移動させる.
Mesh の各頂点は vertices
で取得できる.
頂点の座標は Mesh に対するローカル座標なので, TransformPoint()
でワールド座標に変換する.
目的地の座標が取得できたら,あとは Kyle が目的地を向くように回転させて並行移動させる.
同時にアニメーションを再生して,歩いて移動しているっぽく見せる.
using UnityEngine;
[RequireComponent(typeof(Animator))]
public class Walker : MonoBehaviour
{
private static readonly int AnimatorVelocity = Animator.StringToHash("velocity");
[SerializeField] private Animator animator;
[SerializeField] private int velocity;
[SerializeField] private float walkingSpeed = 0.18f;
[SerializeField] private float runningSpeed = 0.54f;
[SerializeField] private MeshFilter ground;
private Vector3 _destination = Vector3.zero;
private void Update()
{
animator.SetInteger(AnimatorVelocity, velocity);
var distance = Vector3.Distance(transform.position, _destination);
if (distance <= 0.001f)
{
if (ground == null) return;
var vertices = ground.sharedMesh.vertices;
if (vertices.Length == 0) return;
var destination = vertices[Random.Range(0, vertices.Length)];
_destination = ground.transform.TransformPoint(destination);
return;
}
var transformVelocity = 0.0f;
if (velocity > 1) transformVelocity = runningSpeed;
else if (velocity > 0) transformVelocity = walkingSpeed;
var transformLerp = (Time.deltaTime * transformVelocity) / distance;
transform.position = Vector3.Lerp(transform.position, _destination, transformLerp);
transform.LookAt(_destination, ground.transform.up);
}
}
適当なスクリプトなので,変数 ground
として MeshFilter 型を取るものの,平面じゃない MeshFilter を与えられたら Kyle が地面にめりこんでしまうはずだが,今回は平面しか対象にしないので気にしないこととする.
Fig. Plane の上を歩くテスト
AR シーンの中で動かす
前回は検出した平面の上に樹木のモデルを置いていたが,同様に Kyle を置いてみる.
if (raycastManager.Raycast(touch.position, _raycastHits, TrackableType.PlaneWithinPolygon))
{
var hit = _raycastHits.First();
if (hit.trackable is ARPlane)
{
var instance = Instantiate<GameObject>(arObjectPrefab, hit.pose.position, hit.pose.rotation);
var walker = instance.GetComponent<Walker>();
var ground = hit.trackable.GetComponent<MeshFilter>();
if (walker != null && ground != null)
{
walker.SetGround(ground);
walker.SetWalking(true);
}
}
}
置くものは結局のところ GameObject なので Spawn()
する arObjectPrefab
を Kyle に差し替えるだけ.
ARPlane には MeshFilter
が付いているはずなので,GetComponent()
して地面として設定する.
ついでに prefab を切り替えられるボタンを適当に作って,色々と置いてみる.
Fig. ARPlane の上を歩く
という感じで ARPlane の上を歩けるようになった.
まとめ
AR シーンの中で検出した平面の上でキャラクターを歩かせてみた.
- キャラクターは人型のアセット(具体的には Space Robot Kyle)を使用した
- 人型のアセットに歩くアニメーションを設定した(具体的には Basic Motions FREE Pack)
- Animator を組んで,状態遷移できるようにした
- ARPlane から取得した MeshFilter の上を歩くようにした
- MeshFilter からランダムに頂点(vertices)を取得して,それを目的地として歩くようにした
Unity というゲームエンジンや Asset Store というエコシステムを使っているだけなので,技術的にどうこうということはなかった.
というか,いまさらだが Terrain の上を歩かせる方法とか普通にある気がする(が気にしないこととする).