ゴマちゃんフロンティア

アザラシが大好きなエンジニアの開発日記です

【開発日記】「一定距離を保って追従するオブジェクト」の作成

time 2018/07/28

というわけで、今回はタイトル通り「一定距離を保って追従する」オブジェクトについて考えてみます。

ゲーム風にやりたいことを整理すると以下のような感じです。
・オブジェクトは特定のキャラクターに追従して移動する
・キャラクターの周りの一定範囲まで近寄ったら停止する
・停止中のオブジェクトが追従を再開する場合、キャラクターの移動から少し遅らせて開始する

一般的なゲームでもそれなりに見かける挙動ですが、今回自作ゲームにこの挙動を組み込みたくなったので、どのような感じで実装したかご紹介します。

追従用のコライダーの設定

まずは追従するオブジェクトに判定用のコライダー系コンポーネントを設定します。形状や大きさやお好みで構いませんが、ここではSphereColliderを設定しました。

追従される側のキャラクターにも何かしらのコライダーを設定し、判定が拾えるようにしておきましょう。

追従するオブジェクトを制御するスクリプトの作成

スクリプトを作成する前に、現在の移動に関する状態を判別できるように列挙型を定義しておきます。状態管理のコーディングがしやすくなる他、今後「自発的に移動する」「プレイヤー操作で移動させる」のようなケースが増えた場合に対応が楽になります。

public enum MoveMode
{
    Idle = 1,
    Follow = 2
}

次に追従するオブジェクトを制御するためのスクリプトを作成します。後で作成する追従用コライダーのスクリプトから呼び出される側の関数を作り、Update()に移動処理を書きます。
自作ゲームで使用するため、サンプルではDefenceTargetという名前のクラスに実装しています。

public class DefenceTarget
{
    public float move_speed = 3f;

    protected Rigidbody rb;
    protected Transform followTarget;
    protected MoveMode currentMoveMode;

    void Start()
    {
        rb = this.GetComponent<Rigidbody>();
        currentMoveMode = MoveMode.Idle;
    }

    void Update()
    {
        DoAutoMovement();
    }

    protected void DoAutoMovement()
    {
        switch (currentMoveMode) {
            case MoveMode.Wait:
                break;
            case MoveMode.Follow:
                if (followTarget != null) {
                    Quaternion move_rotation = Quaternion.LookRotation(followTarget.transform.position - transform.position, Vector3.up);
                    transform.rotation = Quaternion.Lerp(transform.rotation, move_rotation, 0.1f);
                    rb.velocity = transform.forward * move_speed;
                }

                break;
        }
    }

    public void OnEnterFollowTarget()
    {
        followTarget = null;

        if (currentMoveMode == MoveMode.Follow) {
            currentMoveMode = MoveMode.Idle;
        }
    }

    public void OnExitFollowTarget(Transform target)
    {
        followTarget = target;

        if (currentMoveMode == MoveMode.Idle) {
            currentMoveMode = MoveMode.Follow;
        }
    }
}

Update()内から呼び出しているDoAutoMovement()内で移動処理を記述しています。
Transform.LookAt()を使うと一瞬で振り向いてしまうので、Quaternion.LookRotation()Quaternion.Lerp()を組み合わせて徐々に振り向くようにしています。
追従対象の設定と移動モード切替はOnEnterFollowTarget()OnExitFollowTarget()で行います。後述する追従用コライダーのスクリプトから呼び出します。

追従用コライダーのスクリプト作成

次に追従用コライダーに設定するスクリプトを作成します。

using UnityEngine;

public class FollowCollider : MonoBehaviour
{
    protected DefenceTarget defenceTarget;

    void Start()
    {
        defenceTarget = transform.GetComponentInParent<DefenceTarget>();
    }

    protected void OnTriggerEnter(Collider c)
    {
        if (c.gameObject.tag == "Player") {
            defenceTarget.OnEnterFollowTarget();
        }
    }

    protected void OnTriggerExit(Collider c)
    {
        if (c.gameObject.tag == "Player") {
            defenceTarget.OnExitFollowTarget(c.transform);
        }
    }
}

最初に先ほどの追従用オブジェクトの参照を取得します。先程のクラスの関係でDefenceTarget型を指定していますが、実際に使う際は適切な型に書き換えてください。
OnTriggerEnter()OnTriggerExit()で特定のオブジェクト (ここでは「Player」というタグで判定) の判定を検知し、先ほどの追従オブジェクト用スクリプトで定義した関数を呼び出します。
コライダーベースで移動モードの切替を行うことで「一定範囲内にいる時は止まる」が楽に実現できる上、離れた際の「少し遅らせて追従させる」という挙動も自然と組み込むことができます。

ここまでを実際のゲームに組み込んで動作させると以下のような感じになります。

いい感じにアザラシが追ってきてくれます。挙動は問題なさそうですね。

down

コメントする