Unity3D问题之EnhanceScollView选择角色3D循环滚动效果实现

原文章:https://blog.csdn.net/damenhanter/article/details/50622396

需求

  • 呈现3D效果(2D素材)选择角色效果
  • 滚动保证层级,缩放比例,间距正常跟随
  • 循环滚动
  • 这个界面需求一般也会有游戏会采用(貌似有挺多)

如何实现

实现技术关键点

(3D循环效果,根据数学函数和仔细研究下具体效果很容易通过计算方式直接实现,本文主要目的是介绍下AnimationCurve工具,通过这个工具能够帮助我们实现一些需求当然也包括实现酷炫的3D滚动效果)

  1. 如何控制每个Item之间的间隔(位置),缩放比例,差值平滑
  2. 如何实现item层级关系正确显示("离" 屏幕近的层级高)
  3. 如何实现循环滚动

下面一一讲述当前Demo采用的方法
说到实现的核心,需要知道Unity3D中提供的一个叫做AnimationCurve的组件,这个不仅仅是表面上美术可以使用的组件,也不只是单纯的动画曲线的概念,当然它就是动画曲线,但是我们可以赋予AnimationCurve不同的意义,则可以借助Curve实现不同的功能,(AnimationCurve定义了一个变化趋势或者曲线,在不同的时间点,我们可以得到当前时间点下该曲线对应的y轴信息,这个信息可以是角色跳跃的高度,模型缩放的一个系数,摄像机距离目标的长度,一个角色当前的心情数值等等,曲线可以表示很多的意义)
没用过AnimationCurve的朋友,直接去官网看下介绍就明白如何使用
下面简单说下使用AnimationCurve可以完成的一些功能(上面已经介绍了一部分场景)

  1. 角色2D跳跃
  2. 摄像机移动
  3. 角色心情指数
  4. 缩放系数
  5. 距离系数
  6. ......

我们也赋予AnimationCurve不同的意义,实现我们核心目标(控制位移,控制缩放 当然也可以控制层级)

控制位移,缩放(3D效果的关键),差值过度动画平滑

  1. 创建两个AnimationCurve一个是scaleAnimationCurve和positionXAnimationCurve,分别控制缩放和位移
  2. 时间流水线控制(我们把所有的Item设置好自己对应的时间流位置即可,每次只要一动时间流水线,然后从两个曲线内获得当前流水线对应的缩放系数,位移系数,然后设置item的位移和缩放即可)
  3. 如何制作动画(这个其实就是简单的时间流水线的差值处理,一定时间时间流水值达到目标值即可)
下面放上两张曲线截图和具体实现:


[csharp]  view plain  copy
  1. ///   
  2.     /// 缩放曲线模拟当前缩放值  
  3.     ///   
  4.     private float GetScaleValue(float sliderValue, float added)  
  5.     {  
  6.         float scaleValue = scaleCurve.Evaluate(sliderValue + added);  
  7.         return scaleValue;  
  8.     }  
  9.   
  10.     ///   
  11.     /// 位置曲线模拟当前x轴位置  
  12.     ///   
  13.     private float GetXPosValue(float sliderValue, float added)  
  14.     {  
  15.         float evaluateValue = positionCurve.Evaluate(sliderValue + added) * posCurveFactor;  
  16.         return evaluateValue;  
  17.     }  
  18.   
  19. public void UpdateEnhanceScrollView(float fValue)  
  20.     {  
  21.         for (int i = 0; i < scrollViewItems.Count; i++)  
  22.         {  
  23.             EnhanceItem itemScript = scrollViewItems[i];  
  24.             float xValue = GetXPosValue(fValue, dHorizontalValues[itemScript.scrollViewItemIndex]);  
  25.             float scaleValue = GetScaleValue(fValue, dHorizontalValues[itemScript.scrollViewItemIndex]);  
  26.             itemScript.UpdateScrollViewItems(xValue, yPositionValue, scaleValue);  
  27.         }  
  28.     }  
  29.   
  30.     void Update()  
  31.     {  
  32.         currentDuration += Time.deltaTime;  
  33.         if (currentDuration > duration)  
  34.         {  
  35.             // 更新完毕设置选中item的对象即可  
  36.             currentDuration = duration;  
  37.             if(centerItem != null)  
  38.                 centerItem.SetSelectColor(true);  
  39.             if(preCenterItem != null)  
  40.                 preCenterItem.SetSelectColor(false);  
  41.             canChangeItem = true;  
  42.         }  
  43.   
  44.         SortDepth();  
  45.         float percent = currentDuration / duration;  
  46.         horizontalValue = Mathf.Lerp(originHorizontalValue, horizontalTargetValue, percent);  
  47.         UpdateEnhanceScrollView(horizontalValue);  
  48.     }  

控制层级

只有正确的层级控制,才能够保证"不穿帮",上文也说过,也可以通过AnimationCurve做一个层级曲线,在当前item的时间下面该item的depth或者层级应该是多少,该demo采用的是比较粗暴的list排序方法,按照每个item距离"屏幕的远近"其实就是scale系数,判断哪个item在前,哪个在后面,当然也有些问题,如果距离相同,可能存在item相互打架的可能(这个可以通过控制scaleCurve进行控制)
该Demo使用的UITexture控制层级(其他的任何方式原理一样,只是处理对象不一样,用mesh实现,那就是z轴等等)
具体实现如下:

[csharp]  view plain  copy
  1. public void SortDepth()  
  2.     {  
  3.         textureTargets.Sort(new CompareDepthMethod());  
  4.         for (int i = 0; i < textureTargets.Count; i++)  
  5.             textureTargets[i].depth = i;  
  6.     }  
  7.     ///   
  8.     /// 用于层级对比接口  
  9.     ///   
  10.     public class CompareDepthMethod : IComparer  
  11.     {  
  12.         public int Compare(UITexture left, UITexture right)  
  13.         {  
  14.             if (left.transform.localScale.x > right.transform.localScale.x)  
  15.                 return 1;  
  16.             else if (left.transform.localScale.x < right.transform.localScale.x)  
  17.                 return -1;  
  18.             else  
  19.                 return 0;  
  20.         }  
  21.     }  

实现滚动循环

说道循环滚动,因为我们使用到了AnimationCurve,先天性的动画曲线会有三种模式一种是pingpong,loop,一种是clamp,其中我们需要的是LOOP,没听错这就是滚动循环的关键点(我们的缩放曲线,位移系数曲线从0到1的效果模拟完毕,如果我们继续向前增加时间流水值,那么进入到下一个曲线的时候,所有的item都会反过来进行采样曲线值,就能够巧妙的实现循环效果(缩放系数,位移系数))如果不理解的,可以自己设置一个AnimationCurve,研究下,下面截图示意:

代码部分只是需要知道,如果点击了一个Item将该item移动到中心对应的时间流应该往前或者往后走多少

[csharp]  view plain  copy
  1. ///   
  2. /// 获得当前要移动到中心的Item需要移动的factor间隔数  
  3. ///   
  4. private int GetMoveCurveFactorCount(float targetXPos)  
  5. {  
  6.     int centerIndex = scrollViewItems.Count / 2;  
  7.     for (int i = 0; i < scrollViewItems.Count;i++ )  
  8.     {  
  9.         float factor = (0.5f - dFactor * (centerIndex - i));  
  10.   
  11.         float tempPosX = positionCurve.Evaluate(factor) * posCurveFactor;  
  12.         if (Mathf.Abs(targetXPos - tempPosX) < 0.01f)  
  13.             return Mathf.Abs(i - centerIndex);  
  14.     }  
  15.     return -1;  
  16. }  

注意问题

  1. 制作曲线,记得保证0-1时间轴填充完毕,这样在进行循环处理的时候才不会出现偏差
  2. 额,如果自己用这种方法尝试的朋友,如果有问题,请仔细查看Demo中的参数即可......(主要就是曲线制作问题)

该Demo使用的NGUI,虽然笔者没有用过UGUI,我想任何一个界面Tools都可以通过该方法实现,因为共同点一样,只是层级处理,缩放处理有区别而已

实现效果






改进目标

该项目还有许多需要改进的地方,以后花时间继续完善

  • 支持Editor模式下的编辑,不用运行即可查看效果(这个应该是最关键的功能)
  • 支持偶数个Item进行滑动
  • 支持Drag操作
  • 支持和NGUI类似的DragScrollView和CenterOnChild功能
  • 优化每个Item的层级设置算法效率
  • 优化更新每个Item位置,缩放算法效率

问题:
1. 在处理曲线循环的地方,由于精确度不够,导致持续点按过后会造成卡片大小发生变化,后续直接使用单一曲线或者通过程序控制曲线精确~

GitHub地址:后续更新会直接在版本库中更新

https://github.com/tinyantstudio/EnhancedScrollView


APK测试安装地址:http://pan.baidu.com/s/1bnfPasJ

总结

所有的内容都讲述完毕,如果这篇文章能够帮助到您获得对看到结束的朋友有一个简单的启发,请支持下~,文中存在错误或者描述不清楚的也请指正,共同交流学习,最好的方法就是直接下载Demo,然后看下逻辑和动画曲线的设置参数

欢迎转载,请注明出处~

By NPC燕(AndyKun)

你可能感兴趣的:(转载)