Unity—ScrollRect轨迹滑动

2023-10-19 22:59
文章标签 轨迹 unity 滑动 scrollrect

本文主要是介绍Unity—ScrollRect轨迹滑动,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

以下内容是根据Unity 2020.1.01f版本进行编写的

Unity—ScrollRect轨迹滑动

  • 1、目的
  • 2、思考
  • 3、自定义实现轨迹滑动
  • 4、两种方法的优缺点
  • 5、最终效果
  • 6、项目工程源代码

1、目的

工作中遇到有需要实现轨迹滑动的滑动列表,通常的做法是计算贝塞尔曲线得出轨迹,但是我觉得计算贝塞尔曲线太麻烦了,或许有没有更简单的方法。

2、思考

轨迹滑动可以分两种情况:
第一种是轨迹滑动是比较简单的横向(或纵向)滑动,其中轨迹不会蜿蜒盘旋,也不涉及列表格子之间的重叠关系,这时可以分区间来对Y轴(或X轴)进行设置以达到格子沿着轨迹滑动的效果
例如,轨迹是这样的波浪型的,其中,轨迹点的数量可以无限增加,点越多轨迹可以设置得越平滑,但是消耗的性能也越多:

像这样,类似波浪型的轨迹,可以用一个Vector列表记录下每个点的位置和顺序,然后根据每个格子所在的位置,先由小到大循环判断在哪个区间,例如格子3在pos_5和pos_6中间,所以第三个格子的区间就是5,然后通过pos_5和pos_6两个点相减,得到一个从pos_5指向pos_6的向量vector,通过vector3.distance函数得到pos_5和pos_6两个点的横向距离distanceX1,然后再次通过vector3.distance函数得到pos_5与格子3得横向距离distanceX2,那么,格子3的位置 = pos_5的位置 + distanceX2 / distanceX1 * vector,得到位置后再把位置设置回去就可以了

第二种就是更为复杂的轨迹滑动,例如蜿蜒盘旋式的轨迹,其中还包括有层级关系
例如,轨迹是蜿蜒盘旋的,同样是轨迹点越多,曲线越平滑,也越消耗性能:

像这样,蜿蜒盘旋的轨迹,就不能使用第一种方法了,因为有些位置,一个X值对应下来有多个Y值,无法区分当前需要的是哪个Y值,所以,需要使用另一种方法
我们可以通过获取所有的轨迹点,计算出每两个相邻轨迹点之间的距离,通过距离确定格子应该在哪个区间内
例如,格子3的位置是95,假设pos_1和pos_2的距离为50,pos_2和pos_3的距离为也是50,那么pos_1到pos_3的总距离就是100,所以格子3应该在区间pos_2和pos_3中间。接下来计算实际位置,因为此时格子的X轴和Y轴都会变化,同样无法使用第一种方法来计算格子的位置,需要通过格子的位置减去前面区间距离的和,在例子中,就是格子3的位置减去pos_1到pos_2的距离,即95 – 50 = 45,再通过剩余的距离除以当前区间的距离得出比例ratio,然后就是通过当前的两个区间点相减得到pos_2指向pos_3的向量vector,那么格子3的位置就是,pos_2的位置 + ratio * vector
最后,还有层级的问题,也就是哪个格子遮挡哪个格子,一般是后面的格子遮挡前面的格子,如果需要前面的遮挡后面的格子,只需要动态修改层级就行,unity提供了这个函数:Transform.SetAsFirstSibling()
在实际使用中,我发现使用这种方法后,content的长度有时候会过长,有时候会过短,所以需要确定好格子的数量,设置好content的正确长度

注意:以上都是最基础的思想,因为滑动时改变的是content节点,格子的位置实际上是不变的,或者是由代码设置的,所以实际代码中还需要考虑scrollrect的宽高以及content节点的滑动值(即posX值,滑动时此值会变化)

3、自定义实现轨迹滑动

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;[RequireComponent(typeof(ScrollRect))]
public class CurveSliding : MonoBehaviour
{public Transform posListRoot;public float contentwidth;              //必须自己设置的content实际长度public bool isChangeHierarchy = false;private ScrollRect scrollRect;private RectTransform content;private List<Transform> posTransformList = new List<Transform>();private List<RectTransform> item_transformList = new List<RectTransform>();private List<float> posX_List = new List<float>();private List<float> distanceList = new List<float>();private float scrollRectWidth;private float scrollRectHeight;private float posTotalDistance;private float spacing = 0;private void Start(){scrollRect = GetComponent<ScrollRect>();content = scrollRect.content.transform as RectTransform;HorizontalLayoutGroup contentLayoutGroup = content.GetComponentsInChildren<HorizontalLayoutGroup>(true)[0];if(contentLayoutGroup != null && contentLayoutGroup.name == content.name){spacing = contentLayoutGroup.spacing;}scrollRectWidth = scrollRect.GetComponent<RectTransform>().sizeDelta.x;scrollRectHeight = scrollRect.GetComponent<RectTransform>().sizeDelta.y;for (int i = 0; i < content.childCount; i++){RectTransform item_transform = content.GetChild(i) as RectTransform;if (item_transform.gameObject.activeInHierarchy){item_transformList.Add(item_transform);posX_List.Add(item_transform.localPosition.x);}else{continue;}}float totalDistance = 0;for(int i = 0;i < posListRoot.childCount;i++){Transform posTransform = posListRoot.GetChild(i);if (i > 0){Transform previousPosTransform = posListRoot.GetChild(i - 1);totalDistance += Vector3.Distance(posTransform.localPosition, previousPosTransform.localPosition);}posTransformList.Add(posTransform);distanceList.Add(totalDistance);}posTotalDistance = distanceList[distanceList.Count - 1];content.sizeDelta = new Vector2(contentwidth, content.sizeDelta.y);OnValueChange(Vector2.zero);scrollRect.onValueChanged.AddListener(OnValueChange);}public void OnValueChange(Vector2 vector){for(int i = 0;i < item_transformList.Count;i++){float localPosX = posX_List[i];float posX = localPosX + content.anchoredPosition.x;//如果当前节点的位置 - content的X轴偏移值 > 滑动列表的宽度,则说明当前item在可视范围外if (posX > posTotalDistance + 200 || posX < 0){continue;}int index = -1;foreach(var totalDistance in distanceList){if(posX < totalDistance){break;}else{index++;}}//如果index+1小于位置列表的数量,则其在位置区间内,否则应该在位置区间外if (index + 1 < posListRoot.childCount && index + 1 > 0){float ratio = (posX - distanceList[index]) / (distanceList[index + 1] - distanceList[index]);Vector3 newPos = posTransformList[index].localPosition - ratio * (posTransformList[index].localPosition - posTransformList[index + 1].localPosition);item_transformList[i].localPosition = new Vector3(newPos.x + scrollRectWidth/2 - content.anchoredPosition.x, -scrollRectHeight / 2 + newPos.y, 0);}else if(index <= -1){item_transformList[i].localPosition = new Vector3(item_transformList[i].localPosition.x, -scrollRectHeight / 2 + posListRoot.GetChild(0).localPosition.y, 0);}else if(index >= posListRoot.childCount - 1){if (i < 1){continue;}RectTransform previousItem_RectTransform = item_transformList[i - 1];item_transformList[i].localPosition = new Vector3(previousItem_RectTransform.localPosition.x + spacing + previousItem_RectTransform.sizeDelta.x/2 + item_transformList[i].sizeDelta.x/2, -scrollRectHeight / 2 + posListRoot.GetChild(posListRoot.childCount - 1).localPosition.y, 0);}if (isChangeHierarchy){item_transformList[i].SetAsFirstSibling();}}}
}

4、两种方法的优缺点

一、 两种方法都不能使用排序组件,因为排序组件会控死格子的位置,通过代码也无法修改
二、 第二种方法比第一种方法更复杂,同时消耗的性能也更多,但是能实现更复杂的效果
三、第二种方法需要设置conten的长度,即格子的数量无法动态变化,是一大缺点

5、最终效果

第一种:
在这里插入图片描述

第二种:

6、项目工程源代码

https://github.com/CHJ-Self/MyUGUI



大佬们找到问题欢迎拍砖~

这篇关于Unity—ScrollRect轨迹滑动的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/242909

相关文章

轨迹规划-B样条

B样条究竟是干啥的?白话就是给出一堆点,用样条的方式,给这些点连接起来,并保证丝滑的。 同时B样条分为准均匀和非均匀,以下为准均匀为例。 参考链接1:https://zhuanlan.zhihu.com/p/50626506https://zhuanlan.zhihu.com/p/50626506 参考链接2: https://zhuanlan.zhihu.com/p/536470972h

专题二_滑动窗口_算法专题详细总结

目录 滑动窗口,引入: 滑动窗口,本质:就是同向双指针; 1.⻓度最⼩的⼦数组(medium) 1.解析:给我们一个数组nums,要我们找出最小子数组的和==target,首先想到的就是暴力解法 1)暴力: 2)优化,滑动窗口: 1.进窗口 2.出窗口 3.更新值 2.⽆重复字符的最⻓⼦串(medium) 1)仍然是暴力解法: 2)优化: 进窗口:hash[s[rig

全英文地图/天地图和谷歌瓦片地图杂交/设备分布和轨迹回放/无需翻墙离线使用

一、前言说明 随着风云局势的剧烈变化,对我们搞软件开发的人员来说,影响也是越发明显,比如之前对美对欧的软件居多,现在慢慢的变成了对大鹅和中东以及非洲的居多,这两年明显问有没有俄语或者阿拉伯语的输入法的增多,这要是放在2019年以前,一年也遇不到一个人问这种需求场景的。 地图应用这块也是,之前的应用主要在国内,现在慢慢的多了一些外国的应用场景,这就遇到一个大问题,我们平时主要开发用的都是国内的地

Unity Post Process Unity后处理学习日志

Unity Post Process Unity后处理学习日志 在现代游戏开发中,后处理(Post Processing)技术已经成为提升游戏画面质量的关键工具。Unity的后处理栈(Post Processing Stack)是一个强大的插件,它允许开发者为游戏场景添加各种视觉效果,如景深、色彩校正、辉光、模糊等。这些效果不仅能够增强游戏的视觉吸引力,还能帮助传达特定的情感和氛围。 文档

hot100刷题第1-9题,三个专题哈希,双指针,滑动窗口

求满足条件的子数组,一般是前缀和、滑动窗口,经常结合哈希表; 区间操作元素,一般是前缀和、差分数组 数组有序,更大概率会用到二分搜索 目前已经掌握一些基本套路,重零刷起leetcode hot 100, 套路题按套路来,非套路题适当参考gpt解法。 一、梦开始的地方, 两数之和 class Solution:#注意要返回的是数组下标def twoSum(self, nums: Lis

Unity协程搭配队列开发Tips弹窗模块

概述 在Unity游戏开发过程中,提示系统是提升用户体验的重要组成部分。一个设计良好的提示窗口不仅能及时传达信息给玩家,还应当做到不干扰游戏流程。本文将探讨如何使用Unity的协程(Coroutine)配合队列(Queue)数据结构来构建一个高效且可扩展的Tips弹窗模块。 技术模块介绍 1. Unity协程(Coroutines) 协程是Unity中的一种特殊函数类型,允许异步操作的实现

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光 一,前言二,资源包内容三,免费获取资源包 一,前言 在创意的世界里,每一个细节都能决定一个项目的独特魅力。今天,要向大家介绍一款令人惊艳的粒子效果包 ——Super Confetti FX。 二,资源包内容 💥充满活力与动态,是 Super Confetti FX 最显著的标签。它宛如一位

Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(4)

本文仅作笔记学习和分享,不用做任何商业用途 本文包括但不限于unity官方手册,unity唐老狮等教程知识,如有不足还请斧正​​ Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(3)-CSDN博客  这节就是真正的存储数据了   理清一下思路: 1.存储路径并检查 //2进制文件类存储private static string Data_Binary_Pa

Unity Adressables 使用说明(一)概述

使用 Adressables 组织管理 Asset Addressables 包基于 Unity 的 AssetBundles 系统,并提供了一个用户界面来管理您的 AssetBundles。当您使一个资源可寻址(Addressable)时,您可以使用该资源的地址从任何地方加载它。无论资源是在本地应用程序中可用还是存储在远程内容分发网络上,Addressable 系统都会定位并返回该资源。 您

Unity Adressables 使用说明(六)加载(Load) Addressable Assets

【概述】Load Addressable Assets Addressables类提供了加载 Addressable assets 的方法。你可以一次加载一个资源或批量加载资源。为了识别要加载的资源,你需要向加载方法传递一个键或键列表。键可以是以下对象之一: Address:包含你分配给资源的地址的字符串。Label:包含分配给一个或多个资源的标签的字符串。AssetReference Obj