Unity實(shí)現(xiàn)游戲卡牌滾動(dòng)效果
最近項(xiàng)目中的活動(dòng)面板要做來回滾動(dòng)卡牌預(yù)覽效果,感覺自己來寫的話,也能寫,但是可能會(huì)比較耗時(shí),看到Github上有開源的項(xiàng)目,于是就借用了,Github的資源地址,感謝作者的分享。
本篇博客旨在告訴大家如何利用這個(gè)插件。
插件的核心在于工程中的6個(gè)腳本,以下是六個(gè)腳本的源碼:
DragEnhanceView.cs
using UnityEngine; using System.Collections; using UnityEngine.UI; using UnityEngine.EventSystems; public class UGUIEnhanceItem : EnhanceItem { private Button uButton; private Image image; protected override void OnStart() { image = GetComponent<Image>(); uButton = GetComponent<Button>(); uButton.onClick.AddListener(OnClickUGUIButton); } private void OnClickUGUIButton() { OnClickEnhanceItem(); } // Set the item "depth" 2d or 3d protected override void SetItemDepth(float depthCurveValue, int depthFactor, float itemCount) { int newDepth = (int)(depthCurveValue * itemCount); this.transform.SetSiblingIndex(newDepth); } public override void SetSelectState(bool isCenter) { if (image == null) image = GetComponent<Image>(); image.color = isCenter ? Color.white : Color.gray; } }
EnhanceScrollViewDragController.cs
using UnityEngine; using System.Collections; public class EnhanceScrollViewDragController : MonoBehaviour { private Vector2 lastPosition = Vector2.zero; private Vector2 cachedPosition = Vector2.zero; private GameObject dragTarget; private Camera targetCamera; private int rayCastMask = 0; private bool dragStart = false; public void SetTargetCameraAndMask(Camera camera, int mask) { this.targetCamera = camera; this.rayCastMask = mask; } void Update() { if (this.targetCamera == null) return; #if UNITY_EDITOR ProcessMouseInput(); #elif UNITY_IOS || UNITY_ANDROID ProcessTouchInput(); #endif } /// <summary> /// Process Mouse Input /// </summary> private void ProcessMouseInput() { if (Input.GetMouseButtonDown(0)) { if (targetCamera == null) return; dragTarget = RayCast(this.targetCamera, Input.mousePosition); lastPosition.x = Input.mousePosition.x; lastPosition.y = Input.mousePosition.y; } if (Input.GetMouseButton(0)) { if (dragTarget == null) return; cachedPosition.x = Input.mousePosition.x; cachedPosition.y = Input.mousePosition.y; Vector2 delta = cachedPosition - lastPosition; if (!dragStart && delta.sqrMagnitude != 0f) dragStart = true; if (dragStart) { // Notify target dragTarget.SendMessage("OnEnhanceViewDrag", delta, SendMessageOptions.DontRequireReceiver); } lastPosition = cachedPosition; } if (Input.GetMouseButtonUp(0)) { if (dragTarget != null && dragStart) { dragTarget.SendMessage("OnEnhaneViewDragEnd", SendMessageOptions.DontRequireReceiver); } dragTarget = null; dragStart = false; } } /// <summary> /// Process Touch input /// </summary> private void ProcessTouchInput() { if (Input.touchCount > 0) { Touch touch = Input.GetTouch(0); if (touch.phase == TouchPhase.Began) { if (targetCamera == null) return; dragTarget = RayCast(this.targetCamera, Input.mousePosition); } else if (touch.phase == TouchPhase.Moved) { if (dragTarget == null) return; if (!dragStart && touch.deltaPosition.sqrMagnitude != 0f) { dragStart = true; } if (dragStart) { // Notify target dragTarget.SendMessage("OnEnhanceViewDrag", touch.deltaPosition, SendMessageOptions.DontRequireReceiver); } } else if (touch.phase == TouchPhase.Ended) { if (dragTarget != null && dragStart) { dragTarget.SendMessage("OnEnhaneViewDragEnd", SendMessageOptions.DontRequireReceiver); } dragTarget = null; dragStart = false; } } } public GameObject RayCast(Camera cam, Vector3 inPos) { Vector3 pos = cam.ScreenToViewportPoint(inPos); if (float.IsNaN(pos.x) || float.IsNaN(pos.y)) return null; if (pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f) return null; Ray ray = cam.ScreenPointToRay(inPos); float dis = 100f; RaycastHit[] hits = Physics.RaycastAll(ray, dis, rayCastMask); if (hits.Length > 0) { for (int i = 0; i < hits.Length; i++) { GameObject go = hits[i].collider.gameObject; DragEnhanceView dragView = go.GetComponent<DragEnhanceView>(); if (dragView == null) continue; else { // just return current hover object our drag target return go; } } } return null; } }
EnhanceItem.cs
using UnityEngine; using System.Collections; public class EnhanceItem : MonoBehaviour { // Start index private int curveOffSetIndex = 0; public int CurveOffSetIndex { get { return this.curveOffSetIndex; } set { this.curveOffSetIndex = value; } } // Runtime real index(Be calculated in runtime) private int curRealIndex = 0; public int RealIndex { get { return this.curRealIndex; } set { this.curRealIndex = value; } } // Curve center offset private float dCurveCenterOffset = 0.0f; public float CenterOffSet { get { return this.dCurveCenterOffset; } set { dCurveCenterOffset = value; } } private Transform mTrs; void Awake() { mTrs = this.transform; OnAwake(); } void Start() { OnStart(); } // Update Item's status // 1. position // 2. scale // 3. "depth" is 2D or z Position in 3D to set the front and back item public void UpdateScrollViewItems( float xValue, float depthCurveValue, int depthFactor, float itemCount, float yValue, float scaleValue) { Vector3 targetPos = Vector3.one; Vector3 targetScale = Vector3.one; // position targetPos.x = xValue; targetPos.y = yValue; mTrs.localPosition = targetPos; // Set the "depth" of item // targetPos.z = depthValue; SetItemDepth(depthCurveValue, depthFactor, itemCount); // scale targetScale.x = targetScale.y = scaleValue; mTrs.localScale = targetScale; } protected virtual void OnClickEnhanceItem() { EnhanceScrollView.GetInstance.SetHorizontalTargetItemIndex(this); } protected virtual void OnStart() { } protected virtual void OnAwake() { } protected virtual void SetItemDepth(float depthCurveValue, int depthFactor, float itemCount) { } // Set the item center state public virtual void SetSelectState(bool isCenter) { } }
EnhanceScrollView.cs
using UnityEngine; using System.Collections; using System.Collections.Generic; public class EnhanceScrollView : MonoBehaviour { public enum InputSystemType { NGUIAndWorldInput, // use EnhanceScrollViewDragController.cs to get the input(keyboard and touch) UGUIInput, // use UDragEnhanceView for each item to get drag event } // Input system type(NGUI or 3d world, UGUI) public InputSystemType inputType = InputSystemType.NGUIAndWorldInput; // Control the item's scale curve public AnimationCurve scaleCurve; // Control the position curve public AnimationCurve positionCurve; // Control the "depth"'s curve(In 3d version just the Z value, in 2D UI you can use the depth(NGUI)) // NOTE: // 1. In NGUI set the widget's depth may cause performance problem // 2. If you use 3D UI just set the Item's Z position public AnimationCurve depthCurve = new AnimationCurve(new Keyframe(0, 0), new Keyframe(0.5f, 1), new Keyframe(1, 0)); // The start center index [Tooltip("The Start center index")] public int startCenterIndex = 0; // Offset width between item public float cellWidth = 10f; private float totalHorizontalWidth = 500.0f; // vertical fixed position value public float yFixedPositionValue = 46.0f; // Lerp duration public float lerpDuration = 0.2f; private float mCurrentDuration = 0.0f; private int mCenterIndex = 0; public bool enableLerpTween = true; // center and preCentered item private EnhanceItem curCenterItem; private EnhanceItem preCenterItem; // if we can change the target item private bool canChangeItem = true; private float dFactor = 0.2f; // originHorizontalValue Lerp to horizontalTargetValue private float originHorizontalValue = 0.1f; public float curHorizontalValue = 0.5f; // "depth" factor (2d widget depth or 3d Z value) private int depthFactor = 5; // Drag enhance scroll view [Tooltip("Camera for drag ray cast")] public Camera sourceCamera; private EnhanceScrollViewDragController dragController; public void EnableDrag(bool isEnabled) { if (isEnabled) { if (inputType == InputSystemType.NGUIAndWorldInput) { if (sourceCamera == null) { Debug.LogError("## Source Camera for drag scroll view is null ##"); return; } if (dragController == null) dragController = gameObject.AddComponent<EnhanceScrollViewDragController>(); dragController.enabled = true; // set the camera and mask dragController.SetTargetCameraAndMask(sourceCamera, (1 << LayerMask.NameToLayer("UI"))); } } else { if (dragController != null) dragController.enabled = false; } } // targets enhance item in scroll view public List<EnhanceItem> listEnhanceItems; // sort to get right index private List<EnhanceItem> listSortedItems = new List<EnhanceItem>(); private static EnhanceScrollView instance; public static EnhanceScrollView GetInstance { get { return instance; } } void Awake() { instance = this; } void Start() { canChangeItem = true; int count = listEnhanceItems.Count; dFactor = (Mathf.RoundToInt((1f / count) * 10000f)) * 0.0001f; mCenterIndex = count / 2; if (count % 2 == 0) mCenterIndex = count / 2 - 1; int index = 0; for (int i = count - 1; i >= 0; i--) { listEnhanceItems[i].CurveOffSetIndex = i; listEnhanceItems[i].CenterOffSet = dFactor * (mCenterIndex - index); listEnhanceItems[i].SetSelectState(false); GameObject obj = listEnhanceItems[i].gameObject; if (inputType == InputSystemType.NGUIAndWorldInput) { DragEnhanceView script = obj.GetComponent<DragEnhanceView>(); if (script != null) script.SetScrollView(this); } else { UDragEnhanceView script = obj.GetComponent<UDragEnhanceView>(); if (script != null) script.SetScrollView(this); } index++; } // set the center item with startCenterIndex if (startCenterIndex < 0 || startCenterIndex >= count) { Debug.LogError("## startCenterIndex < 0 || startCenterIndex >= listEnhanceItems.Count out of index ##"); startCenterIndex = mCenterIndex; } // sorted items listSortedItems = new List<EnhanceItem>(listEnhanceItems.ToArray()); totalHorizontalWidth = cellWidth * count; curCenterItem = listEnhanceItems[startCenterIndex]; curHorizontalValue = 0.5f - curCenterItem.CenterOffSet; LerpTweenToTarget(0f, curHorizontalValue, false); // // enable the drag actions // EnableDrag(true); } private void LerpTweenToTarget(float originValue, float targetValue, bool needTween = false) { if (!needTween) { SortEnhanceItem(); originHorizontalValue = targetValue; UpdateEnhanceScrollView(targetValue); this.OnTweenOver(); } else { originHorizontalValue = originValue; curHorizontalValue = targetValue; mCurrentDuration = 0.0f; } enableLerpTween = needTween; } public void DisableLerpTween() { this.enableLerpTween = false; } /// /// Update EnhanceItem state with curve fTime value /// public void UpdateEnhanceScrollView(float fValue) { for (int i = 0; i < listEnhanceItems.Count; i++) { EnhanceItem itemScript = listEnhanceItems[i]; float xValue = GetXPosValue(fValue, itemScript.CenterOffSet); float scaleValue = GetScaleValue(fValue, itemScript.CenterOffSet); float depthCurveValue = depthCurve.Evaluate(fValue + itemScript.CenterOffSet); itemScript.UpdateScrollViewItems(xValue, depthCurveValue, depthFactor, listEnhanceItems.Count, yFixedPositionValue, scaleValue); } } void Update() { if (enableLerpTween) TweenViewToTarget(); } private void TweenViewToTarget() { mCurrentDuration += Time.deltaTime; if (mCurrentDuration > lerpDuration) mCurrentDuration = lerpDuration; float percent = mCurrentDuration / lerpDuration; float value = Mathf.Lerp(originHorizontalValue, curHorizontalValue, percent); UpdateEnhanceScrollView(value); if (mCurrentDuration >= lerpDuration) { canChangeItem = true; enableLerpTween = false; OnTweenOver(); } } private void OnTweenOver() { if (preCenterItem != null) preCenterItem.SetSelectState(false); if (curCenterItem != null) curCenterItem.SetSelectState(true); } // Get the evaluate value to set item's scale private float GetScaleValue(float sliderValue, float added) { float scaleValue = scaleCurve.Evaluate(sliderValue + added); return scaleValue; } // Get the X value set the Item's position private float GetXPosValue(float sliderValue, float added) { float evaluateValue = positionCurve.Evaluate(sliderValue + added) * totalHorizontalWidth; return evaluateValue; } private int GetMoveCurveFactorCount(EnhanceItem preCenterItem, EnhanceItem newCenterItem) { SortEnhanceItem(); int factorCount = Mathf.Abs(newCenterItem.RealIndex) - Mathf.Abs(preCenterItem.RealIndex); return Mathf.Abs(factorCount); } // sort item with X so we can know how much distance we need to move the timeLine(curve time line) static public int SortPosition(EnhanceItem a, EnhanceItem b) { return a.transform.localPosition.x.CompareTo(b.transform.localPosition.x); } private void SortEnhanceItem() { listSortedItems.Sort(SortPosition); for (int i = listSortedItems.Count - 1; i >= 0; i--) listSortedItems[i].RealIndex = i; } public void SetHorizontalTargetItemIndex(EnhanceItem selectItem) { if (!canChangeItem) return; if (curCenterItem == selectItem) return; canChangeItem = false; preCenterItem = curCenterItem; curCenterItem = selectItem; // calculate the direction of moving float centerXValue = positionCurve.Evaluate(0.5f) * totalHorizontalWidth; bool isRight = false; if (selectItem.transform.localPosition.x > centerXValue) isRight = true; // calculate the offset * dFactor int moveIndexCount = GetMoveCurveFactorCount(preCenterItem, selectItem); float dvalue = 0.0f; if (isRight) { dvalue = -dFactor * moveIndexCount; } else { dvalue = dFactor * moveIndexCount; } float originValue = curHorizontalValue; LerpTweenToTarget(originValue, curHorizontalValue + dvalue, true); } // Click the right button to select the next item. public void OnBtnRightClick() { if (!canChangeItem) return; int targetIndex = curCenterItem.CurveOffSetIndex + 1; if (targetIndex > listEnhanceItems.Count - 1) targetIndex = 0; SetHorizontalTargetItemIndex(listEnhanceItems[targetIndex]); } // Click the left button the select next next item. public void OnBtnLeftClick() { if (!canChangeItem) return; int targetIndex = curCenterItem.CurveOffSetIndex - 1; if (targetIndex < 0) targetIndex = listEnhanceItems.Count - 1; SetHorizontalTargetItemIndex(listEnhanceItems[targetIndex]); } public float factor = 0.001f; // On Drag Move public void OnDragEnhanceViewMove(Vector2 delta) { // In developing if (Mathf.Abs(delta.x) > 0.0f) { curHorizontalValue += delta.x * factor; LerpTweenToTarget(0.0f, curHorizontalValue, false); } } // On Drag End public void OnDragEnhanceViewEnd() { // find closed item to be centered int closestIndex = 0; float value = (curHorizontalValue - (int)curHorizontalValue); float min = float.MaxValue; float tmp = 0.5f * (curHorizontalValue < 0 ? -1 : 1); for (int i = 0; i < listEnhanceItems.Count; i++) { float dis = Mathf.Abs(Mathf.Abs(value) - Mathf.Abs((tmp - listEnhanceItems[i].CenterOffSet))); if (dis < min) { closestIndex = i; min = dis; } } originHorizontalValue = curHorizontalValue; float target = ((int)curHorizontalValue + (tmp - listEnhanceItems[closestIndex].CenterOffSet)); preCenterItem = curCenterItem; curCenterItem = listEnhanceItems[closestIndex]; LerpTweenToTarget(originHorizontalValue, target, true); canChangeItem = false; } }
NGUIEnhanceItem.cs
using UnityEngine; using System.Collections; /// <summary> /// NGUI Enhance item example /// </summary> public class NGUIEnhanceItem : EnhanceItem { private UITexture mTexture; protected override void OnAwake() { this.mTexture = GetComponent<UITexture>(); UIEventListener.Get(this.gameObject).onClick = OnClickNGUIItem; } private void OnClickNGUIItem(GameObject obj) { this.OnClickEnhanceItem(); } // Set the item "depth" 2d or 3d protected override void SetItemDepth(float depthCurveValue, int depthFactor, float itemCount) { if (mTexture.depth != (int)Mathf.Abs(depthCurveValue * depthFactor)) mTexture.depth = (int)Mathf.Abs(depthCurveValue * depthFactor); } // Item is centered public override void SetSelectState(bool isCenter) { if (mTexture == null) mTexture = this.GetComponent<UITexture>(); if (mTexture != null) mTexture.color = isCenter ? Color.white : Color.gray; } protected override void OnClickEnhanceItem() { // item was clicked base.OnClickEnhanceItem(); } }
UGUIEnhanceItem.cs
using UnityEngine; using System.Collections; using UnityEngine.UI; using UnityEngine.EventSystems; public class UGUIEnhanceItem : EnhanceItem { private Button uButton; private Image image; protected override void OnStart() { image = GetComponent<Image>(); uButton = GetComponent<Button>(); uButton.onClick.AddListener(OnClickUGUIButton); } private void OnClickUGUIButton() { OnClickEnhanceItem(); } // Set the item "depth" 2d or 3d protected override void SetItemDepth(float depthCurveValue, int depthFactor, float itemCount) { int newDepth = (int)(depthCurveValue * itemCount); this.transform.SetSiblingIndex(newDepth); } public override void SetSelectState(bool isCenter) { if (image == null) image = GetComponent<Image>(); image.color = isCenter ? Color.white : Color.gray; } }
導(dǎo)入以上6個(gè)腳本以后,我們開始制作效果,先從NGUI開始,我們先在場景中,隨便添加一個(gè)背景,然后,我們?cè)赨IRoot下面添加一個(gè)空物體,取名Scrollview,添加EnhanceScrollView.cs腳本,然后制作六個(gè)Texture作為Scrollview的子物體,添加DragEnhanceView.cs腳本,NGUIEnhanceItem.cs腳本,BoxCollider組件。接著,我們?cè)诹鶄€(gè)圖片下方添加兩個(gè)Button,作為左右切換卡牌的按鈕,在點(diǎn)擊事件中,拖入Scrollview,分別添加OnBtnLeftClick,OnBtnRightClick方法。做完以上操作以后,場景大概是這樣:
接著,我們選中Scrollview,調(diào)整腳本參數(shù):
ScaleCurve圖像參數(shù),設(shè)置為如下,左右循環(huán)都為pingpong:
PositionCurve圖像參數(shù)如下,左右循環(huán)都為loop:
DepthCurve圖像參數(shù)如下,左右循環(huán)都為loop:
然后把Scrollview的子物體都拖到ListEnhanceItems這個(gè)公開數(shù)組下:
這樣,我們就把配置工作都做好了,運(yùn)行游戲:
可以看到,效果還不錯(cuò),左右滑動(dòng)或者點(diǎn)擊切換按鈕,就能實(shí)現(xiàn)切換卡牌的功能。
接著,我們看一下UGUI的實(shí)現(xiàn),UGUI的UI布局基本和NGUI保持一致,所不同的是Scrollview的子物體添加的腳本不一樣,所需要的腳本及組件如下圖所示:
然后,還有需要注意的一點(diǎn)是,在Scrollview上的參數(shù)配置上,我們需要把InputType這個(gè)屬性調(diào)整為UGUI Input
曲線設(shè)置和子物體數(shù)組設(shè)置和NGUI一樣,這里就不再重復(fù)了,配置完這些操作以后,運(yùn)行,UGUI也能實(shí)現(xiàn)一樣的卡牌滾動(dòng)效果:
以上,感謝Github。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
c# EPPlus秘籍之Excel實(shí)現(xiàn)圖表導(dǎo)出
這篇文章主要為大家介紹了c# EPPlus秘籍之Excel實(shí)現(xiàn)圖表導(dǎo)出示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12unity 如何使用LineRenderer 動(dòng)態(tài)劃線
這篇文章主要介紹了unity 使用LineRenderer 動(dòng)態(tài)劃線的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-04-04C#實(shí)現(xiàn)基于加減按鈕形式控制系統(tǒng)音量及靜音的方法
這篇文章主要介紹了C#實(shí)現(xiàn)基于加減按鈕形式控制系統(tǒng)音量及靜音的方法,涉及C#引用user32.dll動(dòng)態(tài)鏈接庫操作系統(tǒng)音量的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10