UnityのMoveTo

cocos2d-xならrunAction(MoveTo(2, Vec2(50, 0)))のような感じで簡単にスプライトを動かすことができますよね。

Unity使い始めたら、オブジェクトを動かすのに、きっとcocos2d-xのような組み込みのメソッドあると思い、いろいろと探してみました。
結局そのようなもの見つかっておらず、基本的にはUpdate()におけるフレームごとの操作による演出のようです。

いくつサンプルみた中、2Dチュートリアルアプリ「Roguelike」スプライト動かすスクリプトが一番分かりやすかったと思います。

その中のMoveObjectを参考にして以下の点を変更しました。

  1. UI.Imageにも使えるようにlocal positionが変動するように変更
  2. 決まった時間で移動ではなく、決まったスピードで移動するように変更
  3. 移動終了後のコールバックを追加

そして、こうなります。

using System.Collections;
using UnityEngine;

public class MoveObject : MonoBehaviour
{

    public delegate void MoveObjectDelegate();
    protected MoveObjectDelegate callback;

    public float speed = 500f;

    public virtual void move(Vector3 targetPosition)
    {
        StartCoroutine(SmoothMovement(targetPosition));
    }

    public virtual void move(Vector3 targetPosition, MoveObjectDelegate moveObjectDelegate)
    {
        callback = moveObjectDelegate;
        StartCoroutine(SmoothMovement(targetPosition));
    }

    protected IEnumerator SmoothMovement(Vector3 targetPosition)
    {
        //Calculate the remaining distance to move based on the square magnitude of the difference between current position and end parameter. 
        //Square magnitude is used instead of magnitude because it's computationally cheaper.
        float sqrRemainingDistance = (transform.localPosition - targetPosition).sqrMagnitude;

        //While that distance is greater than a very small amount (Epsilon, almost zero):
        while (sqrRemainingDistance > float.Epsilon)
        {
            //Find a new position proportionally closer to the end, based on the moveTime
            Vector3 newPostion = Vector3.MoveTowards(transform.localPosition, targetPosition, speed * Time.deltaTime);

            //Call MovePosition on attached Rigidbody2D and move it to the calculated position.
            transform.localPosition = newPostion;
            //rb2D.MovePosition(newPostion);

            //Recalculate the remaining distance after moving.
            sqrRemainingDistance = (transform.localPosition - targetPosition).sqrMagnitude;

            //Return and loop until sqrRemainingDistance is close enough to zero to end the function
            yield return null;
        }
        OnMoveDone();
    }

    protected virtual void OnMoveDone()
    {
        if (callback != null)
            callback();
    }
}

そしたら、obj.move(GetPositionOfTarget(), OnArriced);というように使えます。

Unityでコールバックの実装

Unity使い始めて各種処理はフレームベースのようで、cocos2d-xのようなコールバックが恋しくてたまりませんでした。
コンポーネントごとにスクリプトを付けて単独に動くパーツを作るやり方が望ましいと思いますが、それでもあっちこっちにコールバック使えたらなぁ〜runAction的なものがあればなぁ〜なんて思います。

そこでC#でdelegateのやり方でcallbackを実装するとこうなるかと思います。

    public delegate void MyDelegate(GameObject obj);
    protected MyDelegate callback;

    public void SetMyDelegate(MyDelegate myDelegate)
    {
        callback = myDelegate;
    }

    private void OnCallback()
    {
        if (callback != null)
            callback(gameObject);
    }

この場合、スクリプト自分自身のオブジェクトを送り返してます。

参考:

  1. How to make a callback function
  2. Using Delegates (C# Programming Guide)

Unityタッチイベントのスルー

タッチイベントを無視して下層にあるオブジェクトに拾ってほしい時、cocos2d-xと言えば、onTouchBeganの中でreturn false;でやればいいし、Cocos Studio 2の場合は「Properties」⇒「General」⇒「Touchable」のチェックボックスを外すことで実現できますが、Unityはどうなんでしょう?!

Canvasのボタンを配置し、その上にUIのPanelを配置すると、ボタンが押せなくなります。
その場合はPanelに「Canvas Group」コンポーネントを追加し、

その中の「Interactable」と「Blocks Raycasts」のチェックを外せば良いです。

参考:

  1. How to make UI ignore click