Unity 圆形循环复用滚动列表

news2024/12/22 23:12:52

一.在上一篇垂直循环复用滚动列表的基础上,扩展延申了圆形循环复用滚动列表。实现此效果需要导入垂直循环复用滚动列表里面的类。

1.基础类

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.UIElements;

/// <summary>
/// 环形的网格布局;
/// 让子对象摆成一个环形;
/// </summary>
public class CricleGrid : MonoBehaviour
{
    /// <summary>
    /// 是否是自动刷新模式,否则的话需要手动调用刷新;
    /// </summary>
    public bool IsAutoRefresh = true;

    /// <summary>
    /// 是否发生过改变;
    /// </summary>
    private bool IsChanged = false;
    /// <summary>
    /// 上一次检查的数量;
    /// </summary>
    int LastCheckCount = 0;

    /// <summary>
    /// Update每帧调用一次
    /// </summary>
    void Update()
    {
        检查是否需要自动刷新;
        if (!IsAutoRefresh)
            return;

        if (!IsChanged)
        {
            //检测子物体有没有被改变;
            GetChidList();
            int length = ListRect.Count;
            if (length != LastCheckCount)
            {
                LastCheckCount = length;
                IsChanged = true;
            }
            //此时刷新大小和位置;
            if (IsChanged)
                ResetSizeAndPos();
        }
        else
            RefreshAll();
    }

    private void OnValidate()
    {
        //编辑器下每一次更改需要实时刷新;
        RefreshAll();
    }

    /// <summary>
    /// 全部刷新;
    /// </summary>
    public void RefreshAll()
    {
        GetChidList();
        ResetSizeAndPos();
    }

    /// <summary>
    /// 当下激活的Rect;
    /// </summary>
    public List<RectTransform> ListRect = new List<RectTransform>(4);
    List<RectTransform> tempListRect = new List<RectTransform>(4);

    /// <summary>
    /// 获取父节点为本身的子对象
    /// </summary>
    void GetChidList()
    {
        ListRect.Clear();
        GetComponentsInChildren(false, tempListRect);
        int length = tempListRect.Count;
        for (int i = 0; i < length; i++)
        {
            var r = tempListRect[i];
            if (r.transform.parent != transform) continue;
            ListRect.Add(r);
        }
    }

    /// <summary>
    /// 网格大小;
    /// </summary>
    public Vector2 CellSize = new Vector2();

    /// <summary>
    /// 半径;
    /// </summary>
    public float Radius = 1;

    /// <summary>
    /// 起始角度;
    /// </summary>
    [Range(0f, 360f)]
    [SerializeField]
    float m_StartAngle = 30;

    /// <summary>
    /// 起始角度;
    /// </summary>
    public float StartAngle
    {
        get { return m_StartAngle; }
        set
        {
            m_StartAngle = value;
            IsChanged = true;
        }
    }

    /// <summary>
    /// 间隔角度;
    /// </summary>
    [Range(0f, 360f)]
    [SerializeField]
    float m_Angle = 30;

    /// <summary>
    /// 间隔角度;
    /// </summary>
    public float Angle
    {
        get { return m_Angle; }
        set
        {
            m_Angle = value;
            IsChanged = true;
        }
    }
    public Dictionary<int, CricleScrollItemPosData> itemPosDic = new();
    public List<CricleScrollItemPosData> itemPosList = new();
    /// <summary>
    /// 重新将字节点设置大小;
    /// </summary>
    public void ResetSizeAndPos()
    {
        int length = ListRect.Count;
        for (int i = 0; i < length; i++)
        {
            var tran = ListRect[i];
            tran.sizeDelta = CellSize;
            var v = GerCurPosByIndex(i);
            tran.anchoredPosition = new Vector2(v.x,v.y);
            tran.localEulerAngles = new Vector3(0,0, v.z);
        }
    }

    /// <summary>
    /// 返回第几个子对象应该所在的相对位置;
    /// </summary>
    public Vector3 GerCurPosByIndex(int index)
    {
        //1、先计算间隔角度:(弧度制)
        float totalAngle = Mathf.Deg2Rad * (index * Angle + m_StartAngle);
        //2、计算位置
        Vector3 Pos = new Vector2(Radius * Mathf.Cos(totalAngle), Mathf.Sin(totalAngle) * Radius);
        Pos.z = index * Angle + m_StartAngle + 180;
        return Pos;
    }

    public ScrollRect scrollRect;
    public List<object> list = new();
    public GameObject item;
    private List<CustomScrollItemMono> scrollTestItems = new();
    private int startIndex;
    private int endIndex;
    private int showItemCount = 10;



    private void InitItemsPos() 
    {
        int n = (int)(360f / Angle);
        for (int i = 0; i < list.Count; i++)
        {
            var v = GerCurPosByIndex(i);
            CricleScrollItemPosData data = new CricleScrollItemPosData();
            data.AnchoredPosition = new Vector3(v.x, v.y);
            data.LocalEulerAngles = new Vector3(0, 0, v.z);
            itemPosDic.Add(i, data);
            itemPosList.Add(data);
            //if (i < n)
            //{
            //    var v = GerCurPosByIndex(i);
            //    CricleScrollItemPosData data = new CricleScrollItemPosData();
            //    data.AnchoredPosition = new Vector3(v.x, v.y);
            //    data.LocalEulerAngles = new Vector3(0, 0, v.z);
            //    itemPosDic.Add(i, data);
            //    itemPosList.Add(data);
            //}
            //else 
            //{
            //    int temp = i % n;
            //    int m = (i + 1) / n;
            //    CricleScrollItemPosData d = new CricleScrollItemPosData();
            //    d.AnchoredPosition = itemPosDic[temp].AnchoredPosition;
            //    d.LocalEulerAngles.z += itemPosDic[temp].LocalEulerAngles.z + 360 * m;
            //    itemPosDic.Add(i, d);
            //    itemPosList.Add(d);
            //}
        }
        Debug.Log($"InitItemsPos ");
    }

    private void InitShowItems()
    {
        for (int i = 0; i < showItemCount; i++)
        {
            GameObject obj = Instantiate(item, transform);
            obj.transform.name = i.ToString();
            obj.SetActive(true);
            CustomScrollItemMono testItem = obj.GetComponent<CustomScrollItemMono>();
            testItem.Init(i, list[i]);
            scrollTestItems.Add(testItem);
        }
        item.SetActive(false);
    }



    private float eulerAnglersZ;
    private float preA = 1;
    public ScrollRect ScrollRect;
    private RectTransform Content;

    /// <summary>
    /// 用这个初始化
    /// </summary>
    void Start()
    {
        for (int i = 0; i < 100; i++)
        {
            list.Add(new ScrollTestData() { ID = i });
        }
        Content = ScrollRect.content;
        Content.sizeDelta = new Vector2(500, (100 * Angle / 360 + StartAngle % 360 / 360f) * 2 * Mathf.PI * Radius);
        InitItemsPos();
        InitShowItems();
        RefreshAll();
    }

    private void Awake()
    {
        scrollRect.horizontal = false;
        scrollRect.vertical = true;
        scrollRect.onValueChanged.AddListener((value) =>
        {
            float offset = Mathf.Abs(preA - value.y);
            float aa = (offset) * ((100 - 6) * Angle);
            if (scrollRect.velocity.y > 0) //手指上滑 
            {
                transform.localEulerAngles -= new Vector3(0,0, aa);
                eulerAnglersZ += aa;
            }
            else if (scrollRect.velocity.y < 0)//手指下滑 
            {
                transform.localEulerAngles += new Vector3(0, 0, aa);
                eulerAnglersZ -= aa;
            }
            preA = value.y;
 
            for (int i = startIndex; i < itemPosList.Count; i++)
            {
                if (i + 1 < itemPosList.Count)
                {
                    if (scrollRect.velocity.y > 0)//手指上滑 
                    {
                        var targetY = itemPosList[i + 1].LocalEulerAngles.z - 180;
                        //Debug.Log($"ccc y {y} targetY {targetY} startIndex {startIndex} Z {transform.localEulerAngles.z}");
                        if (eulerAnglersZ + StartAngle >= targetY)
                        {
                            startIndex = i + 1;
                            endIndex = startIndex + showItemCount - 1;
                            break;
                        }
                    }
                    else if (scrollRect.velocity.y < 0)//手指下滑
                    {
                        if (startIndex > 0 && startIndex < itemPosDic.Count)
                        {
                            var targetY = itemPosDic[startIndex].LocalEulerAngles.z - 180;
                            if (eulerAnglersZ + StartAngle <= targetY)
                            {
                                startIndex = i - 1;
                                endIndex = startIndex + showItemCount - 1;
                                break;
                            }
                        }
                    }
                }
            }
            //Debug.Log($"bbb startIndex {startIndex} endIndex {endIndex}");
            if (startIndex > 100 - showItemCount) 
            {
                startIndex = 100 - showItemCount;
            }
            if (endIndex >= itemPosDic.Count) { return; }
            int index = 0;
            for (int i = startIndex; i < endIndex + 1; i++)
            {
                if (index < scrollTestItems.Count && i < itemPosDic.Count)
                {
                    var item = scrollTestItems[index];
                    item.Init(i, list[i]);
                    var rect = item.gameObject.GetComponent<RectTransform>();
                    rect.anchoredPosition3D = itemPosDic[i].AnchoredPosition;
                    rect.localEulerAngles = itemPosDic[i].LocalEulerAngles;
                    index += 1;
                }
            }
        });
    }


    public class CricleScrollItemPosData
    {
        public Vector3 AnchoredPosition;
        public Vector3 LocalEulerAngles;
    }

}

2.UI目录

3.item克隆体,挂载脚本

4.按照图示,把CricleGrid脚本,挂在Items节点下,调整各个参数,运行即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2263946.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

京东大数据治理探索与实践 | 京东零售技术实践

01背景和方案 在当今的数据驱动时代&#xff0c;数据作为关键生产要素之一&#xff0c;其在商业活动中的战略价值愈加凸显&#xff0c;京东也不例外。 作为国内领先的电商平台&#xff0c;京东在数据基础设施上的投入极为巨大&#xff0c;涵盖数万台服务器、数 EB 级存储、数百…

android:sharedUserId 应用进程声明介绍

背景 adb install 安装系统软件报错,原因是签名不一致,进程改变。 代码分析 AndroidManifest.xml 定义的 android:sharedUserId 应用归属进程不同,从phone切换到system。 初始配置 <manifest xmlns:android="http://schemas.android.com/apk/res/android"c…

Liveweb视频融合共享平台在果园农场等项目中的视频监控系统搭建方案

一、背景介绍 在我国的大江南北遍布着各种各样的果园&#xff0c;针对这些地处偏僻的果园及农场等环境&#xff0c;较为传统的安全防范方式是建立围墙&#xff0c;但是仅靠围墙仍然无法阻挡不法分子的有意入侵和破坏&#xff0c;因此为了及时发现和处理一些难以察觉的问题&…

交换机链路聚合(手动负载分担模式)(eNSP)

目录 交换机SW_C配置: 交换机-PC划分vlan: 交换机-交换机端口聚合: 交换机SW_D配置: 交换机-PC划分vlan: 交换机-交换机端口聚合: 验证: 链路聚合的端口清除: 交换机端口聚合的存在意义主要有以下几点: 增加带宽 提高冗余性和可靠性 实现负载均衡 降低成本 …

玩转树莓派Pico(19): 迷你气象站5——软件整合

一、前言 各个模块都已经测试了&#xff0c;硬件也组装完成&#xff0c;到了软件整合的步骤了。 目前我仅按照自己的经验来整合&#xff0c;肯定要踩坑的。以后除了多去开源网站看看大佬的代码&#xff0c;还要继续揣摩《无线电》杂志里的文章。很多文章对我来说比较高深&#…

30. 多进程编程

一、什么是进程 进程&#xff08;process&#xff09;则是一个执行中的程序。每个进程都拥有自己的地址空间、内存、数据栈以及其它用于跟踪执行的辅助数据。操作系统管理其上所有进程的执行&#xff0c;并为这些进程合理分配时间。进程也可以通过派生新的进程来执行其它任务&a…

Unity Post请求发送fromdata数据content-type

wwwfrom 的 headers["Content-Type"]修改 错误代码&#xff1a; WWWForm form new WWWForm(); if (form.headers.ContainsKey("Content-Type")) {string boundary string.Format("--{0}", DateTime.Now.Ticks.ToString("x"));form…

aosp15 - Activity生命周期切换

本文探查的是&#xff0c;从App冷启动后到MainActivity生命周期切换的系统实现。 调试步骤 在com.android.server.wm.RootWindowContainer#attachApplication 方法下断点&#xff0c;为了attach目标进程在com.android.server.wm.ActivityTaskSupervisor#realStartActivityLock…

SAP PP ECN CSAP_MAT_BOM_MAINTAIN

刚开始的时候ECN总是加不上&#xff0c; 参考kimi给出的案例 点击链接查看和 Kimi 智能助手的对话 https://kimi.moonshot.cn/share/cth1ipmqvl7f04qkggdg 效果 加上了 FUNCTION ZPBOM_PLM2SAP. *"------------------------------------------------------------------…

GitLab的安装和使用

1.GitLab 环境说明 系统版本 CentOS 7.2 x86_64 软件版本 gitlab-ce-10.8.4 GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用Git作为代码管理工具&#xff0c;并在此基础上搭建起来的web服务。可通过Web界面进行访问公开的或者私人项目。它拥有与Github类似的功能…

开放词汇目标检测(Open-Vocabulary Object Detection, OVOD)综述

定义 开放词汇目标检测&#xff08;Open-Vocabulary Object Detection, OVOD&#xff09;是一种目标检测任务&#xff0c;旨在检测和识别那些未在训练集中明确标注的物体类别。传统的目标检测模型通常只能识别有限数量的预定义类别&#xff0c;而OVOD模型则具有识别“开放词汇…

JaxaFx学习(三)

目录&#xff1a; &#xff08;1&#xff09;JavaFx MVVM架构实现 &#xff08;2&#xff09;javaFX知识点 &#xff08;3&#xff09;JavaFx的MVC架构 &#xff08;4&#xff09;JavaFx事件处理机制 &#xff08;5&#xff09;多窗体编程 &#xff08;6&#xff09;数据…

【C++】小乐乐求和问题的高效求解与算法对比分析

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;问题描述与数学模型1.1 题目概述1.2 输入输出要求1.3 数学建模 &#x1f4af;方法一&#xff1a;朴素循环求和法2.1 实现原理2.2 分析与问题2.3 改进方案2.4 性能瓶颈与结论…

基于Spring Boot的找律师系统

一、系统背景与意义 在现代社会&#xff0c;法律服务的需求日益增长&#xff0c;但传统寻找律师的方式往往存在信息不透明、选择困难等问题。基于Spring Boot的找律师系统旨在解决这些问题&#xff0c;通过线上平台&#xff0c;用户可以轻松搜索、比较和选择合适的律师&#x…

【Spring】方法注解@Bean,配置类扫描路径

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 引入 一&#xff1a;Bean方法注解 1&#xff1a;方法注解要搭配类注解使用 2&#xff1a;执行结果 …

深度学习0-前置知识

一、背景 AI最大&#xff0c;它的目的是通过让机器模仿人类进而超越人类&#xff1b; ML次之&#xff0c;它是AI的一个分支&#xff0c;是让机器模仿人类的一种方法。开发人员用大量数据和算法“训练”机器&#xff0c;让机器自行学会如何执行任务&#xff0c;它的成功取决于…

前端面试题整理-前端异步编程

1. 进程、线程、协程的区别 在并发编程领域&#xff0c;进程、线程和协程是三个核心概念&#xff0c;它们在资源管理、调度和执行上有着本质的不同。 首先&#xff0c;进程是操作系统进行资源分配和调度的独立单位&#xff08;资源分配基本单位&#xff09;&#xff0c;每个进…

ARM学习(38)多进程多线程之间的通信方式

ARM学习(38)ARM学习(38)多进程多线程之间的通信方式 一、问题背景 笔者在调试模拟器的时候,碰到进程间通信的问题,一个进程在等另外一个进程ready的时候,迟迟等不到,然后通过调试发现,另外一个进程变量已经变化了,但是当前进程变量没变化,需要了解进程间通信的方式…

群晖利用acme.sh自动申请证书并且自动重载证书的问题解决

前言 21年的时候写了一个在群晖&#xff08;黑群晖&#xff09;下利用acme.sh自动申请Let‘s Encrypt的脚本工具 群晖使用acme自动申请Let‘s Encrypt证书脚本&#xff0c;自动申请虽然解决了&#xff0c;但是自动重载一直是一个问题&#xff0c;本人也懒&#xff0c;一想到去…

level2逐笔委托查询接口

沪深逐笔委托队列查询 前置步骤 分配数据库服务器 查询模板 以下是沪深委托队列查询的请求模板&#xff1a; http://<数据库服务器>/sql?modeorder_book&code<股票代码>&offset<offset>&token<token>查询参数说明 参数名类型说明mo…