【Unity2D】ゲームオブジェクトを高速でドラッグ&ドロップしても指に追従させる方法

どうも、だらはです。
今回は、ゲームオブジェクトを高速でドラッグ&ドロップしても指に追従させる方法を紹介したいと思います。本記事は、以前書いたスクリプトの改良版となります。

◆以前書いたスクリプト

スポンサーリンク

考え方とスクリプト

以前書いたスクリプトは、RaycastHit2Dで検出したゲームオブジェクトの座標をUpdate()にて毎タスク更新する方法をとっていました。

今回は、RaycastHit2Dで検出したゲームオブジェクトをListに追加し、Listに追加されたゲームオブジェクトの座標Update()にて毎タスク更新する方法をとっています。

◆スクリプト

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class PlayerCtrl : MonoBehaviour
{
    Camera mainCamera;
    GameObject selectedSprite;

    List<GameObject> objSelect0 = new List<GameObject>();
    List<GameObject> objSelect1 = new List<GameObject>();
    List<GameObject> objSelect2 = new List<GameObject>();
    List<GameObject> objSelect3 = new List<GameObject>();
    List<GameObject> objSelect4 = new List<GameObject>();

    void Start()
    {
        mainCamera = Camera.main;
    }

    Vector3 pos;

    void Update()
    {
        int type = 2;

        //従来手法(全オブジェクト取得)
        if (type == 1)
        {
            if (Input.touchCount < 1)
            {
                return;
            }

            foreach (var touch in Input.touches)
            {
                Ray ray = Camera.main.ScreenPointToRay(touch.position);
                //RaycastHit2D hit = Physics2D.Raycast((Vector2)ray.origin, (Vector2)ray.direction);

                foreach (RaycastHit2D hit in Physics2D.RaycastAll((Vector2)ray.origin, (Vector2)ray.direction))
                {
                    if (hit.collider != null)
                    {
                        SendEventStateObj(hit.collider.gameObject, 1);

                        Vector3 screenPoint = Camera.main.WorldToScreenPoint(transform.position);
                        float posX = touch.position.x + Random.Range(-5, 5);
                        float posY = touch.position.y + Random.Range(-5, 5);
                        float posZ = screenPoint.z;
                        pos = new Vector3(posX, posY, posZ);
                        hit.collider.gameObject.transform.position = Camera.main.ScreenToWorldPoint(pos);
                    }

                    if (touch.phase == TouchPhase.Ended)
                    {
                        SendEventStateObj(hit.collider.gameObject, 2);
                    }
                }
            }
        }
        else
        {
            if (Input.touchCount < 1)
            {
                return;
            }

            foreach (var touch in Input.touches)
            {
                //タッチ座標を得る
                Ray ray = Camera.main.ScreenPointToRay(touch.position);

                //同時5タッチまで検出可能
                List objSelect = null;

                switch (touch.fingerId)
                {
                    case 0: objSelect = objSelect0; break;
                    case 1: objSelect = objSelect1; break;
                    case 2: objSelect = objSelect2; break;
                    case 3: objSelect = objSelect3; break;
                    case 4: objSelect = objSelect4; break;
                }

                if (objSelect != null)
                {
                    //Ray発射
                    foreach (RaycastHit2D hit in Physics2D.RaycastAll((Vector2)ray.origin, (Vector2)ray.direction))
                    {
                        //初めての敵オブジェクトを検出
                        if (hit.collider != null && !objSelect.Contains(hit.collider.gameObject))
                        {
                            //オブジェクトに指示(追従中)
                            SendEventStateObj(hit.collider.gameObject, 1);
                            //追従させる敵キャラをリストに追加
                            objSelect.Add(hit.collider.gameObject);
                        }
                    }

                    //タッチを解除
                    if (touch.phase == TouchPhase.Ended)
                    {
                        foreach (GameObject obj in objSelect)
                        {
                            //オブジェクトに指示(追従解除)
                            SendEventStateObj(obj, 2);
                        }
                        //追従させる敵キャラをリストをクリア
                        objSelect.Clear();
                    }
                }

                //スワイプ座標を取得
                Vector3 screenPoint = Camera.main.WorldToScreenPoint(transform.position);
                float posX = touch.position.x + Random.Range(-5, 5);
                float posY = touch.position.y + Random.Range(-5, 5);
                float posZ = screenPoint.z;
                Vector3 pos = new Vector3(posX, posY, posZ);

                //敵キャラをスワイプ座標へ移動
                if (objSelect != null && objSelect.Count != 0)
                {
                    foreach (GameObject obj in objSelect)
                    {
                        obj.transform.position = Camera.main.ScreenToWorldPoint(pos);
                    }
                }
            }
        }
    }

    //対象のオブジェクトにイベントを通知
    void SendEventStateObj(GameObject targetObj, int stateObj)
    {
        ExecuteEvents.Execute<IEventCaller>(
                        target: targetObj,
                        eventData: null,
                        functor: (reciever, eventData) => reciever.ReceiveEventStateObj(stateObj)
                        );
    }
}

補足①

今回はRayに当たった全てのゲームオブジェクトを検出できるように修正しました。(以前のは1つのオブジェクトのみ検出していた)

foreach (RaycastHit2D hit in Physics2D.RaycastAll((Vector2)ray.origin, (Vector2)ray.direction))

補足②

今回は、同時に5タッチまで検出できる仕様としています。

    List<GameObject> objSelect0 = new List<GameObject>();
    List<GameObject> objSelect1 = new List<GameObject>();
    List<GameObject> objSelect2 = new List<GameObject>();
    List<GameObject> objSelect3 = new List<GameObject>();
    List<GameObject> objSelect4 = new List<GameObject>();

補足③

今回は、他オブジェクトの変数を書き換える方法としてイベントを利用しています。
こちらの記事の下の方に概要を記載しましたのでご参照頂けたらと思います。
【Unity】シーン遷移時のOnDestroy処理でエラーとなる現象の対策

補足④

Input.touchesは、タッチを検出することができますが、複数タッチすると、取得される配列のインデックスがころころ変わるため指の識別ができません。
なので、今回はTouch.fingerIdを使っています。

まとめ

いかがでしたでしょうか。
高速スワイプに対応する手段として、今回はListを使った方法を紹介しました。
本件とは関係ないのですが、ブログ記事として記事をまとめるときに、htmlの仕様で<>で囲まれた部分が削除されてしまうことが分かりました。
何とか手動で修正していますが、漏れてしまった部分はエラーとなってしまうためコメントで連絡いただけると助かります。。

以上、だらはでした。

スポンサーリンク

応用

Posted by daraha_gm