1. 程式人生 > >unity實現卡牌翻動效果

unity實現卡牌翻動效果

事實上這是專案需要,我改的一個程式碼,實際上就是利用unity的一些基礎屬性實現其效果。啥也不多說了,先上原始碼:

/// Credit Mrs. YakaYocha 
/// Sourced from - https://www.youtube.com/channel/UCHp8LZ_0-iCvl-5pjHATsgw
/// Please donate: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RJ8D9FRFQF9VS

using UnityEngine.Events;

namespace UnityEngine.UI.Extensions
{
    [RequireComponent(typeof(ScrollRect))]
    [AddComponentMenu("Layout/Extensions/Vertical Scroller")]
    public class UIVerticalScroller : MonoBehaviour
    {
        [Tooltip("Scrollable area (content of desired ScrollRect)")]
        public RectTransform _scrollingPanel;
        [Tooltip("Elements to populate inside the scroller")]
        public GameObject[] _arrayOfElements;
        [Tooltip("Center display area (position of zoomed content)")]
        public RectTransform _center;
        [Tooltip("Select the item to be in center on start. (optional)")]
        public int StartingIndex = -1;
        [Tooltip("Button to go to the next page. (optional)")]
        public GameObject ScrollUpButton;
        [Tooltip("Button to go to the previous page. (optional)")]
        public GameObject ScrollDownButton;
        [Tooltip("Event fired when a specific item is clicked, exposes index number of item. (optional)")]
        public UnityEvent<int> ButtonClicked;


        private float[] distReposition;
        private float[] distance;
        //private int elementsDistance;
        private int minElementsNum;
        private int elementLength;
        //private int elementHalfLength;
        private float deltaY;
        private string result;

        public UIVerticalScroller() { }

        public UIVerticalScroller(RectTransform scrollingPanel, GameObject[] arrayOfElements, RectTransform center)
        {
            _scrollingPanel = scrollingPanel;
            _arrayOfElements = arrayOfElements;
            _center = center;
        }


        public void Awake()
        {
            var scrollRect = GetComponent<ScrollRect>();
            if (!_scrollingPanel)
            {
                _scrollingPanel = scrollRect.content;
            }
            if (!_center)
            {
                Debug.LogError("Please define the RectTransform for the Center viewport of the scrollable area");
            }
            if (_arrayOfElements == null || _arrayOfElements.Length == 0)
            {
                var childCount = scrollRect.content.childCount;
                if (childCount > 0)
                {
                    _arrayOfElements = new GameObject[childCount];
                    for (int i = 0; i < childCount; i++)
                    {
                        _arrayOfElements[i] = scrollRect.content.GetChild(i).gameObject;
                    }                    
                }
            }
        }

        public void Start()
        {
            if (_arrayOfElements.Length < 1)
            {
                Debug.Log("No child content found, exiting..");
                return;
            }

            elementLength = _arrayOfElements.Length;
            distance = new float[elementLength];
            distReposition = new float[elementLength];

            //get distance between buttons
            //elementsDistance = (int)Mathf.Abs(_arrayOfElements[1].GetComponent<RectTransform>().anchoredPosition.y - _arrayOfElements[0].GetComponent<RectTransform>().anchoredPosition.y);
            deltaY = _arrayOfElements[0].GetComponent<RectTransform>().rect.height * elementLength / 3 * 2;
            Vector2 startPosition = new Vector2(_scrollingPanel.anchoredPosition.x, -deltaY);
            _scrollingPanel.anchoredPosition = startPosition;

            for (var i = 0; i < _arrayOfElements.Length; i++)
            {
                AddListener(_arrayOfElements[i], i);
            }

            if (ScrollUpButton)
                ScrollUpButton.GetComponent<Button>().onClick.AddListener(() => { ScrollUp(); });

            if (ScrollDownButton)
                ScrollDownButton.GetComponent<Button>().onClick.AddListener(() => { ScrollDown(); });

            if (StartingIndex > -1)
            {
                StartingIndex = StartingIndex > _arrayOfElements.Length ? _arrayOfElements.Length - 1 : StartingIndex;
                SnapToElement(StartingIndex);
            }
        }

        private void AddListener(GameObject button, int index)
        {
            button.GetComponent<Button>().onClick.AddListener(() => DoSomething(index));
        }

        private void DoSomething(int index)
        {
            if (ButtonClicked != null)
            {
                ButtonClicked.Invoke(index);
            }
        }

        public void Update()
        {
            if (_arrayOfElements.Length < 1)
            {
                return;
            }

            for (var i = 0; i < elementLength; i++)
            {
                distReposition[i] = _center.GetComponent<RectTransform>().position.y - _arrayOfElements[i].GetComponent<RectTransform>().position.y;
                distance[i] = Mathf.Abs(distReposition[i]);

                //Magnifying effect
                float scale = Mathf.Max(0.7f, 1 / (1 + distance[i] / 200));
                _arrayOfElements[i].GetComponent<RectTransform>().transform.localScale = new Vector3(scale, scale, 1f);
            }
            float minDistance = Mathf.Min(distance);

            for (var i = 0; i < elementLength; i++)
            {
                _arrayOfElements[i].GetComponent<CanvasGroup>().interactable = false;
                if (minDistance == distance[i])
                {
                    minElementsNum = i;
                    _arrayOfElements[i].GetComponent<CanvasGroup>().interactable = true;
                    result = _arrayOfElements[i].GetComponentInChildren<Text>().text;
                }
            }

            ScrollingElements(-_arrayOfElements[minElementsNum].GetComponent<RectTransform>().anchoredPosition.y);
        }

        private void ScrollingElements(float position)
        {
            float newY = Mathf.Lerp(_scrollingPanel.anchoredPosition.y, position, Time.deltaTime * 1f);
            Vector2 newPosition = new Vector2(_scrollingPanel.anchoredPosition.x, newY);
            _scrollingPanel.anchoredPosition = newPosition;
        }

        public string GetResults()
        {
            return result;
        }

        public void SnapToElement(int element)
        {
            float deltaElementPositionY = _arrayOfElements[0].GetComponent<RectTransform>().rect.height * element;
            Vector2 newPosition = new Vector2(_scrollingPanel.anchoredPosition.x, -deltaElementPositionY);
            _scrollingPanel.anchoredPosition = newPosition;

        }

        public void ScrollUp()
        {
            float deltaUp = _arrayOfElements[0].GetComponent<RectTransform>().rect.height / 1.2f;
            Vector2 newPositionUp = new Vector2(_scrollingPanel.anchoredPosition.x, _scrollingPanel.anchoredPosition.y - deltaUp);
            _scrollingPanel.anchoredPosition = Vector2.Lerp(_scrollingPanel.anchoredPosition, newPositionUp, 1);
        }

        public void ScrollDown()
        {
            float deltaDown = _arrayOfElements[0].GetComponent<RectTransform>().rect.height / 1.2f;
            Vector2 newPositionDown = new Vector2(_scrollingPanel.anchoredPosition.x, _scrollingPanel.anchoredPosition.y + deltaDown);
            _scrollingPanel.anchoredPosition = newPositionDown;
        }
    }
}

原始碼是上下滑動的,再上我改過之後的程式碼,左右滑動的;

/// Credit Mrs. YakaYocha 
/// Sourced from - https://www.youtube.com/channel/UCHp8LZ_0-iCvl-5pjHATsgw
/// Please donate: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RJ8D9FRFQF9VS

using UnityEngine.Events;

namespace UnityEngine.UI.Extensions
{
    [RequireComponent(typeof(ScrollRect))]
    [AddComponentMenu("Layout/Extensions/Vertical Scroller")]
    public class UIVerticalScrollerMove : MonoBehaviour
    {
        [Tooltip("Scrollable area (content of desired ScrollRect)")]
        public RectTransform _scrollingPanel;//展示面板
        [Tooltip("Elements to populate inside the scroller")]
        public GameObject[] _arrayOfElements;//長度元素
        [Tooltip("Center display area (position of zoomed content)")]
        public RectTransform _center;//位置
        [Tooltip("Select the item to be in center on start. (optional)")]
        public int StartingIndex = -1;//初始指標(外界提供)
        [Tooltip("Button to go to the next page. (optional)")]
        public GameObject ScrollLeftButton;//左按鈕
        [Tooltip("Button to go to the previous page. (optional)")]
        public GameObject ScrollRightButton;//右按鈕
        [Tooltip("Event fired when a specific item is clicked, exposes index number of item. (optional)")]
        public UnityEvent<int> ButtonClicked;//按鈕點選


        private float[] distReposition;//長度改變
        private float[] distance;//長度列表
        //private int elementsDistance;
        private int minElementsNum;//最小元素數
        private int elementLength;//元素長度
        //private int elementHalfLength;
        private float deltaX;//移動x
        private string result;//結果

        public UIVerticalScrollerMove() { }//建構函式

        public UIVerticalScrollerMove(RectTransform scrollingPanel, GameObject[] arrayOfElements, RectTransform center)
        {
            _scrollingPanel = scrollingPanel;
            _arrayOfElements = arrayOfElements;
            _center = center;
        }


        //初始化啟動
        public void Awake()
        {
            var scrollRect = GetComponent<ScrollRect>();//獲取到排列
            if (!_scrollingPanel)
            {
                _scrollingPanel = scrollRect.content;//如果不是展示面板,獲取該物體的可滾動的面板
            }
            if (!_center)//如果設定不成功,列印失敗
            {
                Debug.LogError("Please define the RectTransform for the Center viewport of the scrollable area");
            }
            if (_arrayOfElements == null || _arrayOfElements.Length == 0)
            {
                var childCount = scrollRect.content.childCount;
                if (childCount > 0)
                {
                    _arrayOfElements = new GameObject[childCount];
                    for (int i = 0; i < childCount; i++)
                    {
                        _arrayOfElements[i] = scrollRect.content.GetChild(i).gameObject;
                    }                    
                }
            }//獲取子物體的長度
        }

        //初始化啟動
        public void Start()
        {
            if (_arrayOfElements.Length < 1)
            {
                Debug.Log("No child content found, exiting..");
                return;
            }//沒有子物體的時候,列印尋找失敗

            elementLength = _arrayOfElements.Length;
            distance = new float[elementLength];
            distReposition = new float[elementLength];//通過子物體的長度定義距離長度列表與移動長度列表

            //get distance between buttons
            //elementsDistance = (int)Mathf.Abs(_arrayOfElements[1].GetComponent<RectTransform>().anchoredPosition.y - _arrayOfElements[0].GetComponent<RectTransform>().anchoredPosition.y);
            deltaX = _arrayOfElements[0].GetComponent<RectTransform>().rect.width * elementLength / 3 * 2;
            Vector2 startPosition = new Vector2( -deltaX,_scrollingPanel.anchoredPosition.y);
            _scrollingPanel.anchoredPosition = startPosition;//獲取到更改的按鈕

            for (var i = 0; i < _arrayOfElements.Length; i++)
            {
                AddListener(_arrayOfElements[i], i);
            }//監聽每個按鈕上掛載的方法

            //如果左右按鈕的話,分別監聽不同的方法
            if (ScrollLeftButton)
                ScrollLeftButton.GetComponent<Button>().onClick.AddListener(() => { ScrollLeft(); });

            if (ScrollRightButton)
                ScrollRightButton.GetComponent<Button>().onClick.AddListener(() => { ScrollRight(); });
            
            //比較外界提供的初始指標並進行初始定位
            if (StartingIndex > -1)
            {
                StartingIndex = StartingIndex > _arrayOfElements.Length ? _arrayOfElements.Length - 1 : StartingIndex;
                SnapToElement(StartingIndex);
            }
        }

        //讓該物體監聽到自己所對應的事件
        private void AddListener(GameObject button, int index)
        {
            button.GetComponent<Button>().onClick.AddListener(() => DoSomething(index));
        }

        //index按鈕對應的點選狀態
        private void DoSomething(int index)
        {
            if (ButtonClicked != null)
            {
                ButtonClicked.Invoke(index);
            }
        }

        //邏輯更新
        public void Update()
        {
            if (_arrayOfElements.Length < 1)
            {
                return;
            }//子物體為空的時候返回

            for (var i = 0; i < elementLength; i++)
            {
                distReposition[i] = _center.GetComponent<RectTransform>().position.x - _arrayOfElements[i].GetComponent<RectTransform>().position.x;
                distance[i] = Mathf.Abs(distReposition[i]);

                //Magnifying effect
                float scale = Mathf.Max(0.7f, 1 / (1 + distance[i] / 200));
                _arrayOfElements[i].GetComponent<RectTransform>().transform.localScale = new Vector3(scale, scale, 1f);
            }//不斷更新可滑動面板下面的物體下面的動態數列
            float minDistance = Mathf.Min(distance);//求出最小間距

            for (var i = 0; i < elementLength; i++)
            {
                _arrayOfElements[i].GetComponent<CanvasGroup>().interactable = false;
                if (minDistance == distance[i])
                {
                    minElementsNum = i;
                    _arrayOfElements[i].GetComponent<CanvasGroup>().interactable = true;
                    result = _arrayOfElements[i].GetComponentInChildren<Text>().text;
                }
            }//除了被選中的物體,其餘物體都是不可互動的

            ScrollingElements(-_arrayOfElements[minElementsNum].GetComponent<RectTransform>().anchoredPosition.x);//不斷向著新座標移動
        }

        //不斷移動座標,保證向著目標點移動
        private void ScrollingElements(float position)
        {
            float newX= Mathf.Lerp(_scrollingPanel.anchoredPosition.x, position, Time.deltaTime * 1f);
            Vector2 newPosition = new Vector2(newX,_scrollingPanel.anchoredPosition.y);
            _scrollingPanel.anchoredPosition = newPosition;
        }

        public string GetResults()
        {
            return result;
        }

        //通過指標計算該物體在座標欄下的位置
        public void SnapToElement(int element)
        {
            float deltaElementPositionX = _arrayOfElements[0].GetComponent<RectTransform>().rect.width * element;
            Vector2 newPosition = new Vector2(-deltaElementPositionX,_scrollingPanel.anchoredPosition.y);
            _scrollingPanel.anchoredPosition = newPosition;
        }

        //左右滑動
        public void ScrollLeft()
        {
            float deltaLeft = _arrayOfElements[0].GetComponent<RectTransform>().rect.width / 1.2f;
            Vector2 newPositionLeft = new Vector2(_scrollingPanel.anchoredPosition.x-deltaLeft, _scrollingPanel.anchoredPosition.y);
            _scrollingPanel.anchoredPosition = Vector2.Lerp(_scrollingPanel.anchoredPosition,newPositionLeft, 1);
        }

        public void ScrollRight()
        {
            float deltaRight = _arrayOfElements[0].GetComponent<RectTransform>().rect.width / 1.2f;
            Vector2 newPositionRight = new Vector2(_scrollingPanel.anchoredPosition.x+deltaRight, _scrollingPanel.anchoredPosition.y);
            _scrollingPanel.anchoredPosition = newPositionRight;
        }
    }
}

這是可外掛裡面的類庫,不過核心邏輯可以用Unity來重寫,以上都有註釋。

最後是引用方法:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.UI.Extensions;

public class ScrollingCalendarTest : MonoBehaviour {
	public RectTransform monthsScrollingPanel;
	public GameObject monthsButtonPrefab;
    private GameObject[] monthsButtons;
	public RectTransform monthCenter;
	
	private int monthsSet;
	
	UIVerticalScrollerMove monthsVerticalScroller;
	//Initialize Months
	//生成預製體
	private void InitializeMonths()
	{
		int[] months = new int[12];

		monthsButtons = new GameObject[months.Length];
		for (int i = 0; i < months.Length; i++)
		{
			string month = "";
			months[i] = i;

			GameObject clone = (GameObject)Instantiate(monthsButtonPrefab, new Vector3(i * 380,0,  0), Quaternion.Euler(new Vector3(0, 0, 0))) as GameObject;
			clone.transform.SetParent(monthsScrollingPanel, false);
			clone.transform.localScale = new Vector3(1, 1, 1);

			month = ""+i;

			clone.GetComponentInChildren<Text>().text = month;
			clone.name = "Month_" + months[i];
			clone.AddComponent<CanvasGroup>();
			monthsButtons[i] = clone;
		}
	}
	// Use this for initialization
        public void Awake()
        {
            InitializeMonths();

            //Yes Unity complains about this but it doesn't matter in this case.
            monthsVerticalScroller = new UIVerticalScrollerMove(monthsScrollingPanel, monthsButtons, monthCenter);

            monthsVerticalScroller.Start();
        }

        public void SetDate()
        {
//            monthsSet = int.Parse(inputFieldMonths.text) - 1;

            monthsVerticalScroller.SnapToElement(monthsSet);
        }

        void Update()
        {
            monthsVerticalScroller.Update();

            string monthString = monthsVerticalScroller.GetResults();

        }


        public void MonthsScrollUp()
        {
            monthsVerticalScroller.ScrollLeft();
        }

        public void MonthsScrollDown()
        {
            monthsVerticalScroller.ScrollRight();
        }

}

效果與引用: