OGP

前回までで 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
model-rigging.png model-rigging-result.png

Fig. Rigging の結果

あとは Kyle の Animator コンポーネントに Basic Motions Pack に含まれる適当な Animator Controller を設定すれば Kyle をアニメーションさせることができる.

walking-animation.gif

Fig. 歩くアニメーション

アニメーションの状態を遷移させる

せっかくなので Animator Controller を作って状態遷移を定義し,Basic Motions Pack に含まれるいくつかのアニメーションを使えるようにしてみる(追記:でも歩くアニメーション以外は結局使わなかったという).

ここでは,IdleWalkingRunningRunning Backward の4つの状態を定義する.

walking-state.png

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);
    }
}

walking-state-test.gif

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 が地面にめりこんでしまうはずだが,今回は平面しか対象にしないので気にしないこととする.

walking-test.gif

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 を切り替えられるボタンを適当に作って,色々と置いてみる.

walking-on-ar-plane.gif

Fig. ARPlane の上を歩く

という感じで ARPlane の上を歩けるようになった.

まとめ

AR シーンの中で検出した平面の上でキャラクターを歩かせてみた.

  • キャラクターは人型のアセット(具体的には Space Robot Kyle)を使用した
  • 人型のアセットに歩くアニメーションを設定した(具体的には Basic Motions FREE Pack
  • Animator を組んで,状態遷移できるようにした
  • ARPlane から取得した MeshFilter の上を歩くようにした
  • MeshFilter からランダムに頂点(vertices)を取得して,それを目的地として歩くようにした

Unity というゲームエンジンや Asset Store というエコシステムを使っているだけなので,技術的にどうこうということはなかった.

というか,いまさらだが Terrain の上を歩かせる方法とか普通にある気がする(が気にしないこととする).

:dancer:

References