Unity - 簡易2D拖曳物件移動

  在2D場景中當需要拖曳物件的時候,可以使用一些簡單的方法達到這個目的,這次的目標是在畫面上選擇好物件按下滑鼠不放或是觸控螢幕的按住不放,然後手指或滑鼠移動時物體也會跟著移動。



  要做這個效果可以有很多種方法,這邊使用簡單的兩種方法,一種是使用Vector3.SmoothDamp()讓物體一直朝著滑鼠座標移動,一種是使用Rigidbody的AddForce給物體施加一個方向的力道。



  第一個方式,讓物件朝著滑鼠座標移動,這邊使用SmoothDamp的原因在於使用這個方法內建的功能來限制最高速度。當然也可以用Lerp或MoveTowards等等看你想要的移動方,這邊就簡單計算滑鼠座標換算成世界座標,然後讓物體往該點移動即可。
(關於攝影機的Z軸參考使用ScreenToWorldPoint的時候,回傳值是camera的position而不是目標點座標的問題)

    public Vector3 v;
    public float maxSpeed = 5.0f;
    void TowardTarget()
    {
        Vector3 targetPos = Camera.main.ScreenToWorldPoint (new Vector3(prePos.x, prePos.y, 10f)); //這邊假設你Camera的Z軸在-10而物體的Z軸在0
        transform.position = Vector3.SmoothDamp (transform.position, targetPos, ref v, speedDelta, maxSpeed);
    }



  第二個方式,使用Rigidbody的AddForce,當滑鼠或指標移動的時候,計算當下的移動向量跟速度,然後給物體加上這個方向的力讓物體朝著這個方向移動,這個跟朝著滑鼠座標移動的方式稍有不同,是朝著施力方向移動,並且拖曳滑鼠的速度越快施力越大,移動也就越快。
    private Vector3 prePos;
    void ForceCalculate()
    {
        Vector3 curPos = Input.mousePosition; //記錄當前滑鼠座標
        Vector3 dir = curPos - prePos; //跟前一個frame的座標來取得向量
        float dist = dir.magnitude; //移動的距離
        float v = dist / Time.deltaTime; //使用距離跟時間來計算速度
  
        this.rigidbody.AddForce (dir.normalized * v * Time.deltaTime * speedDelta); //給物體施力
        prePos = curPos;
    }



--
實作測試:點選按鈕切換不同的移動方式,滑鼠點選場景中球球然後拖曳即可。
物體須加上Rigidbody,因為使用2D的設定所以取消Gravity避免一開始就往下墜落。



--
拖曳移動的Code
public class MyDrag : MonoBehaviour
{
    public enum DragMethod { Force, SmoothDamp }
    public DragMethod method = DragMethod.Force; //切換用

    public float speedDelta = 1.0f;
    public float decelerate = 1.0f;
    private bool startDrag;
    private Vector3 prePos;

    void Start()
    {
        prePos = Camera.main.WorldToScreenPoint(transform.position);
        this.rigidbody.drag = decelerate; //使用這個來讓物體逐漸停止,也可以在Rigidbody中設定drag值,這樣就可以移除這行
    }

    void Update ()
    {
        switch (method)
        {
            case DragMethod.Force:
                if(Input.GetKeyUp(KeyCode.Mouse0))
                    startDrag = false;
                if(startDrag)
                    ForceCalculate(); //如果拖曳中,就一直給物體施加滑鼠造成的力
            break;

            case DragMethod.SmoothDamp:
                if(Input.GetKeyUp(KeyCode.Mouse0))
                {
                    if(startDrag) //如果放開滑鼠,就停止物體追隨滑鼠的效果,並讓物體朝著最後移動的向量移動
                    {
                        this.rigidbody.velocity = v;
                        startDrag = false;
                        v = Vector3.zero;
                    }
                }

                if(startDrag) {
                    prePos = Input.mousePosition;
                    TowardTarget (); //如果拖曳中,就讓物體往滑鼠的座標移動
                }
            break;
        }
    }

    void OnMouseDown() //當滑鼠點下物件的時候重設一些數值
    {
        switch(method)
        {
            case DragMethod.Force:
                prePos = Input.mousePosition;
            break;

            case DragMethod.SmoothDamp:
                prePos = Camera.main.WorldToScreenPoint (transform.position);
            break;
        }

        startDrag = true;
    }

    //施力的方式移動
    void ForceCalculate()
    {
        Vector3 curPos = Input.mousePosition;
        Vector3 dir = curPos - prePos;
        float dist = dir.magnitude;
        float v = dist / Time.deltaTime;
  
        this.rigidbody.AddForce (dir.normalized * v * Time.deltaTime * speedDelta);
        prePos = curPos;
    }

    //朝著滑鼠的方式移動
    public Vector3 v;
    public float maxSpeed = 5.0f;
    void TowardTarget()
    {
        Vector3 targetPos = Camera.main.ScreenToWorldPoint (new Vector3(prePos.x, prePos.y, 10f)); //Assume your camera's z is -10 and cube's z is 0
        transform.position = Vector3.SmoothDamp (transform.position, targetPos, ref v, speedDelta, maxSpeed);
    }
}


4 comments:

Unknown said...

您好:想請問一下,在函數中有一個this.rigidbody.drag的部分,我輸入後程式表示錯誤,請問是什麼原因呢?謝謝:)

IVerv said...

在Po這篇的時候使用的Unity是4.6版本的,進入5的版本後有修改這部分,沒辦法直接使用rigidbody,所以必須要修改這部分。
你可以把所有this.rigidbody改為 (Rigidbody)GetComponent(typeof(Rigidbody))
或者一開始宣告的field多放一個
Rigidbody r;
然後在Start()裡面起始化 r = (Rigidbody)GetComponent(typeof(Rigidbody));
之後把所有this.rigidbody改為r來用這樣也可以

tony24 said...

不好意思我想請問一下 他為什麼會說“speedDelta'這個名字並不在目前的情況下存在

IVerv said...

Component裡面我有寫這個變數,不知道你的問題在哪

Post a Comment