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);
	}
}


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