372 lines
13 KiB
C#
372 lines
13 KiB
C#
|
/********************************************************************
|
||
|
文件: 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,
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// 格子数据
|
||
|
/// </summary>
|
||
|
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<ScrollRect>();
|
||
|
//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<ScrollRect>();
|
||
|
|
||
|
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<RectTransform>() : 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<GameObject>(cellPrefabPath);
|
||
|
}
|
||
|
|
||
|
if (cellPrefab == null)
|
||
|
{
|
||
|
Debug.LogError("获取不到对应的格子预制体! 请检查!!!");
|
||
|
return;
|
||
|
}
|
||
|
cellPrefab.SetActive(false);
|
||
|
|
||
|
scrollRect.onValueChanged.RemoveAllListeners();
|
||
|
scrollRect.onValueChanged.AddListener(ScrollOnDragListener);
|
||
|
}
|
||
|
|
||
|
Action<GameObject, int> actionEvent;
|
||
|
List<CellInfo> cellInfoList = new List<CellInfo>();
|
||
|
|
||
|
public void Init(Action<GameObject, int> 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>();
|
||
|
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 = 1)]
|
||
|
private static void CreateScrollViewEx(MenuCommand menuCmd)
|
||
|
{
|
||
|
GameObject selection = Selection.activeGameObject;
|
||
|
|
||
|
GameObject gameObject;
|
||
|
|
||
|
if (selection != null)
|
||
|
{
|
||
|
Debug.Log($"选择物体:{selection.name}");
|
||
|
|
||
|
// 获取物体的根 Canvas
|
||
|
Canvas rootCanvas = selection.GetComponentInParent<Canvas>();
|
||
|
Transform parentTransform;
|
||
|
|
||
|
if (rootCanvas != null)
|
||
|
{
|
||
|
parentTransform = selection.transform;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var canvas = ObjectFactory.CreateGameObject("Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
||
|
var eventSystem = ObjectFactory.CreateGameObject("EventSystem", typeof(UnityEngine.EventSystems.EventSystem), typeof(UnityEngine.EventSystems.StandaloneInputModule));
|
||
|
|
||
|
parentTransform = canvas.transform;
|
||
|
}
|
||
|
|
||
|
gameObject = ObjectFactory.CreateGameObject("New ScrollViewEx", typeof(ScrollViewGridEx), typeof(ScrollRect), typeof(Canvas));
|
||
|
gameObject.transform.SetParent(parentTransform, false);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var canvas = ObjectFactory.CreateGameObject("Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
||
|
var eventSystem = ObjectFactory.CreateGameObject("EventSystem", typeof(UnityEngine.EventSystems.EventSystem), typeof(UnityEngine.EventSystems.StandaloneInputModule));
|
||
|
|
||
|
canvas.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
|
||
|
Transform parentTransform = canvas.transform;
|
||
|
gameObject = ObjectFactory.CreateGameObject("New ScrollViewEx", typeof(ScrollViewGridEx), typeof(ScrollRect), typeof(Canvas));
|
||
|
gameObject.transform.SetParent(parentTransform, false);
|
||
|
}
|
||
|
|
||
|
gameObject.transform.localPosition = Vector3.zero;
|
||
|
gameObject.transform.localScale = new Vector3(1, 1, 1);
|
||
|
gameObject.SetWidth(800);
|
||
|
gameObject.SetHeight(600);
|
||
|
|
||
|
GameObject viewport = ObjectFactory.CreateGameObject("Viewport", typeof(Image), typeof(Mask));
|
||
|
viewport.transform.SetParent(gameObject.transform, false);
|
||
|
RectTransform viewportRect = viewport.GetComponent<RectTransform>();
|
||
|
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<Mask>();
|
||
|
viewportMask.showMaskGraphic = false;
|
||
|
|
||
|
GameObject content = ObjectFactory.CreateGameObject("Content", typeof(RectTransform));
|
||
|
content.transform.SetParent(viewport.transform, false);
|
||
|
RectTransform contentRect = content.GetComponent<RectTransform>();
|
||
|
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 = gameObject.GetComponent<ScrollRect>();
|
||
|
scrollRect.content = contentRect;
|
||
|
scrollRect.viewport = viewportRect;
|
||
|
scrollRect.horizontal = false;
|
||
|
scrollRect.vertical = true;
|
||
|
|
||
|
ScrollViewGridEx scrollViewEx = gameObject.GetComponent<ScrollViewGridEx>();
|
||
|
scrollViewEx.content = contentRect;
|
||
|
|
||
|
EditorUtility.FocusProjectWindow();
|
||
|
Selection.activeObject = gameObject;
|
||
|
EditorGUIUtility.PingObject(Selection.activeObject);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
}
|