深入探讨并实现Unity图像轮播

深入探讨并实现Unity图像轮播

最近接了一个展厅的小项目,需求中有一个图像轮播的小功能,本来以为这是一碟小菜,心想瞬间有了N中解决方案,然而,现实是pia pia打脸,幸好最终完美实现了效果,才保住了作为公司中几个妹子心目中“大神”的老脸。
先看看最终效果:
在这里插入图片描述
注意哦,它两边是有一个渐隐效果的。
说一说制作历程和思路吧:

思路一

一看到我们美监妹子的效果图,第一个想到的是自带的ScrollView(ScrollRect组件),然而确认了需求后,说是这个轮播是要无限循环的,就是说滚动是无穷无尽的,朝左边拖动,最左边的要能自动跑到最右边,朝右边拖动,最右边的要能自动跑到最左边,如果用ScrollRect做,势必要有比较复杂的位置计算,果断PASS掉了。

然后有了第二个思路:

还是用UGUI,所有的图放到一个具有Mask组件的Image下,然后所有的图用anchoredPosition求得与中心点(0,0)的距离,然后用这个距离做一个反比系数,用这个系数去控制图像的尺寸,即:如果距离如果为0,则图像的Size最大,否则,就越小。
k = 1 − ( a n c h o r e d P o s i t i o n − ( 0 , 0 ) ) . m a g n i t u d e 轮 播 组 件 宽 度 / 2 k = 1 - {{(anchoredPosition-(0,0)).magnitude}\over{轮播组件宽度/2}} k=1/2(anchoredPosition(0,0)).magnitude

public void OnDrag(PointerEventData eventData)
{
   Vector2 delta = eventData.delta;
   delta.y = 0; // 排除纵向的拖动
   foreach( CarouselImage image in AllImages )
   {
       image.anchoredPosition += delta;
       float k = 1 - Mathf.Abs(image.anchoredPostion.x) / HalfWidth;
       image.sizeDelta *= k;
   }
}

嗯,看上去不错,然而,这样做有一点没有考虑,那就是中间那个图应该是位于最上层,两边的图应该处于底层,这就需要动态的对他们排序了。很显然,可以用上面的k值进行排序,K越大SiblingIndex就应该越大,这样可以保证中间的图位于顶层。然而,试了几种修改SiblingIndex的方法,都觉得不够优雅,而且设置SiblingIndex的时机总是不对。哎,抓耳挠腮之际,突然想到,干嘛非要局限于一个平面呢。咱们unity可是3D的。

第三个思路

用SpriteRenderer(后来改为了World模式下的Canvas,原因是轮播并不是直接播放原图,而是进行了特定的排版,比如加上边框,图像上添加说明文字等等),围绕一个点去旋转,这些图像设置为广告板,一切问题迎刃而解。

public void LoadImages( CarouselImage [] images )
{
	float deltaAngle = 2f * Mathf.PI / images.Length;
	for( int i = 0; i < images.Length; ++i)
	{
		images[i].localPosition = new Vector3(
			-Mathf.Sin(i * deltaAngle) * Radius,
			0,
			Mathf.Cos(i * deltaAngle ) * Radius);
	}
}

嗯。这样的话,所有的图像都平均分布在这个圆的周围了,想要轮播,只需要绕y轴转动这个顶层的物体就行了。
嗯,看上去不错了,但是。。因为这个是圆,所有的图片围绕这个圆平均分布,但有一个问题是,当图片数量不同时,图片的稀疏程度就会不同,但我们美监要求的是,除非图片数量少于N,否则无论多少图片,可见部分的图始终是N张。那就意味着,这个圆上分布的图并不是均匀分布的。而且圆的半径也很难调整。额。。这个思路又放弃了。

终极思路

怎么办,怎么才能优雅的实现这个呢?受上一个思路的启发,实际上,圆本身是不需要的,本质上我只需要一个圆弧,然后在这段弧上,分布N张图片(图片总量大于N),看不见的图应当可以disable掉。关键是这个弧了。然后,我就想到了贝塞尔曲线。

// Class CarsoulManager
//贝塞尔曲线顶点
[SerializeField]
private Vector3 [] BezierPos = new Vector3 []
{
	new Vector3[ -160, 0, 80 ],
	new Vector3[ 0, 0, 0 ],
	new Vector3[ 160, 0, 80 ]
};
// 支持四阶、三阶、二阶贝塞尔曲线
public Vector3 GetBezierPosition( float lerp )
{
    int count = BezierPos.Length;
    if (count >= 4)
    {
        Vector3 p1 = Vector3.Lerp(BezierPos[0], BezierPos[1], lerp);
        Vector3 p2 = Vector3.Lerp(BezierPos[1], BezierPos[2], lerp);
        Vector3 p3 = Vector3.Lerp(BezierPos[2], BezierPos[3], lerp);
        Vector3 p4 = Vector3.Lerp(p1, p2, lerp);
        Vector3 p5 = Vector3.Lerp(p2, p3, lerp);
        return Vector3.Lerp(p4, p5, lerp);
    }
    else if (count == 3)
    {
        Vector3 p1 = Vector3.Lerp(BezierPos[0], BezierPos[1], lerp);
        Vector3 p2 = Vector3.Lerp(BezierPos[1], BezierPos[2], lerp);
        return Vector3.Lerp(p1, p2, lerp);
    }
    else if (count == 2)
    {
        return Vector3.Lerp(BezierPos[0], BezierPos[1], lerp);
    }
    else if (count == 1)
        return BezierPos[0];
    else
        return Vector3.zero;
}

有了这个贝塞尔曲线,那么加载好的图像就可以平均分配到这条曲线上了。这很容易,使用Lerp就可以了,整个曲线从最左边到最右边,看作是从0到1,只要给每张图分配不同的Lerp值就好了。

// Class CarouselImage:
private float m_lerpVal = 0;
public float LerpValue
{
	get { return m_lerpVal; }
	set
	{
		m_lerpVal = value;
		if ((m_lerpVal < -Carousel.Spacing ) || ( m_lerpVal > 1+Carousel.Spacing))
		{
			if (gameObject.activeSelf)
				gameObject.SetActive(false);
		}
		else
		{
			if (!gameObject.activeSelf)
				gameObject.SetActive(true);
			transform.localPosition = Carousel.GetBezierPosition(m_lerpVal);
		}
	}
}

为了让第0张图在一开始时就位于中心位置,所以加载是按照如下方式进行的:

// Class CarouselManager:
public void LoadImages(CarouselImage [] items)
{
    // 计算每张图的间隔
	float spacing = 1f / N;
	
	int len = items.Length;
	for( int i = 0; i < len; ++ i )
	{
		if( i >= m_Pictures.Count )
		{
			CarouselImage pic = Instantiate<CarouselImage >(PicturePrefab, transform);
			pic.WorldCamera = TheCamera;
			m_Pictures.Add(pic);
		}
		m_Pictures[i].Pictures = items[i];
		int k = ((i % 2 == 0) ? 1 : -1) * ((i + 1) / 2);
		m_Pictures[i].LerpValue = 0.5f + k * spacing;
	}

	for (int i = m_Pictures.Count - 1; i >= len; --i)
	{
		DestroyImmediate(m_Pictures[i].gameObject);
		m_Pictures.RemoveAt(i);
	}
}

稍微解释一下,因为轮播组件是要频繁更换图片的,所以可能会多次调用LoadImages方法,于是,为了节约一丢丢的性能,这里就不删除上次加载的图,而是重新赋予他们新的图像,只有上次加载的图像数量不足时,才创建新的实例,当然,如果上次加载的图像比这次还多,那最后剩余的图像还是要删除的。

那么如何滑动交互呢?很简单:

// Class CarouselTouch
public void OnTouchDrag(float delta)
{
	float min = float.MaxValue;
	float max = float.MinValue;
	float spacing = 1f / N;

	// 首先对所有图像进行新的位置计算,并顺便找到最左边和最右边的图像
	foreach( CarouselImage pic in m_Pictures )
	{
		pic.LerpValue += delta;
		if( pic.LerpValue > max )
		{
			max = pic.LerpValue;
		}
		if( pic.LerpValue < min )
		{
			min = pic.LerpValue;
		}
	}

	// 如果是向左滑动的话,把最左边的图像挪到右边去,反之亦然
	foreach( CarouselImage pic in m_Pictures)
	{
		if( delta > 0 )
		{
			if( pic.LerpValue > ( 1f +  spacing ))
			{
				pic.LerpValue = min - spacing;
				min = pic.LerpValue;
			}
		}
		else
		{
			if( pic.LerpValue < - spacing )
			{
				pic.LerpValue = max + spacing;
				max = pic.LerpValue;
			}
		}
	}
}

很完美了。只剩下最后一个问题:美监要求两边要渐隐。其实这个实现起来也不是很难,两种方法:
1、写Shader,当图像的lerp值超过一个阈值时,比如小于0.1或者大于0.9时,就开始把两边透明化。
2、不想写代码的话,搞个摄像机,对准整个CarouselManager组件,并设置为只渲染Carousel相关的图像layer,渲染到一张RenderTexture中,对这个RenderTexture就可以做两边透明话的处理了,网上搜一下AlphaMask,一堆类似的插件,本质上也是个Mask,放到RawImage中即可。
最后,再看一下整体效果图:
在这里插入图片描述
当然了,如果不想要中间大,两边小的效果,可以吧贝塞尔曲线的Z值全设置为0,这样曲线就退化为一条直线了,效果如下:
在这里插入图片描述
甚至是这样子的效果哦:
在这里插入图片描述


最后,当然这个组件还可以继续再完善,继续再研发其他的效果,比如自动缓缓播放等等,这些都不难,这里就不再继续探讨了。


最后附上下载链接

点此下载源码

### 回答1: Unity Carousel是一款用于在Unity引擎中创建轮播图像功能的插件。它提供了一个简单而强大的方法来展示多个图像,使用户能够轻松地浏览和切换图像Unity Carousel具有以下特点: 1. 简单易用:该插件的界面友好且易于操作,无需编写复杂的代码即可创建和管理轮播图像。 2. 多种效果:Unity Carousel提供了多种效果和过渡选项,可以根据需求选择合适的效果来展示图像。例如,可以使用淡入淡出、滑动、缩放等效果来切换图像。 3. 自定义配置:用户可以自定义轮播图像的大小、拖拽速度、切换时间等参数,以便根据项目需求进行调整。 4. 按钮控制:插件还提供了多个按钮,用户可以通过点击按钮来切换图像,方便用户控制和导航。 5. 资源管理:Unity Carousel支持多种图像格式,用户可以方便地导入和管理图像资源。 6. 手势支持:插件还支持触摸手势,用户可以通过滑动屏幕来切换图像,增加了交互性和易用性。 总之,Unity Carousel是一个功能强大且易于使用的轮播图像插件,它提供了多种效果和配置选项,能够满足不同项目的需求。无论是用于游戏中的图像展示,还是用于应用程序的图片浏览,Unity Carousel都能提供便捷的解决方案。 ### 回答2: Unity Carousel是一款功能强大的轮播图像插件,用于在Unity游戏开发中创建漂亮的轮播效果。 Unity Carousel提供了一个易于使用的接口和丰富的功能,允许开发者轻松地添加和管理多个图像实现无缝地切换和滑动效果。插件支持从本地文件系统加载图像,也可以通过网络加载。 使用Unity Carousel,开发者可以自定义轮播图像的样式和布局。插件内置了多种过渡效果和动画选项,使得切换图像时可以有各种视觉上的过渡效果。同时,开发者还可以自定义轮播图像的尺寸、位置、旋转角度等参数,以适应不同的场景需求。 除了基本的轮播功能,Unity Carousel还提供了其他有用的功能。例如,开发者可以为每个图像添加点击事件,以实现点击图像后的相应操作。此外,插件还支持自动轮播、循环播放和手势控制等功能,为用户提供了更多的交互体验。 总之,Unity Carousel是一款功能丰富的轮播图像插件,具有易于使用、多样化的样式和布局选项,以及额外的功能和交互体验。对于Unity游戏开发者来说,使用Unity Carousel可以快速创建出吸引人的轮播效果,提升游戏的视觉品质并丰富用户体验。 ### 回答3: Unity Carousel是一个功能强大的Unity插件,专门用于创建和管理图像轮播。 首先,Unity Carousel提供了简单而直观的界面,让用户能够轻松地创建和编辑轮播图像。用户可以通过拖放图像文件到轮播区域,或直接从资源管理器中导入图像。此外,Unity Carousel还支持多种图像格式,包括PNG、JPG和GIF等,确保用户可以使用各种类型的图像。 其次,Unity Carousel提供了丰富的轮播效果和模板。用户可以选择不同的过渡效果和动画效果,以及设置每个图像的停留时间。此外,Unity Carousel还提供了多种预设的轮播模板,用户可以根据自己的需求选择合适的模板,从而快速创建出独特且吸引人的轮播效果。 另外,Unity Carousel还具有自定义性强的特点。用户可以根据自己的需要进行定制化设置,如调整图像的位置、大小和旋转角度等。同时,Unity Carousel还支持添加文字或按钮等UI元素,使轮播更加丰富多样,提升用户体验。 最后,Unity Carousel还内置了优化功能,确保在不同平台上的高性能表现。无论是在PC、移动设备还是虚拟现实平台上,Unity Carousel都能够以流畅的方式展示图像,并具有较低的资源消耗。 总之,Unity Carousel是一个功能强大、易于使用且可定制的图像轮播插件。通过它,用户可以快速创建出独特而美观的轮播效果,提升应用程序的可视化效果和用户体验。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

示申○言舌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值