天天看點

【小松教你手遊開發】【系統子產品開發】射線觸發按鈕

最近在做在一個Scrollview下每個Item要實作長按出現其他效果。 在NGUI上可以正常的這麼做。 但是在2D Toolkit上卻有問題。

在NGUI上滑動Scrollview其實是通過拖動每一個Item實作拖動效果。 而2D Toolkit上是在Scrollview上有一個一大塊碰撞體。通過觸發這個碰撞體實作拖動。

這裡的差別就導緻了當你想在2D Toolkit上實作長按Item時,被前面的Scrollview的碰撞體遮擋。 這時候我就用射線來觸發長按功能。(其實原本按鈕就是通過射線來觸發)

射線觸發有個問題就是層次。可能在你想要觸發的按鈕前面有很多個碰撞體,是以我們需要解決的是如何在很多碰撞體的情況下找到你想要的。

這裡我們先說整個邏輯然後看代碼。 首先射線是需要每幀射,是以我們在Update函數裡寫射線邏輯。 射線函數Physics.Raycast(newRay, out hitInfo, 1000, _layerMask)會傳回射線中第一個碰到的碰撞體。 是以我們要做的是,首先通過螢幕射出第一個射線,當射線碰到碰撞體後判斷這個是否我們需要的。 如果不是的話,我們可以通過這個函數輸出的hitInfo中獲得碰撞的點和碰撞體的寬 這個點加上這個碰撞體的Collider.size.z獲得這個碰撞體後面的點。 我們在這個新點上在向螢幕内發出射線檢測下一個碰撞 直到找到我們想要的點為止。

這裡我們可以用遞歸來找。

【小松教你手遊開發】【系統子產品開發】射線觸發按鈕

觸發的時候根據點選的點擷取這個Vector3,并儲存他的x,y以便之後根據這個坐标繼續找到自己想要找的Gameobject

【小松教你手遊開發】【系統子產品開發】射線觸發按鈕

當找到之後就發出點選的效果了,是以這裡用的是Action,ClickHandler像事件一樣通知監聽的腳本

【小松教你手遊開發】【系統子產品開發】射線觸發按鈕

可以看到下面開了一個協程。 這個協程的作用是通過時間間隙LongPressIntervalTime來判斷這個按鈕是否一緻按着。

【小松教你手遊開發】【系統子產品開發】射線觸發按鈕

如果在這段時間内沒有松開按鈕,就會觸發長按事件。

而現在還需加入松開按鈕的邏輯。也是靠射線來判斷

【小松教你手遊開發】【系統子產品開發】射線觸發按鈕

這裡是根據之前保留下來的點和目标GameObject來判斷是否一直點選

【小松教你手遊開發】【系統子產品開發】射線觸發按鈕

發現松開觸發松開的函數

【小松教你手遊開發】【系統子產品開發】射線觸發按鈕

這就完成了一個按鈕的點選。松開。和長按功能。

中間還有各種邏輯限制和判斷。具體代碼看腳本。

using UnityEngine;

using System.Collections;

using System;

public class RaycastGameObject : MonoBehaviour {

    public Action ClickHandler;

    public Action ReleaseHandler;

    public Action LongPressHandler;

    public GameObject TargetGameObject = null;

    public float LongPressIntervalTime = 1.5f;

    public string LayerName = "";

    bool _itemPressing = false;

    bool _checkingPressing = false;

    Vector3 _raycaseOriginVector3 = new Vector3();

    LayerMask _layerMask;

    F_Panel _panel;

    void Awake()

    {

        if (_panel == null)

            GetPanelInParent(transform);

        if (TargetGameObject == null)

            TargetGameObject = gameObject;

        if (LayerName == "")

        {

            _layerMask = 1 << gameObject.layer;

            LayerName = LayerMask.LayerToName(gameObject.layer);

        }

        else

            _layerMask = 1 << LayerMask.NameToLayer(LayerName);

    }

    // Update is called once per frame

    void Update()

    {

        if (_panel == null)

        {

            GetPanelInParent(transform);

        }

        else

        {

            if (!_checkingPressing && Input.GetMouseButton(0) && PopUpManager.Get.CurrentPopup.name == _panel.name)

            {

                _checkingPressing = true;

                _raycaseOriginVector3 = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0);

                RaycastGameObjectClick(Camera.main.ScreenPointToRay(Input.mousePosition), TargetGameObject);

            }

            else if (_itemPressing)

            {

                RaycastGameObjectRelease(new Ray(_raycaseOriginVector3, new Vector3(0, 0, 1)), TargetGameObject);

            }

        }

    }

    void RaycastGameObjectClick(Ray ray, GameObject targetObject)

    {

        Ray newRay = ray;

        RaycastHit hitInfo;

        if (Physics.Raycast(newRay, out hitInfo, 1000, _layerMask))

        {

            Debug.DrawLine(newRay.origin, hitInfo.point);

            if (hitInfo.collider.gameObject == null)

            {

                _checkingPressing = false;

                return;

            }

            else if (hitInfo.collider.gameObject != targetObject)

            {

                _raycaseOriginVector3 = hitInfo.point;

                Vector3 newPoint = new Vector3(hitInfo.point.x, hitInfo.point.y, hitInfo.point.z + hitInfo.transform.GetComponent<BoxCollider>().size.z);

                RaycastGameObjectClick(new Ray(newPoint, new Vector3(0, 0, 1)), targetObject);

            }

            else

            {

                OnItemPressedEnter();

                return;

            }

        }

        else

        {

            _checkingPressing = false;

        }

    }

    void RaycastGameObjectRelease(Ray ray, GameObject targetObject)

    {

        if (Input.GetMouseButton(0))

        {

            Ray newRay = ray;

            RaycastHit hitInfo;

            if (Physics.Raycast(newRay, out hitInfo, _layerMask))

            {

                Debug.DrawLine(newRay.origin, hitInfo.point);

                GameObject gameobj = hitInfo.collider.gameObject;

                if (gameobj != targetObject)

                    OnItemPressedEnd();

            }

        }

        else

        {

            OnItemPressedEnd();

        }

    }

    void OnItemPressedEnter()

    {

        _itemPressing = true;

        if (ClickHandler != null)

            ClickHandler();

        StartCoroutine(WaitAndCheckPress(LongPressIntervalTime));

    }

    void OnItemPressedEnd()

    {

        _checkingPressing = false;

        _itemPressing = false;

        if (ReleaseHandler != null)

            ReleaseHandler();

    }

    IEnumerator WaitAndCheckPress(float seconds)

    {

        yield return new WaitForSeconds(seconds);

        if (_itemPressing)

        {

            if (PopUpManager.Get.CurrentPopup.name == _panel.name)

                LongPressHandler();

        }

    }

    void GetPanelInParent(Transform _t)

    {

        if (_t.parent != null)

        {

            F_Panel getPanel = _t.parent.GetComponent<F_Panel>();

            if (getPanel == null)

            {

                GetPanelInParent(_t.parent);

            }

            else

            {

                _panel = getPanel;

            }

        }

    } }

繼續閱讀