/******************************************************************** 文件: ScrollViewGridEx.cs 作者: 梦语 邮箱: 1982614048@qq.com 创建时间: 2024/4/5 17:11 最后修改: 梦语 最后修改时间: 2024/04/23 09:21:55 功能: *********************************************************************/ using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using Sirenix.OdinInspector; #if UNITY_EDITOR using UnityEditor; #endif namespace Ether { public class ScrollViewGridEx : SerializedMonoBehaviour { public enum ScrollDirection { [InspectorName("从上到下")] TopToDown, [InspectorName("从下到上")] DownToTop, [InspectorName("从左到右")] LeftToRight, [InspectorName("从右到左")] RightToLeft, } /// /// 格子数据 /// public class CellInfo { public int index; public Vector3 pos; //已左上角为点 public GameObject item; } #region 参数 private ScrollRect scrollRect; public ScrollRect ScrollRect => scrollRect; [ShowInInspector] private RectTransform content; [Tooltip("该属性优先"), LabelText("格子预制体")] [ShowIf("@this.cellPrefabPath == null || this.cellPrefabPath == \"\"")] public GameObject cellPrefab; [Tooltip("格子预制体属性优先"), LabelText("格子预制体路径")] [ShowIf("@this.cellPrefab == null")] public string cellPrefabPath; private ScrollDirection scrollDirection; [SerializeField, ExposeProperty, Label("方向")] public ScrollDirection Direction { get => scrollDirection; set { scrollDirection = value; ScrollRect scroll = GetComponent(); //Debug.LogError(scrollDirection); switch (value) { case ScrollDirection.TopToDown: case ScrollDirection.DownToTop: scroll.horizontal = false; scroll.vertical = true; break; case ScrollDirection.LeftToRight: case ScrollDirection.RightToLeft: scroll.horizontal = true; scroll.vertical = false; break; default: break; } } } [SerializeField, Label("一行或一列数量")] private int rowOrColNum = 1; [SerializeField, Label("间隔")] private Vector2 specing; #endregion private void Awake() { scrollRect = GetComponent(); if (scrollRect == null) { Debug.LogError("该组件需要对应的ScrollRect! 请检查!!!"); return; } if (content == null) { content = scrollRect?.content; } if (content == null) { Transform viewport = scrollRect?.viewport; content = viewport != null ? viewport?.GetChild(0).GetComponent() : null; } if (content == null) { Debug.LogError("获取不到对应的ScrollView中的content! 请检查!!!"); return; } content.anchorMin = new Vector2(0, 1); content.anchorMax = new Vector2(0, 1); if (cellPrefab == null) { cellPrefab = LoaderTools.LoadAsset(cellPrefabPath); } if (cellPrefab == null) { Debug.LogError("获取不到对应的格子预制体! 请检查!!!"); return; } cellPrefab.SetActive(false); scrollRect.onValueChanged.RemoveAllListeners(); scrollRect.onValueChanged.AddListener(ScrollOnDragListener); } Action actionEvent; List cellInfoList = new List(); public void Init(Action dataAction, int dataMaxNum = -1) { cellInfoList.Clear(); actionEvent = dataAction; if (dataMaxNum != -1) { RefreshData(dataMaxNum); } } public void RefreshData(int dataMaxNum) { if (cellPrefab == null) { Debug.LogError("获取不到对应的格子预制体! 请检查!!!"); return; } foreach (CellInfo info in cellInfoList) { if (info.item != null) { info.item.PushPool(); } info.PushPool(); } for (int i = 0; i < dataMaxNum; i++) { CellInfo cellInfo = PoolManager.Inst.Get(); cellInfo.index = i; cellInfoList.Add(cellInfo); } Vector2 cellSize = cellPrefab.GetSize(); switch (scrollDirection) { case ScrollDirection.TopToDown: case ScrollDirection.DownToTop: int rowNum = dataMaxNum / rowOrColNum + 1; float width = (cellSize.x + specing.x) * rowOrColNum - specing.x; float height = (cellSize.y + specing.y) * rowNum - specing.y; content.gameObject.SetSize(new Vector2(width, height)); for (int i = 0; i < cellInfoList.Count; i++) { CellInfo cellInfo = cellInfoList[i]; int curCol = i % rowOrColNum; int curRow = i / rowOrColNum; float posX = curCol * (cellSize.x + specing.x); float posY = -curRow * (cellSize.y + specing.y); cellInfo.pos = new Vector3(posX, posY); //Debug.Log(cellInfo.pos); } break; case ScrollDirection.LeftToRight: case ScrollDirection.RightToLeft: int colNum = dataMaxNum / rowOrColNum + 1; float contentWidth = (cellSize.x + specing.x) * colNum - specing.x; float contentHeight = (cellSize.y + specing.y) * rowOrColNum - specing.y; content.gameObject.SetSize(new Vector2(contentWidth, contentHeight)); for (int i = 0; i < cellInfoList.Count; i++) { CellInfo cellInfo = cellInfoList[i]; int curRow = i % rowOrColNum; int curCol = i / rowOrColNum; float posX = curCol * (cellSize.x + specing.x); float posY = -curRow * (cellSize.y + specing.y); cellInfo.pos = new Vector3(posX, posY); //Debug.Log(cellInfo.pos); } break; default: break; } CheckCellItemData(); } Vector2 lastPos = Vector2.zero; Vector2 curPos = Vector2.zero; private void ScrollOnDragListener(Vector2 pos) { curPos = pos; if (curPos != lastPos) { //Debug.Log(pos); CheckCellItemData(); } lastPos = curPos; } private void CheckCellItemData() { for (int i = 0; i < cellInfoList.Count; i++) { CellInfo cellInfo = cellInfoList[i]; //Debug.Log(cellInfo.pos); if (IsInScrollRange(cellInfo)) { if (cellInfo.item == null) { cellInfo.item = PoolManager.Inst.Get(cellPrefab, content); cellInfo.item.transform.localPosition = cellInfo.pos; cellInfo.item.name = cellInfo.index.ToString(); } actionEvent?.Invoke(cellInfo.item, cellInfo.index); } else { if (cellInfo.item != null) { cellInfo.item.PushPool(cellPrefab.name); cellInfo.item = null; } } } } private bool IsInScrollRange(CellInfo cellInfo) { Vector2 contentPos = content.anchoredPosition; Vector2 scrollSize = scrollRect.gameObject.GetSize(); Vector2 cellSize = cellPrefab.GetSize(); float topPos = -contentPos.y + cellSize.y; float downPos = -contentPos.y - scrollSize.y; float leftPos = -contentPos.x - cellSize.x; float rightPos = -contentPos.x + scrollSize.x; //Debug.Log($"cellInfo.pos:{cellInfo.pos} topPos:{topPos}/downPos:{downPos}/leftPos:{leftPos}/rightPos:{rightPos}"); if (cellInfo.pos.x >= leftPos && cellInfo.pos.x <= rightPos && cellInfo.pos.y >= downPos && cellInfo.pos.y <= topPos) { return true; } return false; } #if UNITY_EDITOR [MenuItem("GameObject/UIEx/ScrollViewEx/ScrollViewGridEx", priority = -990)] private static void CreateScrollViewEx(MenuCommand menuCmd) { GameObject selection = Selection.activeGameObject; CommonExtension.CreateComponent(selection, (obj) => { obj.SetWidth(800); obj.SetHeight(600); GameObject viewport = ObjectFactory.CreateGameObject("Viewport", typeof(Image), typeof(Mask)); viewport.transform.SetParent(obj.transform, false); RectTransform viewportRect = viewport.GetComponent(); viewportRect.anchorMin = new Vector2(0, 0); viewportRect.anchorMax = new Vector2(1, 1); viewportRect.pivot = new Vector2(0, 1); viewport.SetWidth(0); viewport.SetHeight(0); Mask viewportMask = viewport.GetComponent(); viewportMask.showMaskGraphic = false; GameObject content = ObjectFactory.CreateGameObject("Content", typeof(RectTransform)); content.transform.SetParent(viewport.transform, false); RectTransform contentRect = content.GetComponent(); contentRect.anchorMin = new Vector2(0, 1); contentRect.anchorMax = new Vector2(0, 1); contentRect.pivot = new Vector2(0, 1); content.SetWidth(800); content.SetHeight(600); ScrollRect scrollRect = obj.GetComponent(); scrollRect.content = contentRect; scrollRect.viewport = viewportRect; scrollRect.horizontal = false; scrollRect.vertical = true; ScrollViewGridEx scrollViewEx = obj.GetComponent(); scrollViewEx.content = contentRect; }, typeof(Canvas)); } #endif } }