6

私は Unity を初めて使用し、タッチ入力を使用してマップ/地形上でカメラを移動する方法を理解しようとしています。カメラは (90,0,0) の回転で地形を見下ろします。地形はレイヤー 8 にあります。キーボードで移動するのに問題はありませんでしたが、今はタッチして移動しようとしています。iOS で予想される使用法を維持したい場合は、非常に異なります。

組み込みの iOS アプリで考えられる最良の例は、ユーザーが画面に触れ、指が画面上にある限り、マップ上のそのポイントが指の下にとどまるマップです。そのため、ユーザーが指を動かすと、マップが指で動いているように見えます。この方法でそれを行う方法を示す例を見つけることができませんでした。マウスを使ってカメラやキャラクターを動かす例をたくさん見てきましたが、このスタイルにうまく変換できないようです。

Unity3D Answers にも投稿されています。

http://answers.unity3d.com/questions/283159/move-camera-over-terrain-using-touch-input.html

4

5 に答える 5

18

以下は、必要なものです。遠近法カメラを使用する場合、指/カーソルと地形を 1 対 1 で対応させるのは難しいことに注意してください。カメラを正射投影に変更すると、以下のスクリプトによって、指/カーソルの位置とマップの動きの間の完全なマップが得られるはずです。遠近法を使用すると、わずかなオフセットに気付くでしょう。

レイ トレーシングを使用してこれを行うこともできますが、そのルートは雑で直感的ではないことがわかりました。

テスト用のカメラ設定 (値はインスペクターから取得されるため、そこに適用します):

  1. 位置: 0,20,0
  2. 方向: 90,0,0
  3. 投影法: 遠近法/正投影

using UnityEngine;
using System.Collections;



public class ViewDrag : MonoBehaviour {
    Vector3 hit_position = Vector3.zero;
    Vector3 current_position = Vector3.zero;
    Vector3 camera_position = Vector3.zero;
    float z = 0.0f;
    
    // Use this for initialization
    void Start () {
        
    }
    
    void Update(){
        if(Input.GetMouseButtonDown(0)){
            hit_position = Input.mousePosition;
            camera_position = transform.position;
            
        }
        if(Input.GetMouseButton(0)){
            current_position = Input.mousePosition;
            LeftMouseDrag();        
        }
    }
    
    void LeftMouseDrag(){
        // From the Unity3D docs: "The z position is in world units from the camera."  In my case I'm using the y-axis as height
        // with my camera facing back down the y-axis.  You can ignore this when the camera is orthograhic.
        current_position.z = hit_position.z = camera_position.y;
        
        // Get direction of movement.  (Note: Don't normalize, the magnitude of change is going to be Vector3.Distance(current_position-hit_position)
        // anyways.  
        Vector3 direction = Camera.main.ScreenToWorldPoint(current_position) - Camera.main.ScreenToWorldPoint(hit_position);
        
        // Invert direction to that terrain appears to move with the mouse.
        direction = direction * -1;
        
        Vector3 position = camera_position + direction;
        
        transform.position = position;
    }
}
于 2012-07-16T03:07:40.853 に答える
6

このスクリプトを思いつきました(カメラに追加しました):

private Vector2 worldStartPoint;

void Update () {

    // only work with one touch
    if (Input.touchCount == 1) {
        Touch currentTouch = Input.GetTouch(0);

        if (currentTouch.phase == TouchPhase.Began) {
            this.worldStartPoint = this.getWorldPoint(currentTouch.position);
        }

        if (currentTouch.phase == TouchPhase.Moved) {
            Vector2 worldDelta = this.getWorldPoint(currentTouch.position) - this.worldStartPoint;

            Camera.main.transform.Translate(
                -worldDelta.x,
                -worldDelta.y,
                0
            );
        }
    }
}

// convert screen point to world point
private Vector2 getWorldPoint (Vector2 screenPoint) {
    RaycastHit hit;
    Physics.Raycast(Camera.main.ScreenPointToRay(screenPoint), out hit);
    return hit.point;
}
于 2015-01-08T09:27:38.310 に答える
1

Pavel's answer は私を大いに助けてくれました. 私のシナリオは、正投影カメラを使用した 3D ワールドです。私が取り組んでいるトップダウン スタイルの RTS。パンとズームを Google マップのように機能させたいのですが、パンとズームを行うと、マウスは常にマップ上の同じ場所にとどまります。このスクリプトは私にとってこれを実現し、うまくいけば他の人のニーズに対応するのに十分な堅牢性を備えています。私はそれをたくさんテストしていませんが、初心者が学べるようにコメントしました。

using UnityEngine;

// I usually attach this to my main camera, but in theory you can attach it to any object in scene, since it uses Camera.main instead of "this".
public class CameraMovement : MonoBehaviour
{
    private Vector3 MouseDownPosition;

    void Update()
    {
        // If mouse wheel scrolled vertically, apply zoom...
        // TODO: Add pinch to zoom support (touch input)
        if (Input.mouseScrollDelta.y != 0)
        {
            // Save location of mouse prior to zoom
            var preZoomPosition = getWorldPoint(Input.mousePosition);

            // Apply zoom (might want to multiply Input.mouseScrollDelta.y by some speed factor if you want faster/slower zooming
            Camera.main.orthographicSize = Mathf.Clamp(Camera.main.orthographicSize + Input.mouseScrollDelta.y, 5, 80);

            // How much did mouse move when we zoomed?
            var delta = getWorldPoint(Input.mousePosition) - preZoomPosition;

            // Rotate camera to top-down (right angle = 90) before applying adjustment (otherwise we get "slide" in direction of camera angle).
            // TODO: If we allow camera to rotate on other axis we probably need to adjust that also.  At any rate, you want camera pointing "straight down" for this part to work.
            var rot = Camera.main.transform.localEulerAngles;
            Camera.main.transform.localEulerAngles = new Vector3(90, rot.y, rot.z);

            // Move the camera by the amount mouse moved, so that mouse is back in same position now.
            Camera.main.transform.Translate(delta.x, delta.z, 0);

            // Restore camera rotation
            Camera.main.transform.localEulerAngles = rot;
        }

        // When mouse is first pressed, just save location of mouse/finger.
        if (Input.GetMouseButtonDown(0))
        {
            MouseDownPosition = getWorldPoint(Input.mousePosition);
        }

        // While mouse button/finger is down...
        if (Input.GetMouseButton(0))
        {
            // Total distance finger/mouse has moved while button is down
            var delta = getWorldPoint(Input.mousePosition) - MouseDownPosition;

            // Adjust camera by distance moved, so mouse/finger stays at exact location (in world, since we are using getWorldPoint for everything).
            Camera.main.transform.Translate(delta.x, delta.z, 0);
        }
    }

    // This works by casting a ray.  For this to work well, this ray should always hit your "ground".  Setup ignore layers if you need to ignore other colliders.
    // Only tested this with a simple box collider as ground (just one flat ground).
    private Vector3 getWorldPoint(Vector2 screenPoint)
    {
        RaycastHit hit;
        Physics.Raycast(Camera.main.ScreenPointToRay(screenPoint), out hit);
        return hit.point;
    }
}
于 2016-10-14T02:33:47.873 に答える
0

Pavel からの回答に基づいて、スクリプトを単純化し、1 本以上の指で触れて 2 本目の指を離したときの見苦しい「ジャンプ」を削除しました。

private bool moreThenOneTouch = false;
private Vector3 worldStartPoint;

void Update() {

    Touch currentTouch;
    // only work with one touch
    if (Input.touchCount == 1 && !moreThenOneTouch) {
        currentTouch = Input.GetTouch(0);

        if (currentTouch.phase == TouchPhase.Began) {
            this.worldStartPoint = Camera.main.ScreenToWorldPoint(currentTouch.position);
        }

        if (currentTouch.phase == TouchPhase.Moved) {
            Vector3 worldDelta = Camera.main.ScreenToWorldPoint(currentTouch.position) - this.worldStartPoint;
            
            Camera.main.transform.Translate(
                -worldDelta.x,
                -worldDelta.y,
                0
            );
        }
    
    }

    if (Input.touchCount > 1) {
        moreThenOneTouch = true;
    } else {
        moreThenOneTouch = false;
        if(Input.touchCount == 1)
            this.worldStartPoint = Camera.main.ScreenToWorldPoint(Input.GetTouch(0).position);
    }
}
于 2021-02-22T20:58:14.707 に答える