Help us understand the problem. What is going on with this article?

Unityでオブジェクトをドラッグしながらスクロールする

More than 1 year has passed since last update.

はじめに

Unityでオブジェクトをドラッグしながら画面スクロールしたいので、サンプルを作りました。
uGUIベースなのでスマホでも動くはずです。

今回のコードを下記に置きました。
https://github.com/jnhtt/CreatePathWithScroll/tree/master/Assets/CreatePathWithScroll

こんな感じです。
スクロール開始をドラッグ中のマウス位置が画面端に来た時にしました。
CreatePathWithScroll.gif

前提

以前に書いた下記の記事にある処理を組み合わせつつ、変更を加えたものをベースにコードを書きます。
- UnityでListにした座標を辿る
- Unityで見下ろしカメラをドラッグで動かす

概要

ObjectレイヤーのGameObjectをドラッグするか、ObjectレイヤーのGameObjectがない所をドラッグするか処理が分かれます。
ObjectレイヤーのGameObjectがない所をDragする場合は、Unityで見下ろしカメラをドラッグで動かすを使います。
ObjectレイヤーのGameObjectをドラッグする場合は、UnityでListにした座標を辿るを使います。
そのため、スクロール関係の処理がメインになります。

ドラッグ処理の分岐

ObjectレイヤーのGameObjectをドラッグする時はMoverOperationを使い、
それ以外をドラッグする場合はLookingDownCameraOperationを使います。
どちらを使うかは、OnPointerDownで決めます。

ControllerPanel.OnPointerDown
    public void OnPointerDown(PointerEventData pointerEventData)
    {
        inputOperation = CreateInputOperation(pointerEventData.position);
        if (inputOperation != null) {
            inputData.screenPosition = pointerEventData.position;
            inputOperation.OnPointerDown(inputData);
        }
    }

CreateInputOperationの中身はこんな感じです。

ControllerPanel.CreateInputOperation
    private IInputOperation CreateInputOperation(Vector2 screenPos)
    {
        int objectLayer = 1 << LayerMask.NameToLayer("Object");
        RaycastHit hit;
        if (Utils.TryGetHitInfo(screenPos, objectLayer, out hit))
        {
            Mover mover = hit.collider.transform.GetComponent<Mover>();
            if (mover == null) {
                mover = hit.collider.gameObject.AddComponent<Mover>();
            }
            return new MoverOperation(mover);
        }
        return new LookingDownCameraOperation();
    }

ObjectレイヤーのGameObjectをドラッグ

画面端の時にカメラを動かす

Screen座標からViewport座標を得ます。
Viewport座標は画面左下が(0, 0)、右上(1, 1)です。
ここでは、Viewport座標で0.1幅の枠を作って画面端の判定を行います。

画面 端の範囲
左端 [0, 0.1)
右端 (0.9, 1]
上端 (0.9, 1]
下端 [0, 0.1)

端に近いと早くスクロールします。

MoverOperation.MoveCameraInScreenEdge
    private void MoveCameraInScreenEdge(Vector2 screenPos)
    {
        var viewportPos = CameraManager.Instance.Camera.ScreenToViewportPoint(screenPos);
        viewportPos.z = 0f;

        Vector3 move = Vector3.zero;
        if (viewportPos.x < VIEWPORT_EDGE_LEFT) {
            move.x = viewportPos.x - VIEWPORT_EDGE_LEFT;
        } else if (viewportPos.x > VIEWPORT_EDGE_RIGHT) {
            move.x = viewportPos.x - VIEWPORT_EDGE_RIGHT;
        }
        if (viewportPos.y < VIEWPORT_EDGE_DOWN) {
            move.z = viewportPos.y - VIEWPORT_EDGE_DOWN;
        } else if (viewportPos.y > VIEWPORT_EDGE_UP) {
            move.z = viewportPos.y - VIEWPORT_EDGE_UP;
        }

        CameraManager.Instance.Move(move * SCROLL_SPEED);
    }

スクロール中のパス生成

マウス位置を動かさない場合のスクロール、OnDragイベントが飛んでこないのでUpdate関数を追加して対応しました。
これでいいのか?分からないです。。。

ControllerPanel.Update
    private void Update()
    {
        if (inputOperation != null) {
            inputOperation.Update(inputData, Time.deltaTime);
        }
    }

IInputOperationBaseInputOperationUpdate(InputData, float)を追加します。
そしてMoverOperation.Updateを実装します。
パスの点を追加する間隔を確認しつつ、点を追加します。

MoverOperation.Update
    public override void Update(InputData inputData, float deltaTime)
    {
        base.Update(inputData, deltaTime);
        if (dragFlag) {
            MoveCameraInScreenEdge(inputData.screenPosition);
            if (!addPathPointByEventFlag && addPathPointTimer > ADD_PATH_POINT_INTERVAL) {
                RaycastHit hit;
                if (Utils.TryGetHitInfo(inputData.screenPosition, 1 << LayerMask.NameToLayer("Ground"), out hit)) {
                    AddPathPoint(hit.point + UP_OFFSET);
                }
                addPathPointTimer = 0f;
            }
            addPathPointByEventFlag = false;
        }
        addPathPointTimer += deltaTime;
    }

さいごに

オブジェクトをドラッグしながら画面スクロールするようになったと思います。
パスを引いてる感が出ていると良いですが、どうでしょう。
スクロールスピードと画面端の領域は好みで調整する必要がありそうです。特にカメラの高さを考慮する必要がある気がします。

RTSっぽいものが作れそうになってきたと思います。
このサンプルにBehaviourTreeを入れるとそれっぽくできるんじゃないかと。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした