Unity3D UGUI实现翻书效果

提示:素材来源网络,侵权必删

UI翻书效果

  • 效果图
  • 一、准备工作
    • 1.新建Unity工程
    • 2.新建Hierarchy面板
  • 二、使用步骤
    • 1.新建UIBook脚本,挂在UIBook上
    • 2.新建BookModels脚本
    • 3.新建DragButton脚本
    • 4.新建DragLeftPage脚本
    • 5.新建DragRightPage脚本
    • 6.新建IDragPage脚本
    • 7.新建Page脚本
    • 8.新建Shadow脚本
    • 9.新建TheDraggingPage脚本
  • 3.总结


效果图

提示:以下是本篇文章正文内容,下面案例可供参考

一、准备工作

1.新建Unity工程

准备几张图片,新建一个文件夹放进去,然后把文件夹放在Resources文件夹下

2.新建Hierarchy面板

如图
Unity3D UGUI实现翻书效果_第1张图片

二、使用步骤

1.新建UIBook脚本,挂在UIBook上

代码如下(示例):

using System;
using System.Collections;
using UnityEngine;

namespace HKZ
{
     
    public class UIBook : MonoBehaviour
    {
     
        //组件
        private RectTransform rect;
        private Sprite[] bookSprites;//存放所以的图片(书页)
        private RectTransform clippingMask;

        //引用
        /// 
        /// 正在翻页的书页的左面
        /// 
        private TheDraggingPage leftSideOfPage;
        /// 
        /// 正在翻页的书页的右面
        /// 
        private TheDraggingPage rightSideOfPage;
        /// 
        /// 左侧静止页面
        /// 
        private Page leftPage;
        /// 
        /// 右侧静止页面
        /// 
        private Page rightPage;
        private DragPageBase dragPage;
        /// 
        /// 数据类
        /// 
        private BookModels bookModels;

        //数据
        private int currentLeftId;//当前左边页数
        private float aniDuration = 0.5f;
        private bool isDragging;

        public int CurrentLeftId
        {
     
            get
            {
     
                return currentLeftId;
            }

            set
            {
     
                currentLeftId = value;

                if (currentLeftId < -1)
                {
     
                    currentLeftId = -1;
                }
                else if (currentLeftId > bookSprites.Length - 1)
                {
     
                    currentLeftId = bookSprites.Length - 1;
                }
            }
        }
        private void Start()
        {
     
            Canvas canvas;

            InitAddComponent(out canvas);//初始化     

            InitDate(canvas);

            UpdateID(isDragging);
        }
        /// 
        /// 初始化,找到子项并添加相应的脚本(正在翻页,静态页面)
        /// 
        private void InitAddComponent(out Canvas canvas)
        {
     
            rect = GetComponent<RectTransform>();//获取组件
            canvas = null;
            foreach (Canvas c in rect.GetComponentsInParent<Canvas>())
            {
     
                if (c.isRootCanvas)
                {
     
                    canvas = c;
                    break;
                }
            }

            clippingMask = rect.Find("ClippingMask").GetComponent<RectTransform>();
            rightPage = rect.Find("RightPage").gameObject.AddComponent<Page>();
            leftPage = rect.Find("LeftPage").gameObject.AddComponent<Page>();
            rightSideOfPage = rect.Find("RightSide").gameObject.AddComponent<TheDraggingPage>();
            leftSideOfPage = rect.Find("LeftSide").gameObject.AddComponent<TheDraggingPage>();

            rect.Find("RightDragButton").gameObject.AddComponent<DragButton>().
                Init(OnMouseDragRightPage, OnUpdatePage, OnEndDragRightPage);
            rect.Find("LeftDragButton").gameObject
                      .AddComponent<DragButton>()
                      .Init(OnMouseDragLeftPage, OnUpdatePage, OnEndDragLeftPage);

            rightPage.Init(GetSprite);
            leftPage.Init(GetSprite);
            rightSideOfPage.Init(GetSprite);
            leftSideOfPage.Init(GetSprite);
        }

        private Sprite GetSprite(int index)
        {
     
            if (index>=0&&index<bookSprites.Length)
            {
     
                return bookSprites[index];
            }
            return null;
        }

        /// 
        /// 初始化数据(加载图片,设置整个UIBook大小)
        /// 
        private void InitDate(Canvas canvas)
        {
     
            bookModels = new BookModels();//创建并初始化

            bookSprites = Resources.LoadAll<Sprite>("Book");//加载所有的图片(书页)

            if (bookSprites.Length > 0)
            {
     
                rect.sizeDelta = new Vector2(bookSprites[0].rect.width * 2, bookSprites[0].rect.height);//设置UIBook大小
            }

            CurrentLeftId = -1;
            isDragging = false;

            float scaleFactor = 1;//默认图片缩放比例为1
            if (canvas != null)
            {
     
                scaleFactor = canvas.scaleFactor;
            }
            //计算屏幕上书页的显示尺寸
            float pageWidth = rect.rect.width * scaleFactor / 2; ;//书页宽度
            float pageHeight = rect.rect.height * scaleFactor;//书页高度

            //底边中心点的获取
            Vector3 pos = rect.position + Vector3.down * pageHeight / 2;
            bookModels.BottomCenter = World2LocalPos(pos);
            //右下角坐标
            pos = rect.position + Vector3.down * pageHeight / 2 + Vector3.right * pageWidth;
            bookModels.RightCorner = World2LocalPos(pos);
            //上边中心点的获取
            pos = rect.position + Vector3.up * pageHeight / 2;
            bookModels.TopCenter = World2LocalPos(pos);
            //左下角坐标的获取
            pos = rect.position + Vector3.down * pageHeight / 2 + Vector3.left * pageWidth;
            bookModels.LeftCorner = World2LocalPos(pos);

            float width = rect.rect.width / 2;
            float height = rect.rect.height;
            //每页的宽度
            bookModels.PageWidth = width;
            //对角线的计算
            bookModels.PageDiagonal = Mathf.Sqrt(Mathf.Pow(width, 2) + Mathf.Pow(height, 2));
            //设置ClippingMask的大小
            clippingMask.sizeDelta = new Vector2(bookModels.PageDiagonal, bookModels.PageDiagonal + bookModels.PageWidth);
            //反转切面的轴心点Y值
            bookModels.ClippingPivotY = bookModels.PageWidth / clippingMask.sizeDelta.y;

            leftSideOfPage.InitShadow(new Vector2(bookModels.PageDiagonal, bookModels.PageDiagonal));
            rightSideOfPage.InitShadow(new Vector2(bookModels.PageDiagonal, bookModels.PageDiagonal));
        }

        /// 
        /// 获取clippingMask
        /// 
        /// 
        public RectTransform GetClippingMask()
        {
     
            return clippingMask;
        }
        /// 
        /// 世界坐标转换为local坐标
        /// 
        /// 
        /// 
        private Vector3 World2LocalPos(Vector3 world)
        {
     
            return rect.InverseTransformPoint(world);
        }
        /// 
        /// Local坐标转化世界坐标
        /// 
        /// 
        /// 
        public Vector3 Local2WorldPos(Vector3 local)
        {
     
            return rect.TransformPoint(local);
        }

        private void UpdateID(bool isDragging)
        {
     
            if (isDragging)
            {
     
                leftPage.ID = CurrentLeftId;
                leftSideOfPage.ID = CurrentLeftId + 1;
                rightSideOfPage.ID = CurrentLeftId + 2;
                rightPage.ID = CurrentLeftId + 3;
            }
            else
            {
     
                leftPage.ID = CurrentLeftId;
                rightPage.ID = CurrentLeftId + 1;
            }
        }

        //开始拖动
        private void OnMouseDragRightPage()
        {
     
            if (CurrentLeftId<bookSprites.Length-1)
            {
     
                isDragging = true;
                dragPage = new DragRightPage(this, bookModels, leftSideOfPage, rightSideOfPage, rightPage.transform.position);
                dragPage.BeginDragPage(World2LocalPos(Input.mousePosition));
                UpdateID(isDragging);
            }
        }
        private void OnMouseDragLeftPage()
        {
     
            if (CurrentLeftId > 0)
            {
     
                dragPage = new DragLeftPage(this, bookModels, rightSideOfPage, leftSideOfPage, leftPage.transform.position);
                dragPage.BeginDragPage(World2LocalPos(Input.mousePosition));
                isDragging = true;
                CurrentLeftId -= 2;
                UpdateID(isDragging);
            }
        }
        //持续拖动
        private void OnUpdatePage()
        {
     
            if (isDragging)
            {
     
                dragPage.UpdateDrag();
            }
        }
        //结束拖动
        private void OnEndDragRightPage()
        {
     
            if (isDragging)
            {
     
                isDragging = false;
                bool isLeft = JudgeCornerIsLeft();
                dragPage.EndDragPage(() =>AniEnd(isLeft));
            }
        }
        //结束拖动
        private void OnEndDragLeftPage()
        {
     
            if (isDragging)
            {
     
                isDragging = false;
                bool isLeft = JudgeCornerIsLeft();
                dragPage.EndDragPage(() => AniEnd(isLeft));
            }
        }

        private bool JudgeCornerIsLeft()
        {
     
            return bookModels.CurrentPageCorner.x < bookModels.BottomCenter.x;
        }

        private void AniEnd(bool isLeft)
        {
     
            if (isLeft)
            {
     
                CurrentLeftId += 2;
            }
        }

        /// 
        /// 获取点击位置
        /// 
        /// 
        public Vector3 GetClickPos()
        {
     
            if (isDragging)
            {
     
                return World2LocalPos(Input.mousePosition);
            }
            else
            {
     
                return bookModels.ClickPoint;
            }
        }
        /// 
        /// 计算拖拽角的位置
        /// 
        /// 
        public Vector3 CulculateDraggingCorner(Vector3 click)
        {
     
            Vector3 corner = Vector3.zero;

            corner = LimitBotomCenter(click);

            return LimitTopCenter(corner);
        }
        /// 
        /// 获取底边中心点
        /// 
        /// 
        /// 
        private Vector3 LimitBotomCenter(Vector3 click)
        {
     
            Vector3 offset = click - bookModels.BottomCenter;
            float radians = Mathf.Atan2(offset.y, offset.x);

            Vector3 cornerLimit = new Vector3(
                bookModels.PageWidth * Mathf.Cos(radians)
                , bookModels.PageWidth * Mathf.Sin(radians))
                + bookModels.BottomCenter;

            float distance = Vector2.Distance(click, bookModels.BottomCenter);

            if (distance < bookModels.PageWidth)
            {
     
                return click;
            }
            else
            {
     
                return cornerLimit;
            }
        }
        /// 
        /// 获取上边中心点
        /// 
        /// 
        /// 
        private Vector3 LimitTopCenter(Vector3 corner)
        {
     
            Vector3 offset = corner - bookModels.TopCenter;
            float radians = Mathf.Atan2(offset.y, offset.x);

            Vector3 cornerLimit = new Vector3(
               bookModels.PageDiagonal * Mathf.Cos(radians)
               , bookModels.PageDiagonal * Mathf.Sin(radians))
               + bookModels.TopCenter;

            float distance = Vector2.Distance(corner, bookModels.TopCenter);

            if (distance > bookModels.PageDiagonal)
                return cornerLimit;

            return corner;
        }
        /// 
        /// 计算折叠线的角度数
        /// 
        /// 
        /// 
        /// 
        public float CulculateFoldAngle(Vector3 corner, Vector3 bookCorner, out Vector3 bottomCrossPoint)
        {
     
            Vector3 twoCornerCenter = (corner + bookCorner) / 2;
            Vector3 offset = bookCorner - twoCornerCenter;
            float randians = Mathf.Atan2(offset.y, offset.x);
            float offsetX = twoCornerCenter.x - offset.y * Mathf.Tan(randians);
            offsetX = LimitOffsetX(offsetX, bookCorner, bookModels.BottomCenter);
            bottomCrossPoint = new Vector3(offsetX, bookModels.BottomCenter.y);

            offset = bottomCrossPoint - twoCornerCenter;
            return Mathf.Atan(offset.y / offset.x) * Mathf.Rad2Deg; //randians to degress
        }

        private float LimitOffsetX(float offsetX, Vector3 bookCorner, Vector3 bottomCenter)
        {
     
            if (offsetX > bottomCenter.x && bottomCenter.x > bookCorner.x)
                return bottomCenter.x;

            if (offsetX < bottomCenter.x && bottomCenter.x < bookCorner.x)
                return bottomCenter.x;

            return offsetX;
        }

        public void FlipAni(Vector3 target, Action onComplete)
        {
     
            StartCoroutine(PageAni(target, aniDuration, ()=>
            {
     
                if (onComplete!=null)
                {
     
                    onComplete();
                }
                ResetDate();
            }));
        }

        private void ResetDate()
        {
     
            UpdateID(isDragging);
            leftSideOfPage.SetActiveState(false);
            rightSideOfPage.SetActiveState(false);
        }

        private IEnumerator PageAni(Vector3 target,float duration,Action onComplete)
        {
     
            Vector3 offset = (target - bookModels.ClickPoint) / duration;
            float symbol = (target - bookModels.ClickPoint).x;
            yield return new WaitUntil(() =>
            {
     
                bookModels.ClickPoint += offset * Time.deltaTime;
                dragPage.UpdateDrag();
                if (symbol>0)
                {
     
                    return bookModels.ClickPoint.x >= target.x;
                }
                else
                {
     
                    return bookModels.ClickPoint.x <= target.x;
                }
            });
            if (onComplete!=null)
            {
     
                onComplete();
            }
        }
    }
}

2.新建BookModels脚本

代码如下(示例):

using UnityEngine;

namespace HKZ
{
     
    public class BookModels
    {
            
        /// 
        /// 一页的宽度
        /// 
        public float PageWidth;
        /// 
        /// 对角线
        /// 
        public float PageDiagonal;
        /// 
        /// 底边中心点
        /// 
        public Vector3 BottomCenter;
        /// 
        /// 顶点中心点
        /// 
        public Vector3 TopCenter;
        /// 
        /// 当前被拖动的书页顶点位置
        /// 
        public Vector3 CurrentPageCorner;
        /// 
        /// 右边书页右下角顶点
        /// 
        public Vector3 RightCorner;
        /// 
        /// 左边书页右下角顶点
        /// 
        public Vector3 LeftCorner;
        /// 
        /// 当前点击的点
        /// 
        public Vector3 ClickPoint;
        /// 
        /// 反转切面的轴心点Y值
        /// 
        public float ClippingPivotY;
    }
}

3.新建DragButton脚本

代码如下(示例):

using System;
using UnityEngine;
using UnityEngine.EventSystems;

namespace HKZ
{
     
    public class DragButton : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
    {
     
        private Action onBeginDrag;
        private Action onDrag;
        private Action onEndDrag;

        /// 
        /// 初始化数据
        /// 
        /// 
        /// 
        /// 
        public void Init(Action onBeginDrag, Action onDrag, Action onEndDrag)
        {
     
            this.onBeginDrag = onBeginDrag;
            this.onDrag = onDrag;
            this.onEndDrag = onEndDrag;
        }
        /// 
        /// 开始拖动
        /// 
        /// 
        public void OnBeginDrag(PointerEventData eventData)
        {
     
            if (onBeginDrag!=null)
            {
     
                onBeginDrag();
            }
        }
        /// 
        /// 持续拖动
        /// 
        /// 
        public void OnDrag(PointerEventData eventData)
        {
     
            if (onDrag != null)
            {
     
                onDrag();
            }
        }
        /// 
        /// 结束拖动
        /// 
        /// 
        public void OnEndDrag(PointerEventData eventData)
        {
     
            if (onEndDrag!=null)
            {
     
                onEndDrag();
            }
        }
    }
}

4.新建DragLeftPage脚本

代码如下(示例):

using UnityEngine;

namespace HKZ
{
     
    public class DragLeftPage : DragPageBase
    {
     
        public DragLeftPage(UIBook uIBook, BookModels bookModels, TheDraggingPage frontPage, TheDraggingPage backPage, Vector3 startPos)
            : base(uIBook, bookModels, frontPage, backPage, startPos)
        {
     

        }

        protected override Vector3 GetBookCorner()
        {
     
            return bookModels.LeftCorner;
        }

        protected override Vector2 GetCilppingMaskPivot()
        {
     
            return new Vector2(0, bookModels.ClippingPivotY);
        }

        protected override Vector2 GetPagePivot()
        {
     
            return Vector2.right;
        }

        protected override Vector3 GetValidAngle(float angle)
        {
     
            return Vector3.forward * (angle + 180);
        }
    }
}

5.新建DragRightPage脚本

代码如下(示例):

using UnityEngine;

namespace HKZ
{
     
    public class DragRightPage : DragPageBase
    {
     
        public DragRightPage(UIBook uIBook, BookModels bookModels, TheDraggingPage frontPage, TheDraggingPage backPage, Vector3 startPos)
            : base(uIBook, bookModels, frontPage, backPage, startPos)
        {
     

        }

        protected override Vector3 GetBookCorner()
        {
     
            return bookModels.RightCorner;
        }

        protected override Vector2 GetCilppingMaskPivot()
        {
     
            return new Vector2(1, bookModels.ClippingPivotY);
        }

        protected override Vector2 GetPagePivot()
        {
     
            return Vector2.zero;
        }

        protected override Vector3 GetValidAngle(float angle)
        {
     
            return Vector3.forward * angle;
        }
    }
}

6.新建IDragPage脚本

代码如下(示例):

using System;
using UnityEngine;

namespace HKZ
{
     
    public interface IDragPage
    {
     
        void BeginDragPage(Vector3 point);

        void UpdateDrag();

        void EndDragPage(Action complete);
    }

    public abstract class DragPageBase
    {
     
        private UIBook uIBook;
        protected BookModels bookModels;
        private TheDraggingPage frontPage;
        private TheDraggingPage backPage;
        private Vector3 startPos;
        private RectTransform clippingMask;

        public DragPageBase(UIBook uIBook, BookModels bookModels,
            TheDraggingPage frontPage, TheDraggingPage backPage, Vector3 startPos)
        {
     
            this.uIBook = uIBook;
            this.bookModels = bookModels;
            this.frontPage = frontPage;
            this.backPage = backPage;
            this.startPos = startPos;
            clippingMask = uIBook.GetClippingMask();
        }
        /// 
        /// 开始拖拽
        /// 
        /// 
        public void BeginDragPage(Vector3 point)
        {
     
            //设置中心点
            clippingMask.pivot = GetCilppingMaskPivot();

            bookModels.ClickPoint = point;

            frontPage.BeginDragPage(startPos, GetPagePivot());
            backPage.BeginDragPage(startPos, GetPagePivot());
        }
        protected abstract Vector2 GetCilppingMaskPivot();

        protected abstract Vector2 GetPagePivot();

        /// 
        /// 拖拽过程
        /// 
        public void UpdateDrag()
        {
     
            bookModels.ClickPoint = uIBook.GetClickPos();

            backPage.SetParent(clippingMask, true);
            frontPage.SetParent(uIBook.transform, true);

            bookModels.CurrentPageCorner = uIBook.CulculateDraggingCorner(bookModels.ClickPoint);

            Vector3 bottomCrossPoint = UpdateClippingMask();

            UpdateBackSide(bottomCrossPoint);

            frontPage.SetParent(clippingMask, true);
            frontPage.ResetShadowDate();
            frontPage.transform.SetAsFirstSibling();

            backPage.SetShadowFollow(clippingMask);
        }

        private Vector3 UpdateClippingMask()
        {
     
            Vector3 bottomCrossPoint;
            float angle = uIBook.CulculateFoldAngle(bookModels.CurrentPageCorner, GetBookCorner(), out bottomCrossPoint);
            if (angle > 0)
            {
     
                angle = angle - 90;
            }
            else
            {
     
                angle = angle + 90;
            }

            clippingMask.eulerAngles = Vector3.forward * angle;
            clippingMask.localPosition = bottomCrossPoint;

            return bottomCrossPoint;
        }

        protected abstract Vector3 GetBookCorner();

        private void UpdateBackSide(Vector3 bottonCrossPoint)
        {
     
            backPage.transform.position = uIBook.Local2WorldPos(bookModels.CurrentPageCorner);
            Vector3 offset = bottonCrossPoint - bookModels.CurrentPageCorner;

            float angle = Mathf.Atan2(offset.y, offset.x) * Mathf.Rad2Deg;
            backPage.transform.eulerAngles = GetValidAngle(angle);
        }

        protected abstract Vector3 GetValidAngle(float angle);

        public void EndDragPage(Action complete)
        {
     
            Vector3 corner;
            if (bookModels.CurrentPageCorner.x>bookModels.BottomCenter.x)
            {
     
                corner = bookModels.RightCorner;
            }
            else
            {
     
                corner = bookModels.LeftCorner;
            }
            uIBook.FlipAni(corner, complete);
        }
    }
}

7.新建Page脚本

代码如下(示例):

using System;
using UnityEngine;
using UnityEngine.UI;

namespace HKZ
{
     
    public class Page : MonoBehaviour
    {
     
        //引用
        public Shadow Shadow
        {
     
            get;
            private set;
        }
        //组件
        private Func<int, Sprite> getSprite;
        private Image img_Page;
        protected RectTransform rect;
        //数据
        /// 
        /// 页数
        /// 
        private int id;
        public int ID
        {
     
            get
            {
     
                return id;
            }
            set
            {
     
                id = value;
                ChangeSprite(value);
            }
        }

        /// 
        /// 初始化数据
        /// 
        /// 
        public virtual void Init(Func<int,Sprite> getSprite)
        {
     
            rect = GetComponent<RectTransform>();
            this.getSprite = getSprite;
            img_Page = GetComponent<Image>();
            Shadow = transform.GetChild(0).gameObject.AddComponent<Shadow>();
        }
        /// 
        /// 赋值Sprite
        /// 
        /// 
        private void ChangeSprite(int id)
        {
     
            img_Page.sprite = getSprite(id);
        }
        /// 
        /// 设置活跃状态
        /// 
        /// 
        public void SetActiveState(bool isActive)
        {
     
            gameObject.SetActive(isActive);
        }
        /// 
        /// 设置父物体
        /// 
        /// 
        /// 
        public void SetParent(Transform parent,bool worldPosStays)
        {
     
            transform.SetParent(parent, worldPosStays);
        }
    }
}

8.新建Shadow脚本

代码如下(示例):

using UnityEngine;
using UnityEngine.UI;

namespace HKZ
{
     
    public class Shadow : MonoBehaviour
    {
     
        //组件
        private RectTransform rect;
        private Image image;
        private Color defaultColor = new Color(1, 1, 1, 0.5f);
        /// 
        /// 初始化数据
        /// 
        /// 
        public void Init(Vector2 size)
        {
     
            rect = GetComponent<RectTransform>();
            image = GetComponent<Image>();
            rect.sizeDelta = size;//设置大小
            image.color = defaultColor;//设置颜色
        }
        /// 
        /// 设置shadow的位置
        /// 
        /// 
        public void SetShadowFollow(Transform target)
        {
     
            transform.position = target.position;
            transform.rotation = target.rotation;
        }

        public void ResetShadowDate()
        {
     
            rect.anchoredPosition = Vector2.zero;
            rect.localEulerAngles = Vector2.zero;
        }
    }
}

9.新建TheDraggingPage脚本

代码如下(示例):

using System;
using UnityEngine;

namespace HKZ
{
     
    public class TheDraggingPage : Page
    {
     
        public override void Init(Func<int, Sprite> getSprite)
        {
     
            base.Init(getSprite);
            SetActiveState(false);
        }
        /// 
        /// 开始拖动
        /// 
        /// 
        /// 
        public void BeginDragPage(Vector3 pos,Vector2 pivot)
        {
     
            SetActiveState(true);
            rect.pivot = pivot;
            transform.position = pos;
            
            transform.eulerAngles = Vector3.zero;
        }
        /// 
        /// 初始化Shadow的大小
        /// 
        /// 
        public void InitShadow(Vector2 size)
        {
     
            Shadow.Init(size);
        }
        /// 
        /// 让shadow跟随书页
        /// 
        /// 
        public void SetShadowFollow(Transform target)
        {
     
            Shadow.SetShadowFollow(target);
        }

        public void ResetShadowDate()
        {
     
            Shadow.ResetShadowDate();
        }
    }
}

完结,如何?,翻书效果不错吧,是不是想打赏我点辛苦费


3.总结

在这里插入图片描述

你可能感兴趣的:(UGUI实现翻书效果,笔记,unity3d,ugui,c#)