【Unity】オブジェクトの移動メソッドを基底クラスに持たせる

Unity

2DでUnityのゲームを作成しています。

ゲーム作成において初期に躓いたのがオブジェクトの移動に関することでした。

というのもゲームオブジェクトをシーンに出現させた後は、基本的にユーザーが認識できる速度で移動をさせてあげる必要があるので、単純にtransform.positionを変更するのはあまり使わない。

↓下記ソースだと瞬間移動してしまう

Vector3 toPos = new Vector3(100, 100, 0);
transform.position = toPos;

まあ、中間点を移動するような記述が一切ないので当たり前ですね。どうするのがベターなのか考えてみたので、詳細を記載してみたいと思います。

Rigidbodyを使用してオブジェクトを移動させる

Rigidbodyのメソッドを使用して移動をさせるパターンです。自分は2Dでゲームを作っているのでRigidbody2Dを使います。

↓とりあえずコンポーネント追加して、自由に動かしたいのでGravityScaleは0

UnityのRgidbodyのコンポーネント追加方法

よくある移動ロジックだと、速度と移動角度を与えてあげるというのがあります。

using UnityEngine;

public class Test : MonoBehaviour
{
    void Start()
    {
        move(0, 100f, 0 , 10000f); // X軸の角度, Y軸の角度, z軸の角度, 移動速度
    }

    void move(float x, float y, float z , float speed)
    {
        transform.eulerAngles = new Vector3(0, 100, 0);
        GetComponent<Rigidbody2D>().AddForce(transform.forward * speed * Time.deltaTime);
    }
}

上記で角度と速度を調整すれば、取りあえずアニメーションで動くようにはなるかと思います。(上記ソースなら、そもそもUpdateでよくねって気もしますが)

終点座標が決まっている場合もあるかと思うので、その場合はMovePositionをコルーチンで使うのがベターですかね。AddForceでも、もちろんいい気もしますがわかりにくい。


using System.Collections;
using UnityEngine;

public class Test : MonoBehaviour
{
    void Start()
    {
        StartCoroutine(move(0f, 1f, 0f, 100));
    }

    private IEnumerator move(float x, float y, float z, float endTime)
    {
        Vector3 startPosition = transform.position;
        Vector3 position = new Vector3(x, y, z) - startPosition;
        float currentTime = 0;

        while (currentTime < endTime)
        {
            currentTime += Time.deltaTime;
            GetComponent<Rigidbody2D>().MovePosition(startPosition + (position * (currentTime / endTime)));
            yield return new WaitForSeconds(0.01f);
        }
    }
}

こんな感じですかね。もしかしたら速度とか制御しないと止まらないかも…。

あとは…後述する方法に共通するのですが、コルーチンで回している場合、途中でオブジェクトをぶっ壊されると困る。

個人的にRigidbody2Dを使う要件(障害物当たり判定とか)はあまりないです。それであれば次に書く単純にコルーチンでtransform.positionで移動させるのが良いと思いました。

単純にコルーチンでtransform.positionを変更する

前に書いたロジックとほぼ同じですが、こちらはRigidbody2Dを使用しない形です。


using System.Collections;
using UnityEngine;

public class Test : MonoBehaviour
{
    void Start()
    {
        StartCoroutine(move(0f, 1f, 0f, 5f));
    }

    private IEnumerator move(float x, float y, float z, float endTime)
    {
        Vector3 startPosition = transform.position;
        Vector3 position = new Vector3(x, y, z) - startPosition;
        float currentTime = 0;

        while (currentTime < endTime)
        {
            currentTime += Time.deltaTime;
            transform.position = startPosition + (position * (currentTime / endTime));
            yield return new WaitForSeconds(0.01f);
        }
    }
}

個人的にはこれを採用しています。正直、見えるオブジェクトは移動させたいことがほとんどなので、オブジェクトの基底クラスとして採用することにしました。

ついでにコルーチンじゃなくて、移動完了を待ちたいときもあるので、そのバージョンも作っておきます。(async/await)

↓基底クラス

using System;
using System.Collections;
using UnityEngine;
using System.Threading.Tasks;

public class Test : MonoBehaviour
{

    public IEnumerator move(float x, float y, float z, float endTime)
    {
        Vector3 startPosition = transform.position;
        Vector3 position = new Vector3(x, y, z) - startPosition;
        float currentTime = 0;

        while (currentTime < endTime)
        {
            currentTime += Time.deltaTime;
            transform.position = startPosition + (position * (currentTime / endTime));
            yield return new WaitForSeconds(0.01f);
        }
    }

    public async Task moveAwait(float x, float y, float z, float endTime)
    {
        Vector3 startPosition = transform.position;
        Vector3 position = new Vector3(x, y, z) - startPosition;
        float currentTime = 0;

        while (currentTime < endTime)
        {
            currentTime += Time.deltaTime;
            transform.position = startPosition + (position * (currentTime / endTime));
            await Task.Delay(TimeSpan.FromSeconds(0.01f));
        }
    }
}

↓下位クラス

public class Test2 : Test
{
    void Start()
    {
        StartCoroutine(move(0f, 1f, 0f, 5f));
    }
}

StartCoroutineとか書くのも面倒なので、それも基底クラスの方に持たせてメソッドが呼び出されたらオブジェクトが移動するようにするだけの方がスッキリするかも。

DOTweenを利用してオブジェクトをアニメーション移動させる

オブジェクトをアニメーションで移動させたい時といえば、「DOTween」が便利なので、そちらも記載しておきたいと思います。

DOTweenは無料版も有り、無料版で十分使えるのでおすすめです。

まずはアセットストアから無料版を導入します。

アセットストアからDoTweenを入手する方法

インポートは全てのオブジェクトをインポートします。

DoTweenをインポートする方法

SetUp画面で「Setup DOTween」ボタンを押します。

DoTweenをセットアップする方法

DOTweenを使用するには「using DG.Tweening;」というインポート分を追加すればOKです。非常に簡単にオブジェクトをアニメーション移動することが出来ます。

using DG.Tweening;
using UnityEngine;

public class Test2 : MonoBehaviour
{
    void Start()
    {
        move(0, 100f, 0 , 5f); // X軸の終点, Y軸の終点, z軸の終点, 移動時間
    }

    void move(float x, float y, float z , float endTime)
    {
        Vector3 position = new Vector3(1f, 0, 0);
        Tween tw = transform.DOMove(position, endTime);
        tw.Play();
    }
}

お手軽さは間違いなくNo1ですね。

色々とオプションがあるので、それらを使えばほとんどの動作は実現できそうな気がします。

Tweenは開放しなくても大丈夫なのか気になるけど、とりあえず無しで良いっぽい。

async/awaitを使用する場合は、tween.IsPlaying()というメソッドで実行中かどうかを取れるので、これを使用して実行中の場合は時間を止めれば良い。

using DG.Tweening;
using System;
using UnityEngine;
using System.Threading.Tasks;

public class Test2 : MonoBehaviour
{
    async void Start()
    {
        await move(0, 100f, 0 , 5f); // X軸の終点, Y軸の終点, z軸の終点, 移動時間
    }

    async Task move(float x, float y, float z , float endTime)
    {
        Vector3 position = new Vector3(1f, 0, 0);
        Tween tw = transform.DOMove(position, endTime);
        tw.Play();

        while (tw.IsPlaying())
        {
            await Task.Delay(TimeSpan.FromSeconds(0.01f));
        }
    }
}

コメント

タイトルとURLをコピーしました