最近在做在一個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;
}
}
} }