unity選擇關卡的跑馬燈效果

最近項目中需要做一個類似亂鬥西遊的選擇關卡的效果,如下所示


涉及到的知識點也就四元數在旋轉中的應用。

四元數公式:
    q.w=cos(a/2) 
    q.x=RAix.x*sin(a/2)
    q.y=RAix.y*sin(a/2)
    q.z=RAix.z*sin(a/2)

首先,需要需要假定一個旋轉的半徑,然後在該空間中放入一系列的單元,這些單元圍繞着座標系的某跟軸旋轉,每個單元必然存在一個圍繞該軸的角度,比如放入三個單元,那麼,這三個單元圍繞某個軸旋轉的角度必然依次是0, 120, 240,。然後拖拽屏幕,這三個單元在初始旋轉角度的基礎上做角度的偏移,再根據這個新值計算出新的位置。原理就是這樣,下面放上代碼。


using UnityEngine;
using System.Collections;
using UnityEngine.Events;
using System.Collections.Generic;

public class LoopScrollView : MonoBehaviour {
	[SerializeField]
	protected TouchEvent mTouchEvent;
	[SerializeField]
	private List<GameObject> mChildrenList;
	[SerializeField]
	private Vector2 mStartTouchPosition;
	[SerializeField]
	private Vector2 mLastTouchPosition;
	[SerializeField]
	private UILayer mUILayer;
	[SerializeField]
	protected float mR = 400;		//虛擬旋轉半徑
	[SerializeField]
	private float mAngle = 0;	//虛擬當前旋轉角度
	[SerializeField]
	private float mTargetAngle = 1000;	//選擇目標角度
	private float mTouchOffsetAngle = 0;	//每次拖動旋轉的角度
	private int mCurSelectIndex = 0;

	public static LoopScrollView Create(Vector2 size, RectTransform rectT, UILayer uiLayer)
	{
		LoopScrollView obj = UINode.Create<LoopScrollView>("Layout/Common/LoopScrollView", rectT);
		obj.GetComponent<RectTransform>().sizeDelta = size;
		obj.mUILayer = uiLayer;
		return obj;
	}

	void Awake()
	{
		mTouchEvent.AddTouchEventListener(onTouchCallBack);
	}

	void Update()
	{
		if(mTargetAngle != 1000)
		{
			mAngle = Mathf.Lerp(mAngle, mTargetAngle, 0.2f);
			for(int i = 0; i < mChildrenList.Count; ++i)
			{
				UpdatePositionByAngle(mChildrenList[i], -2.0f*Mathf.PI*i/mChildrenList.Count + mAngle);
			}
			float deltaAngle = 180*(mTargetAngle - mAngle)/Mathf.PI;
			if(deltaAngle < 0.5f && deltaAngle > -0.5f)
			{
				mAngle = mTargetAngle;
				for(int i = 0; i < mChildrenList.Count; ++i)
				{
					UpdatePositionByAngle(mChildrenList[i], -2.0f*Mathf.PI*i/mChildrenList.Count + mAngle);
				}
				mTargetAngle = 1000;
			}
			UpdateZOrder();
		}
	}

	void setR(int r)
	{
		mR = r;
		for(int i = 0; i < mChildrenList.Count; ++i)
		{
			UpdatePositionByAngle(mChildrenList[i], -2.0f*Mathf.PI*i/mChildrenList.Count);
		}
	}

	public void AddItem(GameObject obj)
	{
		obj.transform.parent = this.transform;
		obj.transform.localScale = new Vector3(1,1,1);
		mChildrenList.Add(obj);
		for(int i = 0; i < mChildrenList.Count; ++i)
		{
			UpdatePositionByAngle(mChildrenList[i], -2.0f*Mathf.PI*i/mChildrenList.Count);
		}
		UpdateZOrder();
	}





	void onTouchCallBack(TouchEvent touchEvent)
	{
		switch (touchEvent.GetTouchEventType())
		{
		case TouchEvent.TouchEventType.Down:
			mLastTouchPosition = touchEvent.Position;
			mStartTouchPosition = touchEvent.Position;
			break;
		case TouchEvent.TouchEventType.Move:
			//Debug.Log("拖動");
			if(mLastTouchPosition != Vector2.zero)
			{
				MoveChildren(mLastTouchPosition, touchEvent.Position);
			}
			mLastTouchPosition = touchEvent.Position;
			break;
		case TouchEvent.TouchEventType.Up:
			mLastTouchPosition = Vector2.zero;
			if(mTouchOffsetAngle > 0.4*Mathf.PI/mChildrenList.Count)
			{
				mTargetAngle = mAngle + 2.0f*Mathf.PI/mChildrenList.Count;
				++mCurSelectIndex;
				if(mCurSelectIndex > mChildrenList.Count - 1)
				{
					mCurSelectIndex = 0;
				}
			}
			else if(mTouchOffsetAngle < -0.4*Mathf.PI/mChildrenList.Count)
			{
				mTargetAngle = mAngle - 2.0f*Mathf.PI/mChildrenList.Count;
				--mCurSelectIndex;
				if(mCurSelectIndex < 0)
				{
					mCurSelectIndex = mChildrenList.Count - 1;
				}
			}
			else
			{
				mTargetAngle = mAngle;
			}
			mAngle = mAngle + mTouchOffsetAngle;
			mTouchOffsetAngle = 0;
			UpdateSelectIndex(mCurSelectIndex);
			break;
		default:
			break;
		}
	}

	void MoveChildren(Vector2 lastPosiotn, Vector2 curPosition)
	{
		//圓心座標
		Vector2 center = new Vector2(0, mR);
		for(int i = 0; i < mChildrenList.Count; ++i)
		{
			Vector2 lastUIPos;
			Vector2 curUIPos;
			RectTransformUtility.ScreenPointToLocalPointInRectangle(mUILayer.transform as RectTransform, lastPosiotn, mUILayer.UICanvas.worldCamera, out lastUIPos);
			RectTransformUtility.ScreenPointToLocalPointInRectangle(mUILayer.transform as RectTransform, curPosition, mUILayer.UICanvas.worldCamera, out curUIPos);
			Vector2 offset = curUIPos - lastUIPos;
			//位移轉角度
			if(mTouchOffsetAngle < 2.0f*Mathf.PI/mChildrenList.Count && mTouchOffsetAngle > -2.0f*Mathf.PI/mChildrenList.Count)
			{
				//mAngle = mAngle - (offset.x)/(2.0f*Mathf.PI*mR);
				mTouchOffsetAngle = mTouchOffsetAngle - (offset.x)/(2.0f*Mathf.PI*mR);
			}
			UpdatePositionByAngle(mChildrenList[i], -2.0f*Mathf.PI*i/mChildrenList.Count + mAngle + mTouchOffsetAngle);
		} 
		UpdateZOrder();
	}

	void UpdatePositionByAngle(GameObject obj,  float angle)
	{
		angle += Mathf.PI/2;
		Quaternion q = new Quaternion(0, 1*Mathf.Sin(angle/2), 0, Mathf.Cos(angle/2));
		Vector3 vec = q * new Vector3(mR, 0, 0);
		obj.transform.localPosition = vec + new Vector3(0, 0, mR);
		obj.transform.localPosition = new Vector3(obj.transform.localPosition.x, (obj.transform.localPosition.z - mR)/6, obj.transform.localPosition.z);
	}

	//深度排序
	void UpdateZOrder()
	{
		List<GameObject> sortBuf = new List<GameObject>();
		List<GameObject> sortResult = new List<GameObject>();
		float leastZ = 10000;
		for(int i = 0; i < mChildrenList.Count; ++i)
		{
			sortBuf.Add(mChildrenList[i]);
		}
		for(int i = 0; i < mChildrenList.Count; ++i)
		{
			int index = -1;
			for(int j = 0; j < sortBuf.Count; ++j)
			{
				if(sortBuf[j].transform.localPosition.z < leastZ)
				{
					leastZ = sortBuf[j].transform.localPosition.z;
					index = j;
				}
			}
			sortResult.Add(sortBuf[index]);
			sortBuf.RemoveAt(index);
			leastZ = 10000;
		}

		for(int i = 0; i < sortResult.Count; ++i)
		{
			sortResult[i].transform.SetSiblingIndex(sortResult.Count - i - 1);
		}
	}

	//更新選擇
	void UpdateSelectIndex(int index)
	{
		Debug.Log("選擇的序號:" + index);
		EventCenter.Single.PostEvent("LOOP_SCROLL_VIEW_SELECT_INDEX", index);
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章