|
@@ -0,0 +1,1352 @@
|
|
|
|
|
+using UnityEngine;
|
|
|
|
|
+using UnityEditor;
|
|
|
|
|
+
|
|
|
|
|
+namespace MapEditor
|
|
|
|
|
+{
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 网格地图编辑器 - 全新实现
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public class GridMapEditor : EditorWindow
|
|
|
|
|
+ {
|
|
|
|
|
+ // Canvas相关
|
|
|
|
|
+ private Canvas targetCanvas;
|
|
|
|
|
+ private Vector2 canvasSize;
|
|
|
|
|
+
|
|
|
|
|
+ // Sprite相关
|
|
|
|
|
+ private Sprite currentSprite;
|
|
|
|
|
+ private Vector2 spriteSize;
|
|
|
|
|
+
|
|
|
|
|
+ // 图层系统
|
|
|
|
|
+ [System.Serializable]
|
|
|
|
|
+ public class SpriteLayer
|
|
|
|
|
+ {
|
|
|
|
|
+ public Sprite sprite;
|
|
|
|
|
+ public string name;
|
|
|
|
|
+ public bool isVisible = true;
|
|
|
|
|
+ public bool isPreviewVisible = true; // 在预览窗体中是否可见
|
|
|
|
|
+
|
|
|
|
|
+ public SpriteLayer(Sprite sprite)
|
|
|
|
|
+ {
|
|
|
|
|
+ this.sprite = sprite;
|
|
|
|
|
+ this.name = sprite != null ? sprite.name : "未命名图层";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private System.Collections.Generic.List<SpriteLayer> spriteLayersList = new System.Collections.Generic.List<SpriteLayer>();
|
|
|
|
|
+ private int selectedLayerIndex = -1;
|
|
|
|
|
+ private Vector2 layerListScrollPosition;
|
|
|
|
|
+ private int draggedLayerIndex = -1;
|
|
|
|
|
+
|
|
|
|
|
+ // 网格相关
|
|
|
|
|
+ private int gridColumns = 0;
|
|
|
|
|
+ private int gridRows = 0;
|
|
|
|
|
+
|
|
|
|
|
+ // UI相关
|
|
|
|
|
+ private Vector2 scrollPosition;
|
|
|
|
|
+ private Rect previewRect;
|
|
|
|
|
+ private float previewScale = 1f;
|
|
|
|
|
+ private bool isPaintingEnabled = true; // 左键绘制,右键擦除
|
|
|
|
|
+ private bool showOtherLayers = true; // 是否显示其他图层(半透明)
|
|
|
|
|
+
|
|
|
|
|
+ // 颜色
|
|
|
|
|
+ private Color gridColor = new Color(0.3f, 0.3f, 0.3f, 0.8f); // 更明显的网格线
|
|
|
|
|
+ private Color backgroundColor = new Color(0.2f, 0.2f, 0.2f, 1f);
|
|
|
|
|
+
|
|
|
|
|
+ // 配置数据模型(仅用于JSON序列化)
|
|
|
|
|
+ [System.Serializable]
|
|
|
|
|
+ private class MapConfig
|
|
|
|
|
+ {
|
|
|
|
|
+ public string canvasName;
|
|
|
|
|
+ public float canvasWidth;
|
|
|
|
|
+ public float canvasHeight;
|
|
|
|
|
+ public System.Collections.Generic.List<LayerConfig> layers = new System.Collections.Generic.List<LayerConfig>();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [System.Serializable]
|
|
|
|
|
+ private class LayerConfig
|
|
|
|
|
+ {
|
|
|
|
|
+ public string name;
|
|
|
|
|
+ public string spriteAssetPath; // 仅编辑器可用
|
|
|
|
|
+ public string resourcesPath; // 运行时可用(Assets/Resources 下)
|
|
|
|
|
+ public bool isVisible;
|
|
|
|
|
+ public bool isPreviewVisible;
|
|
|
|
|
+ public System.Collections.Generic.List<ItemConfig> items = new System.Collections.Generic.List<ItemConfig>();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [System.Serializable]
|
|
|
|
|
+ private class ItemConfig
|
|
|
|
|
+ {
|
|
|
|
|
+ public int cellX;
|
|
|
|
|
+ public int cellY;
|
|
|
|
|
+ public float posX; // Canvas坐标(以中心为原点)
|
|
|
|
|
+ public float posY;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 放置的数据:按Sprite区分的"图层"
|
|
|
|
|
+ private System.Collections.Generic.Dictionary<Sprite, System.Collections.Generic.Dictionary<Vector2Int, GameObject>> spriteLayers = new System.Collections.Generic.Dictionary<Sprite, System.Collections.Generic.Dictionary<Vector2Int, GameObject>>();
|
|
|
|
|
+
|
|
|
|
|
+ // 每个图层的Canvas绝对位置记录
|
|
|
|
|
+ private System.Collections.Generic.Dictionary<Sprite, System.Collections.Generic.Dictionary<Vector2Int, Vector2>> layerCanvasPositions = new System.Collections.Generic.Dictionary<Sprite, System.Collections.Generic.Dictionary<Vector2Int, Vector2>>();
|
|
|
|
|
+
|
|
|
|
|
+ // 单元格间距(Canvas单位,像素),默认0
|
|
|
|
|
+ private Vector2 cellGap = Vector2.zero;
|
|
|
|
|
+
|
|
|
|
|
+ [MenuItem("工具/网格地图编辑器")]
|
|
|
|
|
+ public static void ShowWindow()
|
|
|
|
|
+ {
|
|
|
|
|
+ GridMapEditor window = GetWindow<GridMapEditor>("网格地图编辑器");
|
|
|
|
|
+ window.minSize = new Vector2(600, 600);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void OnGUI()
|
|
|
|
|
+ {
|
|
|
|
|
+ DrawToolbar();
|
|
|
|
|
+ EditorGUILayout.Space(10);
|
|
|
|
|
+
|
|
|
|
|
+ if (targetCanvas != null && spriteLayersList.Count > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ DrawPreviewArea();
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ DrawHelpBox();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 绘制工具栏
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void DrawToolbar()
|
|
|
|
|
+ {
|
|
|
|
|
+ EditorGUILayout.BeginVertical("box");
|
|
|
|
|
+
|
|
|
|
|
+ GUILayout.Label("网格地图编辑器", EditorStyles.boldLabel);
|
|
|
|
|
+ EditorGUILayout.Space(5);
|
|
|
|
|
+
|
|
|
|
|
+ // Canvas选择
|
|
|
|
|
+ EditorGUI.BeginChangeCheck();
|
|
|
|
|
+ Canvas newCanvas = (Canvas)EditorGUILayout.ObjectField(
|
|
|
|
|
+ "目标Canvas",
|
|
|
|
|
+ targetCanvas,
|
|
|
|
|
+ typeof(Canvas),
|
|
|
|
|
+ true
|
|
|
|
|
+ );
|
|
|
|
|
+ if (EditorGUI.EndChangeCheck())
|
|
|
|
|
+ {
|
|
|
|
|
+ targetCanvas = newCanvas;
|
|
|
|
|
+ UpdateCanvasSize();
|
|
|
|
|
+ CalculateGrid();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (targetCanvas != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ EditorGUILayout.LabelField("Canvas大小", $"{canvasSize.x} x {canvasSize.y}");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ EditorGUILayout.Space(5);
|
|
|
|
|
+
|
|
|
|
|
+ // 图层管理
|
|
|
|
|
+ DrawLayerManagement();
|
|
|
|
|
+
|
|
|
|
|
+ EditorGUILayout.Space(5);
|
|
|
|
|
+
|
|
|
|
|
+ // 网格信息
|
|
|
|
|
+ if (gridColumns > 0 && gridRows > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ EditorGUILayout.LabelField("网格信息", $"{gridColumns} 列 x {gridRows} 行 = {gridColumns * gridRows} 个单元格");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ EditorGUILayout.Space(5);
|
|
|
|
|
+
|
|
|
|
|
+ // 单元间距设置
|
|
|
|
|
+ EditorGUI.BeginChangeCheck();
|
|
|
|
|
+ Vector2 newGap = EditorGUILayout.Vector2Field("单元间距", cellGap);
|
|
|
|
|
+ if (EditorGUI.EndChangeCheck())
|
|
|
|
|
+ {
|
|
|
|
|
+ newGap.x = Mathf.Max(0, newGap.x);
|
|
|
|
|
+ newGap.y = Mathf.Max(0, newGap.y);
|
|
|
|
|
+ cellGap = newGap;
|
|
|
|
|
+ CalculateGrid();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 预览缩放
|
|
|
|
|
+ previewScale = EditorGUILayout.Slider("预览缩放", previewScale, 0.1f, 2f);
|
|
|
|
|
+
|
|
|
|
|
+ // 其他图层显示开关
|
|
|
|
|
+ showOtherLayers = EditorGUILayout.Toggle("显示其他图层", showOtherLayers);
|
|
|
|
|
+
|
|
|
|
|
+ EditorGUILayout.Space(10);
|
|
|
|
|
+
|
|
|
|
|
+ // 配置保存/加载
|
|
|
|
|
+ EditorGUILayout.BeginHorizontal();
|
|
|
|
|
+ if (GUILayout.Button("保存配置", GUILayout.Height(24)))
|
|
|
|
|
+ {
|
|
|
|
|
+ SaveConfigToFile();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (GUILayout.Button("加载配置", GUILayout.Height(24)))
|
|
|
|
|
+ {
|
|
|
|
|
+ LoadConfigFromFile();
|
|
|
|
|
+ }
|
|
|
|
|
+ EditorGUILayout.EndHorizontal();
|
|
|
|
|
+
|
|
|
|
|
+ EditorGUILayout.Space(5);
|
|
|
|
|
+
|
|
|
|
|
+ // 保存按钮
|
|
|
|
|
+ GUI.enabled = targetCanvas != null && HasPlacedSprites();
|
|
|
|
|
+ if (GUILayout.Button("保存为纹理贴图", GUILayout.Height(30)))
|
|
|
|
|
+ {
|
|
|
|
|
+ SaveAsTexture();
|
|
|
|
|
+ }
|
|
|
|
|
+ GUI.enabled = true;
|
|
|
|
|
+
|
|
|
|
|
+ EditorGUILayout.EndVertical();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 绘制图层管理UI
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void DrawLayerManagement()
|
|
|
|
|
+ {
|
|
|
|
|
+ EditorGUILayout.BeginVertical("box");
|
|
|
|
|
+ GUILayout.Label("图层管理", EditorStyles.boldLabel);
|
|
|
|
|
+
|
|
|
|
|
+ // 添加新图层按钮
|
|
|
|
|
+ EditorGUILayout.BeginHorizontal();
|
|
|
|
|
+ if (GUILayout.Button("添加图层", GUILayout.Height(25)))
|
|
|
|
|
+ {
|
|
|
|
|
+ // 打开Sprite选择对话框
|
|
|
|
|
+ string path = EditorUtility.OpenFilePanel("选择Sprite", "Assets", "png");
|
|
|
|
|
+ if (!string.IsNullOrEmpty(path))
|
|
|
|
|
+ {
|
|
|
|
|
+ // 将绝对路径转换为相对路径
|
|
|
|
|
+ if (path.StartsWith(Application.dataPath))
|
|
|
|
|
+ {
|
|
|
|
|
+ path = "Assets" + path.Substring(Application.dataPath.Length);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 加载Sprite
|
|
|
|
|
+ Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path);
|
|
|
|
|
+ if (sprite != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ AddLayer(sprite);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ EditorUtility.DisplayDialog("错误", "无法加载Sprite,请确保选择的是有效的图片文件", "确定");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (GUILayout.Button("清空所有图层", GUILayout.Height(25)))
|
|
|
|
|
+ {
|
|
|
|
|
+ if (EditorUtility.DisplayDialog("确认清空", "确定要清空所有图层吗?", "确定", "取消"))
|
|
|
|
|
+ {
|
|
|
|
|
+ ClearAllLayers();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ EditorGUILayout.EndHorizontal();
|
|
|
|
|
+
|
|
|
|
|
+ EditorGUILayout.Space(5);
|
|
|
|
|
+
|
|
|
|
|
+ // 图层列表
|
|
|
|
|
+ if (spriteLayersList.Count > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ layerListScrollPosition = EditorGUILayout.BeginScrollView(layerListScrollPosition, GUILayout.Height(150));
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = 0; i < spriteLayersList.Count; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ DrawLayerItem(i);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ EditorGUILayout.EndScrollView();
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ EditorGUILayout.HelpBox("暂无图层,请添加图层开始编辑", MessageType.Info);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ EditorGUILayout.EndVertical();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 绘制单个图层项
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void DrawLayerItem(int index)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (index >= spriteLayersList.Count) return;
|
|
|
|
|
+
|
|
|
|
|
+ var layer = spriteLayersList[index];
|
|
|
|
|
+ bool isSelected = selectedLayerIndex == index;
|
|
|
|
|
+
|
|
|
|
|
+ EditorGUILayout.BeginHorizontal();
|
|
|
|
|
+
|
|
|
|
|
+ // 选择按钮
|
|
|
|
|
+ if (GUILayout.Button(isSelected ? "●" : "○", GUILayout.Width(20), GUILayout.Height(20)))
|
|
|
|
|
+ {
|
|
|
|
|
+ selectedLayerIndex = index;
|
|
|
|
|
+ currentSprite = layer.sprite;
|
|
|
|
|
+ UpdateSpriteSize();
|
|
|
|
|
+ CalculateGrid();
|
|
|
|
|
+ Repaint();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 场景可见性切换
|
|
|
|
|
+ EditorGUILayout.LabelField("场景", GUILayout.Width(30));
|
|
|
|
|
+ layer.isVisible = EditorGUILayout.Toggle(layer.isVisible, GUILayout.Width(20));
|
|
|
|
|
+
|
|
|
|
|
+ // 预览可见性切换
|
|
|
|
|
+ EditorGUILayout.LabelField("预览", GUILayout.Width(30));
|
|
|
|
|
+ layer.isPreviewVisible = EditorGUILayout.Toggle(layer.isPreviewVisible, GUILayout.Width(20));
|
|
|
|
|
+
|
|
|
|
|
+ // 图层名称
|
|
|
|
|
+ EditorGUILayout.LabelField(layer.name, isSelected ? EditorStyles.boldLabel : EditorStyles.label);
|
|
|
|
|
+
|
|
|
|
|
+ // 上下移动按钮
|
|
|
|
|
+ EditorGUILayout.BeginVertical();
|
|
|
|
|
+ if (GUILayout.Button("↑", GUILayout.Width(20), GUILayout.Height(15)))
|
|
|
|
|
+ {
|
|
|
|
|
+ MoveLayerUp(index);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (GUILayout.Button("↓", GUILayout.Width(20), GUILayout.Height(15)))
|
|
|
|
|
+ {
|
|
|
|
|
+ MoveLayerDown(index);
|
|
|
|
|
+ }
|
|
|
|
|
+ EditorGUILayout.EndVertical();
|
|
|
|
|
+
|
|
|
|
|
+ // 删除按钮
|
|
|
|
|
+ if (GUILayout.Button("×", GUILayout.Width(20), GUILayout.Height(20)))
|
|
|
|
|
+ {
|
|
|
|
|
+ if (EditorUtility.DisplayDialog("删除图层", $"确定要删除图层 '{layer.name}' 吗?", "确定", "取消"))
|
|
|
|
|
+ {
|
|
|
|
|
+ RemoveLayer(index);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ EditorGUILayout.EndHorizontal();
|
|
|
|
|
+
|
|
|
|
|
+ // 拖拽处理
|
|
|
|
|
+ HandleLayerDrag(index);
|
|
|
|
|
+
|
|
|
|
|
+ EditorGUILayout.Space(2);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 处理图层拖拽(简化版本)
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void HandleLayerDrag(int index)
|
|
|
|
|
+ {
|
|
|
|
|
+ // 暂时禁用拖拽功能,避免卡顿
|
|
|
|
|
+ // 可以通过上下箭头按钮来调整顺序
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 绘制预览区域
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void DrawPreviewArea()
|
|
|
|
|
+ {
|
|
|
|
|
+ EditorGUILayout.BeginVertical("box");
|
|
|
|
|
+ GUILayout.Label("预览区域", EditorStyles.boldLabel);
|
|
|
|
|
+
|
|
|
|
|
+ // 计算预览区域(取Canvas与网格尺寸中较大者)
|
|
|
|
|
+ Vector2 cellSizeCanvas = new Vector2(spriteSize.x + cellGap.x, spriteSize.y + cellGap.y);
|
|
|
|
|
+ float gridCanvasWidth = Mathf.Max(1, gridColumns) * cellSizeCanvas.x;
|
|
|
|
|
+ float gridCanvasHeight = Mathf.Max(1, gridRows) * cellSizeCanvas.y;
|
|
|
|
|
+ float drawCanvasWidth = Mathf.Max(canvasSize.x, gridCanvasWidth);
|
|
|
|
|
+ float drawCanvasHeight = Mathf.Max(canvasSize.y, gridCanvasHeight);
|
|
|
|
|
+
|
|
|
|
|
+ float scaledWidth = drawCanvasWidth * previewScale;
|
|
|
|
|
+ float scaledHeight = drawCanvasHeight * previewScale;
|
|
|
|
|
+
|
|
|
|
|
+ // 创建滚动视图
|
|
|
|
|
+ scrollPosition = EditorGUILayout.BeginScrollView(
|
|
|
|
|
+ scrollPosition,
|
|
|
|
|
+ GUILayout.ExpandWidth(true),
|
|
|
|
|
+ GUILayout.ExpandHeight(true)
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 获取预览区域的矩形
|
|
|
|
|
+ previewRect = GUILayoutUtility.GetRect(
|
|
|
|
|
+ scaledWidth,
|
|
|
|
|
+ scaledHeight,
|
|
|
|
|
+ GUILayout.ExpandWidth(false),
|
|
|
|
|
+ GUILayout.ExpandHeight(false)
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 绘制背景(保持透明,不覆盖网格线)
|
|
|
|
|
+ // EditorGUI.DrawRect(previewRect, backgroundColor);
|
|
|
|
|
+
|
|
|
|
|
+ // 绘制网格
|
|
|
|
|
+ DrawGrid(previewRect);
|
|
|
|
|
+
|
|
|
|
|
+ // 处理鼠标
|
|
|
|
|
+ HandleMouseInPreview(previewRect);
|
|
|
|
|
+
|
|
|
|
|
+ EditorGUILayout.EndScrollView();
|
|
|
|
|
+ EditorGUILayout.EndVertical();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 绘制网格
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void DrawGrid(Rect rect)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (gridColumns <= 0 || gridRows <= 0) return;
|
|
|
|
|
+
|
|
|
|
|
+ Handles.BeginGUI();
|
|
|
|
|
+
|
|
|
|
|
+ // 将Canvas单位映射到预览区域,保持等比(使用绘制区域的Canvas尺寸)
|
|
|
|
|
+ Vector2 cellSizeCanvas = new Vector2(spriteSize.x + cellGap.x, spriteSize.y + cellGap.y);
|
|
|
|
|
+ float gridCanvasWidth = Mathf.Max(1, gridColumns) * cellSizeCanvas.x;
|
|
|
|
|
+ float gridCanvasHeight = Mathf.Max(1, gridRows) * cellSizeCanvas.y;
|
|
|
|
|
+ float drawCanvasWidth = Mathf.Max(canvasSize.x, gridCanvasWidth);
|
|
|
|
|
+ float drawCanvasHeight = Mathf.Max(canvasSize.y, gridCanvasHeight);
|
|
|
|
|
+ float scaleX = rect.width / drawCanvasWidth;
|
|
|
|
|
+ float scaleY = rect.height / drawCanvasHeight;
|
|
|
|
|
+
|
|
|
|
|
+ // 计算Canvas在预览区域中的位置(居中显示)
|
|
|
|
|
+ float canvasOffsetX = (rect.width - drawCanvasWidth * scaleX) / 2f;
|
|
|
|
|
+ float canvasOffsetY = (rect.height - drawCanvasHeight * scaleY) / 2f;
|
|
|
|
|
+
|
|
|
|
|
+ float cellWidthCanvas = spriteSize.x + cellGap.x;
|
|
|
|
|
+ float cellHeightCanvas = spriteSize.y + cellGap.y;
|
|
|
|
|
+ float cellWidth = cellWidthCanvas * scaleX;
|
|
|
|
|
+ float cellHeight = cellHeightCanvas * scaleY;
|
|
|
|
|
+ float gridWidth = gridColumns * cellWidth;
|
|
|
|
|
+ float gridHeight = gridRows * cellHeight;
|
|
|
|
|
+
|
|
|
|
|
+ // 网格起始位置(考虑Canvas偏移)
|
|
|
|
|
+ float gridStartX = rect.x + canvasOffsetX;
|
|
|
|
|
+ float gridStartY = rect.y + canvasOffsetY;
|
|
|
|
|
+
|
|
|
|
|
+ // 绘制垂直线
|
|
|
|
|
+ for (int x = 0; x <= gridColumns; x++)
|
|
|
|
|
+ {
|
|
|
|
|
+ float xPos = gridStartX + x * cellWidth;
|
|
|
|
|
+ Handles.color = gridColor;
|
|
|
|
|
+ Handles.DrawLine(
|
|
|
|
|
+ new Vector3(xPos, gridStartY, 0),
|
|
|
|
|
+ new Vector3(xPos, gridStartY + gridHeight, 0)
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 绘制水平线
|
|
|
|
|
+ for (int y = 0; y <= gridRows; y++)
|
|
|
|
|
+ {
|
|
|
|
|
+ float yPos = gridStartY + y * cellHeight;
|
|
|
|
|
+ Handles.color = gridColor;
|
|
|
|
|
+ Handles.DrawLine(
|
|
|
|
|
+ new Vector3(gridStartX, yPos, 0),
|
|
|
|
|
+ new Vector3(gridStartX + gridWidth, yPos, 0)
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 绘制已放置的Sprite预览
|
|
|
|
|
+ if (currentSprite != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ DrawSpriteGrid(rect, cellWidth, cellHeight);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Handles.EndGUI();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 在网格中绘制Sprite
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void DrawSpriteGrid(Rect rect, float cellWidth, float cellHeight)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (spriteLayersList.Count == 0) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 计算Canvas在预览区域中的缩放和偏移
|
|
|
|
|
+ Vector2 cellSizeCanvas = new Vector2(spriteSize.x + cellGap.x, spriteSize.y + cellGap.y);
|
|
|
|
|
+ float gridCanvasWidth = Mathf.Max(1, gridColumns) * cellSizeCanvas.x;
|
|
|
|
|
+ float gridCanvasHeight = Mathf.Max(1, gridRows) * cellSizeCanvas.y;
|
|
|
|
|
+ float drawCanvasWidth = Mathf.Max(canvasSize.x, gridCanvasWidth);
|
|
|
|
|
+ float drawCanvasHeight = Mathf.Max(canvasSize.y, gridCanvasHeight);
|
|
|
|
|
+ float scaleX = rect.width / drawCanvasWidth;
|
|
|
|
|
+ float scaleY = rect.height / drawCanvasHeight;
|
|
|
|
|
+
|
|
|
|
|
+ // 计算Canvas在预览区域中的位置(居中显示)
|
|
|
|
|
+ float canvasOffsetX = (rect.width - drawCanvasWidth * scaleX) / 2f;
|
|
|
|
|
+ float canvasOffsetY = (rect.height - drawCanvasHeight * scaleY) / 2f;
|
|
|
|
|
+
|
|
|
|
|
+ // 渲染所有图层(按顺序,从下到上)
|
|
|
|
|
+ for (int layerIndex = 0; layerIndex < spriteLayersList.Count; layerIndex++)
|
|
|
|
|
+ {
|
|
|
|
|
+ var layer = spriteLayersList[layerIndex];
|
|
|
|
|
+ if (!layer.isVisible || !layer.isPreviewVisible || layer.sprite == null) continue;
|
|
|
|
|
+
|
|
|
|
|
+ bool isSelectedLayer = layerIndex == selectedLayerIndex;
|
|
|
|
|
+
|
|
|
|
|
+ // 根据开关决定是否显示其他图层
|
|
|
|
|
+ if (!isSelectedLayer && !showOtherLayers) continue;
|
|
|
|
|
+
|
|
|
|
|
+ float layerOpacity = isSelectedLayer ? 1f : 0.5f; // 非选中图层半透明
|
|
|
|
|
+
|
|
|
|
|
+ // 设置透明度
|
|
|
|
|
+ Color originalColor = GUI.color;
|
|
|
|
|
+ GUI.color = new Color(1f, 1f, 1f, layerOpacity);
|
|
|
|
|
+
|
|
|
|
|
+ // 绘制该图层的所有Sprite(使用原始尺寸)
|
|
|
|
|
+ DrawLayerSpritesWithOriginalSize(rect, layer, canvasOffsetX, canvasOffsetY, scaleX, scaleY);
|
|
|
|
|
+
|
|
|
|
|
+ // 恢复颜色
|
|
|
|
|
+ GUI.color = originalColor;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 绘制单个图层的所有Sprite(使用记录的Canvas绝对位置)
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void DrawLayerSpritesWithOriginalSize(Rect rect, SpriteLayer layer, float canvasOffsetX, float canvasOffsetY, float scaleX, float scaleY)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (layer.sprite == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ Texture2D texture = layer.sprite.texture;
|
|
|
|
|
+ Rect spriteRect = layer.sprite.rect;
|
|
|
|
|
+
|
|
|
|
|
+ // 计算UV坐标
|
|
|
|
|
+ Rect uv = new Rect(
|
|
|
|
|
+ spriteRect.x / texture.width,
|
|
|
|
|
+ spriteRect.y / texture.height,
|
|
|
|
|
+ spriteRect.width / texture.width,
|
|
|
|
|
+ spriteRect.height / texture.height
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 获取该图层的Sprite数据
|
|
|
|
|
+ if (!spriteLayers.ContainsKey(layer.sprite)) return;
|
|
|
|
|
+ var layerData = spriteLayers[layer.sprite];
|
|
|
|
|
+
|
|
|
|
|
+ // 绘制该图层中已放置的Sprite
|
|
|
|
|
+ foreach (var kvp in layerData)
|
|
|
|
|
+ {
|
|
|
|
|
+ Vector2Int cell = kvp.Key;
|
|
|
|
|
+
|
|
|
|
|
+ // 使用记录的Canvas绝对位置
|
|
|
|
|
+ if (!layerCanvasPositions.ContainsKey(layer.sprite) || !layerCanvasPositions[layer.sprite].ContainsKey(cell))
|
|
|
|
|
+ {
|
|
|
|
|
+ continue; // 没有位置记录,跳过
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Vector2 canvasPosition = layerCanvasPositions[layer.sprite][cell];
|
|
|
|
|
+
|
|
|
|
|
+ // 将Canvas坐标转换为预览区域坐标
|
|
|
|
|
+ // Canvas坐标系:中心为原点,向右为+X,向上为+Y
|
|
|
|
|
+ // 预览区域坐标系:左上角为原点,向右为+X,向下为+Y
|
|
|
|
|
+ float previewX = rect.x + canvasOffsetX + (canvasPosition.x + canvasSize.x / 2f) * scaleX;
|
|
|
|
|
+ float previewY = rect.y + canvasOffsetY + (canvasSize.y / 2f - canvasPosition.y) * scaleY;
|
|
|
|
|
+
|
|
|
|
|
+ // 使用该图层Sprite的原始尺寸
|
|
|
|
|
+ Vector2 originalSpriteSize = new Vector2(spriteRect.width, spriteRect.height);
|
|
|
|
|
+
|
|
|
|
|
+ // 计算Sprite在预览区域中的实际尺寸(保持原始比例)
|
|
|
|
|
+ float spriteScaleX = originalSpriteSize.x * scaleX;
|
|
|
|
|
+ float spriteScaleY = originalSpriteSize.y * scaleY;
|
|
|
|
|
+
|
|
|
|
|
+ // 计算居中位置
|
|
|
|
|
+ float centerX = previewX - spriteScaleX / 2f;
|
|
|
|
|
+ float centerY = previewY - spriteScaleY / 2f;
|
|
|
|
|
+
|
|
|
|
|
+ Rect cellRect = new Rect(centerX, centerY, spriteScaleX, spriteScaleY);
|
|
|
|
|
+
|
|
|
|
|
+ GUI.DrawTextureWithTexCoords(cellRect, texture, uv);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 绘制单个图层的所有Sprite(旧方法,保持兼容)
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void DrawLayerSprites(Rect rect, SpriteLayer layer, float canvasOffsetX, float canvasOffsetY, float scaleX, float scaleY)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (layer.sprite == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ Texture2D texture = layer.sprite.texture;
|
|
|
|
|
+ Rect spriteRect = layer.sprite.rect;
|
|
|
|
|
+
|
|
|
|
|
+ // 计算UV坐标
|
|
|
|
|
+ Rect uv = new Rect(
|
|
|
|
|
+ spriteRect.x / texture.width,
|
|
|
|
|
+ spriteRect.y / texture.height,
|
|
|
|
|
+ spriteRect.width / texture.width,
|
|
|
|
|
+ spriteRect.height / texture.height
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 获取该图层的Sprite数据
|
|
|
|
|
+ if (!spriteLayers.ContainsKey(layer.sprite)) return;
|
|
|
|
|
+ var layerData = spriteLayers[layer.sprite];
|
|
|
|
|
+
|
|
|
|
|
+ float cellWidthCanvas = spriteSize.x + cellGap.x;
|
|
|
|
|
+ float cellHeightCanvas = spriteSize.y + cellGap.y;
|
|
|
|
|
+
|
|
|
|
|
+ // 绘制该图层中已放置的Sprite
|
|
|
|
|
+ foreach (var kvp in layerData)
|
|
|
|
|
+ {
|
|
|
|
|
+ Vector2Int cell = kvp.Key;
|
|
|
|
|
+ Rect cellRect = new Rect(
|
|
|
|
|
+ rect.x + canvasOffsetX + cell.x * cellWidthCanvas * scaleX,
|
|
|
|
|
+ rect.y + canvasOffsetY + cell.y * cellHeightCanvas * scaleY,
|
|
|
|
|
+ spriteSize.x * scaleX,
|
|
|
|
|
+ spriteSize.y * scaleY
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ GUI.DrawTextureWithTexCoords(cellRect, texture, uv);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 处理预览区域内的鼠标事件(左键放置,右键删除)
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void HandleMouseInPreview(Rect rect)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!isPaintingEnabled || selectedLayerIndex < 0 || selectedLayerIndex >= spriteLayersList.Count || targetCanvas == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ var selectedLayer = spriteLayersList[selectedLayerIndex];
|
|
|
|
|
+ if (selectedLayer.sprite == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ Event e = Event.current;
|
|
|
|
|
+ if (e == null) return;
|
|
|
|
|
+ if (e.isMouse && (e.type == EventType.MouseDown || e.type == EventType.MouseDrag))
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!rect.Contains(e.mousePosition)) return;
|
|
|
|
|
+ // 只在MouseDown或按住拖动时响应
|
|
|
|
|
+ int button = e.button; // 0左 1右
|
|
|
|
|
+ Vector2Int? cell = ScreenToCell(rect, e.mousePosition);
|
|
|
|
|
+ if (!cell.HasValue) return;
|
|
|
|
|
+ if (button == 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ PlaceSpriteAtCell(cell.Value, selectedLayer.sprite);
|
|
|
|
|
+ e.Use();
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (button == 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ RemoveSpriteAtCell(cell.Value, selectedLayer.sprite);
|
|
|
|
|
+ e.Use();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 将预览坐标转换为网格坐标
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private Vector2Int? ScreenToCell(Rect rect, Vector2 mousePos)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (gridColumns <= 0 || gridRows <= 0) return null;
|
|
|
|
|
+
|
|
|
|
|
+ Vector2 cellSizeCanvas = new Vector2(spriteSize.x + cellGap.x, spriteSize.y + cellGap.y);
|
|
|
|
|
+ float gridCanvasWidth = Mathf.Max(1, gridColumns) * cellSizeCanvas.x;
|
|
|
|
|
+ float gridCanvasHeight = Mathf.Max(1, gridRows) * cellSizeCanvas.y;
|
|
|
|
|
+ float drawCanvasWidth = Mathf.Max(canvasSize.x, gridCanvasWidth);
|
|
|
|
|
+ float drawCanvasHeight = Mathf.Max(canvasSize.y, gridCanvasHeight);
|
|
|
|
|
+ float scaleX = rect.width / drawCanvasWidth;
|
|
|
|
|
+ float scaleY = rect.height / drawCanvasHeight;
|
|
|
|
|
+
|
|
|
|
|
+ // 计算Canvas在预览区域中的位置(居中显示)
|
|
|
|
|
+ float canvasOffsetX = (rect.width - drawCanvasWidth * scaleX) / 2f;
|
|
|
|
|
+ float canvasOffsetY = (rect.height - drawCanvasHeight * scaleY) / 2f;
|
|
|
|
|
+
|
|
|
|
|
+ float cellWidthCanvas = cellSizeCanvas.x;
|
|
|
|
|
+ float cellHeightCanvas = cellSizeCanvas.y;
|
|
|
|
|
+
|
|
|
|
|
+ // 转换为Canvas坐标系
|
|
|
|
|
+ float localXCanvas = (mousePos.x - rect.x - canvasOffsetX) / scaleX;
|
|
|
|
|
+ float localYCanvas = (mousePos.y - rect.y - canvasOffsetY) / scaleY;
|
|
|
|
|
+
|
|
|
|
|
+ int col = Mathf.FloorToInt(localXCanvas / cellWidthCanvas);
|
|
|
|
|
+ int row = Mathf.FloorToInt(localYCanvas / cellHeightCanvas);
|
|
|
|
|
+ col = Mathf.Clamp(col, 0, gridColumns - 1);
|
|
|
|
|
+ row = Mathf.Clamp(row, 0, gridRows - 1);
|
|
|
|
|
+ return new Vector2Int(col, row);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 在Canvas上指定单元放置一个Image
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void PlaceSpriteAtCell(Vector2Int cell, Sprite sprite)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (sprite == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ if (!spriteLayers.ContainsKey(sprite))
|
|
|
|
|
+ {
|
|
|
|
|
+ spriteLayers[sprite] = new System.Collections.Generic.Dictionary<Vector2Int, GameObject>();
|
|
|
|
|
+ layerCanvasPositions[sprite] = new System.Collections.Generic.Dictionary<Vector2Int, Vector2>();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (spriteLayers[sprite].ContainsKey(cell)) return; // 已存在
|
|
|
|
|
+
|
|
|
|
|
+ GameObject parent = GetOrCreateLayerParent(sprite);
|
|
|
|
|
+ GameObject go = new GameObject($"Cell_{cell.x}_{cell.y}");
|
|
|
|
|
+ go.transform.SetParent(parent.transform, false);
|
|
|
|
|
+ var img = go.AddComponent<UnityEngine.UI.Image>();
|
|
|
|
|
+ img.sprite = sprite;
|
|
|
|
|
+
|
|
|
|
|
+ RectTransform rectTransform = go.GetComponent<RectTransform>();
|
|
|
|
|
+ rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
|
|
|
|
|
+ rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
|
|
|
|
+
|
|
|
|
|
+ // 使用该Sprite的原始尺寸
|
|
|
|
|
+ Vector2 spriteSize = new Vector2(sprite.rect.width, sprite.rect.height);
|
|
|
|
|
+ rectTransform.sizeDelta = spriteSize;
|
|
|
|
|
+
|
|
|
|
|
+ // 计算该单元在Canvas中的锚定位置(以Canvas中心为原点,右为+X,上为+Y)
|
|
|
|
|
+ // 使用当前选中图层的网格尺寸来计算位置
|
|
|
|
|
+ float cellWCanvas = this.spriteSize.x + cellGap.x;
|
|
|
|
|
+ float cellHCanvas = this.spriteSize.y + cellGap.y;
|
|
|
|
|
+ float x = -canvasSize.x / 2f + cell.x * cellWCanvas + this.spriteSize.x / 2f;
|
|
|
|
|
+ float y = canvasSize.y / 2f - cell.y * cellHCanvas - this.spriteSize.y / 2f;
|
|
|
|
|
+
|
|
|
|
|
+ // 记录Canvas绝对位置
|
|
|
|
|
+ Vector2 canvasPosition = new Vector2(x, y);
|
|
|
|
|
+ layerCanvasPositions[sprite][cell] = canvasPosition;
|
|
|
|
|
+ rectTransform.anchoredPosition = canvasPosition;
|
|
|
|
|
+
|
|
|
|
|
+ spriteLayers[sprite][cell] = go;
|
|
|
|
|
+ Repaint();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 删除指定单元的Image
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void RemoveSpriteAtCell(Vector2Int cell, Sprite sprite)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (sprite == null || !spriteLayers.ContainsKey(sprite)) return;
|
|
|
|
|
+ if (!spriteLayers[sprite].ContainsKey(cell)) return;
|
|
|
|
|
+ var go = spriteLayers[sprite][cell];
|
|
|
|
|
+ if (go != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ GameObject.DestroyImmediate(go);
|
|
|
|
|
+ }
|
|
|
|
|
+ spriteLayers[sprite].Remove(cell);
|
|
|
|
|
+
|
|
|
|
|
+ // 清理位置记录
|
|
|
|
|
+ if (layerCanvasPositions.ContainsKey(sprite) && layerCanvasPositions[sprite].ContainsKey(cell))
|
|
|
|
|
+ {
|
|
|
|
|
+ layerCanvasPositions[sprite].Remove(cell);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Repaint();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 按当前Sprite获取或创建一个层级父节点
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private GameObject GetOrCreateLayerParent(Sprite sprite)
|
|
|
|
|
+ {
|
|
|
|
|
+ string parentName = $"GridLayer_{sprite.name}";
|
|
|
|
|
+ Transform existing = targetCanvas.transform.Find(parentName);
|
|
|
|
|
+ if (existing != null) return existing.gameObject;
|
|
|
|
|
+ GameObject parent = new GameObject(parentName);
|
|
|
|
|
+ parent.transform.SetParent(targetCanvas.transform, false);
|
|
|
|
|
+ return parent;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 绘制帮助框
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void DrawHelpBox()
|
|
|
|
|
+ {
|
|
|
|
|
+ EditorGUILayout.BeginVertical("box");
|
|
|
|
|
+ EditorGUILayout.HelpBox(
|
|
|
|
|
+ "请先完成以下设置:\n\n" +
|
|
|
|
|
+ "1. 选择一个Canvas(用于确定画布大小)\n" +
|
|
|
|
|
+ "2. 添加图层并选择Sprite图片(用于确定网格单元大小)\n\n" +
|
|
|
|
|
+ "编辑器会自动计算网格数量并铺满Canvas",
|
|
|
|
|
+ MessageType.Info
|
|
|
|
|
+ );
|
|
|
|
|
+ EditorGUILayout.EndVertical();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 更新Canvas大小
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void UpdateCanvasSize()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (targetCanvas == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ canvasSize = Vector2.zero;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ RectTransform rectTransform = targetCanvas.GetComponent<RectTransform>();
|
|
|
|
|
+ if (rectTransform != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ canvasSize = rectTransform.sizeDelta;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ canvasSize = new Vector2(800, 600); // 默认值
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 更新Sprite大小
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void UpdateSpriteSize()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (currentSprite == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ spriteSize = Vector2.zero;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 使用Sprite的像素大小
|
|
|
|
|
+ spriteSize = new Vector2(currentSprite.rect.width, currentSprite.rect.height);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 计算网格
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void CalculateGrid()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (targetCanvas == null || currentSprite == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ gridColumns = 0;
|
|
|
|
|
+ gridRows = 0;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (spriteSize.x <= 0 || spriteSize.y <= 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ gridColumns = 0;
|
|
|
|
|
+ gridRows = 0;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 计算需要多少个单元以覆盖至少Canvas尺寸(向上取整)
|
|
|
|
|
+ float cellW = spriteSize.x + cellGap.x;
|
|
|
|
|
+ float cellH = spriteSize.y + cellGap.y;
|
|
|
|
|
+ gridColumns = Mathf.Max(1, Mathf.CeilToInt(canvasSize.x / Mathf.Max(1f, cellW)));
|
|
|
|
|
+ gridRows = Mathf.Max(1, Mathf.CeilToInt(canvasSize.y / Mathf.Max(1f, cellH)));
|
|
|
|
|
+
|
|
|
|
|
+ Repaint();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 检查是否有放置的Sprite
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private bool HasPlacedSprites()
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (var layer in spriteLayers.Values)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (layer.Count > 0) return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 保存为纹理贴图
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void SaveAsTexture()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (targetCanvas == null || currentSprite == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 直接使用Canvas的实际尺寸作为纹理尺寸,不进行任何缩放
|
|
|
|
|
+ int textureWidth = Mathf.RoundToInt(canvasSize.x);
|
|
|
|
|
+ int textureHeight = Mathf.RoundToInt(canvasSize.y);
|
|
|
|
|
+
|
|
|
|
|
+ // Canvas坐标系:中心为原点,向右为+X,向上为+Y
|
|
|
|
|
+ // 纹理坐标系:左下角为原点,向右为+X,向上为+Y
|
|
|
|
|
+ float minX = -canvasSize.x / 2f;
|
|
|
|
|
+ float minY = -canvasSize.y / 2f;
|
|
|
|
|
+ float maxX = canvasSize.x / 2f;
|
|
|
|
|
+ float maxY = canvasSize.y / 2f;
|
|
|
|
|
+
|
|
|
|
|
+ // 收集所有已放置的Sprite信息(按图层顺序)
|
|
|
|
|
+ var entries = new System.Collections.Generic.List<(Sprite sprite, Texture2D tex, Rect spriteRect, Vector2 size, Vector2 center)>();
|
|
|
|
|
+
|
|
|
|
|
+ // 按图层列表的顺序收集Sprite(确保渲染顺序正确)
|
|
|
|
|
+ foreach (var layerInfo in spriteLayersList)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!layerInfo.isVisible || layerInfo.sprite == null) continue;
|
|
|
|
|
+
|
|
|
|
|
+ if (spriteLayers.ContainsKey(layerInfo.sprite))
|
|
|
|
|
+ {
|
|
|
|
|
+ var dict = spriteLayers[layerInfo.sprite];
|
|
|
|
|
+ foreach (var kvp in dict)
|
|
|
|
|
+ {
|
|
|
|
|
+ GameObject go = kvp.Value;
|
|
|
|
|
+ if (go == null) continue;
|
|
|
|
|
+ var rt = go.GetComponent<RectTransform>();
|
|
|
|
|
+ var img = go.GetComponent<UnityEngine.UI.Image>();
|
|
|
|
|
+ if (rt == null || img == null || img.sprite == null) continue;
|
|
|
|
|
+
|
|
|
|
|
+ Vector2 size = rt.sizeDelta; // 实际显示尺寸(像素)
|
|
|
|
|
+ Vector2 center = rt.anchoredPosition; // 以Canvas中心为原点
|
|
|
|
|
+
|
|
|
|
|
+ var sp = img.sprite;
|
|
|
|
|
+ entries.Add((sp, sp.texture, sp.rect, size, center));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建纹理
|
|
|
|
|
+ Texture2D finalTexture = new Texture2D(textureWidth, textureHeight, TextureFormat.RGBA32, false);
|
|
|
|
|
+ // 填充透明背景(确保整个Canvas范围都是透明的)
|
|
|
|
|
+ Color[] pixels = new Color[textureWidth * textureHeight];
|
|
|
|
|
+ for (int i = 0; i < pixels.Length; i++) pixels[i] = Color.clear;
|
|
|
|
|
+ finalTexture.SetPixels(pixels);
|
|
|
|
|
+
|
|
|
|
|
+ // 2) 绘制每个已放置的Sprite元素:只渲染在Canvas范围内的部分
|
|
|
|
|
+ foreach (var e in entries)
|
|
|
|
|
+ {
|
|
|
|
|
+ // 计算Sprite在Canvas坐标系中的边界
|
|
|
|
|
+ float spriteLeft = e.center.x - e.size.x / 2f;
|
|
|
|
|
+ float spriteRight = e.center.x + e.size.x / 2f;
|
|
|
|
|
+ float spriteBottom = e.center.y - e.size.y / 2f;
|
|
|
|
|
+ float spriteTop = e.center.y + e.size.y / 2f;
|
|
|
|
|
+
|
|
|
|
|
+ // 检查Sprite是否与Canvas范围有交集
|
|
|
|
|
+ if (spriteRight <= minX || spriteLeft >= maxX || spriteTop <= minY || spriteBottom >= maxY)
|
|
|
|
|
+ {
|
|
|
|
|
+ continue; // 完全超出Canvas范围,跳过
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 计算在Canvas范围内的裁剪区域
|
|
|
|
|
+ float clipLeft = Mathf.Max(spriteLeft, minX);
|
|
|
|
|
+ float clipRight = Mathf.Min(spriteRight, maxX);
|
|
|
|
|
+ float clipBottom = Mathf.Max(spriteBottom, minY);
|
|
|
|
|
+ float clipTop = Mathf.Min(spriteTop, maxY);
|
|
|
|
|
+
|
|
|
|
|
+ // 转换为纹理坐标(左下角为原点)
|
|
|
|
|
+ int startX = Mathf.RoundToInt(clipLeft - minX);
|
|
|
|
|
+ int startY = Mathf.RoundToInt(clipBottom - minY);
|
|
|
|
|
+ int endX = Mathf.RoundToInt(clipRight - minX);
|
|
|
|
|
+ int endY = Mathf.RoundToInt(clipTop - minY);
|
|
|
|
|
+
|
|
|
|
|
+ // 计算源纹理的UV偏移(由于裁剪)
|
|
|
|
|
+ float uvOffsetX = (clipLeft - spriteLeft) / e.size.x;
|
|
|
|
|
+ float uvOffsetY = (clipBottom - spriteBottom) / e.size.y;
|
|
|
|
|
+ float uvScaleX = (clipRight - clipLeft) / e.size.x;
|
|
|
|
|
+ float uvScaleY = (clipTop - clipBottom) / e.size.y;
|
|
|
|
|
+
|
|
|
|
|
+ // 源UV
|
|
|
|
|
+ Rect spriteRect = e.spriteRect;
|
|
|
|
|
+ Texture2D spriteTexture = e.tex;
|
|
|
|
|
+ Rect baseUv = new Rect(
|
|
|
|
|
+ spriteRect.x / spriteTexture.width,
|
|
|
|
|
+ spriteRect.y / spriteTexture.height,
|
|
|
|
|
+ spriteRect.width / spriteTexture.width,
|
|
|
|
|
+ spriteRect.height / spriteTexture.height
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 应用裁剪后的UV
|
|
|
|
|
+ Rect uv = new Rect(
|
|
|
|
|
+ baseUv.x + uvOffsetX * baseUv.width,
|
|
|
|
|
+ baseUv.y + uvOffsetY * baseUv.height,
|
|
|
|
|
+ baseUv.width * uvScaleX,
|
|
|
|
|
+ baseUv.height * uvScaleY
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 确保坐标在有效范围内
|
|
|
|
|
+ startX = Mathf.Clamp(startX, 0, textureWidth - 1);
|
|
|
|
|
+ startY = Mathf.Clamp(startY, 0, textureHeight - 1);
|
|
|
|
|
+ endX = Mathf.Clamp(endX, 0, textureWidth);
|
|
|
|
|
+ endY = Mathf.Clamp(endY, 0, textureHeight);
|
|
|
|
|
+
|
|
|
|
|
+ for (int y = startY; y < endY; y++)
|
|
|
|
|
+ {
|
|
|
|
|
+ for (int x = startX; x < endX; x++)
|
|
|
|
|
+ {
|
|
|
|
|
+ float u = (x - startX) / (float)(endX - startX) * uv.width + uv.x;
|
|
|
|
|
+ float v = (y - startY) / (float)(endY - startY) * uv.height + uv.y;
|
|
|
|
|
+
|
|
|
|
|
+ int sx = Mathf.Clamp(Mathf.RoundToInt(u * spriteTexture.width), 0, spriteTexture.width - 1);
|
|
|
|
|
+ int sy = Mathf.Clamp(Mathf.RoundToInt(v * spriteTexture.height), 0, spriteTexture.height - 1);
|
|
|
|
|
+ Color pc = spriteTexture.GetPixel(sx, sy);
|
|
|
|
|
+
|
|
|
|
|
+ int fi = y * textureWidth + x;
|
|
|
|
|
+ if (pc.a > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ pixels[fi] = Color.Lerp(pixels[fi], pc, pc.a);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 应用像素到纹理
|
|
|
|
|
+ finalTexture.SetPixels(pixels);
|
|
|
|
|
+ finalTexture.Apply();
|
|
|
|
|
+
|
|
|
|
|
+ // 保存纹理文件
|
|
|
|
|
+ string fileName = $"GridMap_{System.DateTime.Now:yyyyMMdd_HHmmss}.png";
|
|
|
|
|
+ string filePath = EditorUtility.SaveFilePanel("保存网格地图纹理", "Assets", fileName, "png");
|
|
|
|
|
+
|
|
|
|
|
+ if (!string.IsNullOrEmpty(filePath))
|
|
|
|
|
+ {
|
|
|
|
|
+ byte[] pngData = finalTexture.EncodeToPNG();
|
|
|
|
|
+ System.IO.File.WriteAllBytes(filePath, pngData);
|
|
|
|
|
+
|
|
|
|
|
+ // 刷新资源数据库
|
|
|
|
|
+ AssetDatabase.Refresh();
|
|
|
|
|
+
|
|
|
|
|
+ // 自动清理生成的GameObject和图层数据
|
|
|
|
|
+ CleanupAllData();
|
|
|
|
|
+
|
|
|
|
|
+ EditorUtility.DisplayDialog("保存成功", $"网格地图已保存为纹理贴图:\n{filePath}\n\n纹理尺寸:{textureWidth}x{textureHeight}\n(与Canvas尺寸完全一致,超出部分不渲染)\n\n已自动清理生成的GameObject和图层数据", "确定");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 清理临时纹理
|
|
|
|
|
+ if (finalTexture != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ DestroyImmediate(finalTexture);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 添加新图层
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public void AddLayer(Sprite sprite)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (sprite == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ var newLayer = new SpriteLayer(sprite);
|
|
|
|
|
+ spriteLayersList.Add(newLayer);
|
|
|
|
|
+ selectedLayerIndex = spriteLayersList.Count - 1;
|
|
|
|
|
+ currentSprite = sprite;
|
|
|
|
|
+ UpdateSpriteSize();
|
|
|
|
|
+ CalculateGrid();
|
|
|
|
|
+
|
|
|
|
|
+ // 更新GameObject的渲染顺序
|
|
|
|
|
+ UpdateGameObjectRenderOrder();
|
|
|
|
|
+
|
|
|
|
|
+ Repaint();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 删除图层
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void RemoveLayer(int index)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (index < 0 || index >= spriteLayersList.Count) return;
|
|
|
|
|
+
|
|
|
|
|
+ var layer = spriteLayersList[index];
|
|
|
|
|
+
|
|
|
|
|
+ // 清理该图层的GameObject
|
|
|
|
|
+ if (targetCanvas != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ string parentName = $"GridLayer_{layer.sprite.name}";
|
|
|
|
|
+ Transform existing = targetCanvas.transform.Find(parentName);
|
|
|
|
|
+ if (existing != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ DestroyImmediate(existing.gameObject);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 从列表中移除
|
|
|
|
|
+ spriteLayersList.RemoveAt(index);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新选中索引
|
|
|
|
|
+ if (selectedLayerIndex == index)
|
|
|
|
|
+ {
|
|
|
|
|
+ selectedLayerIndex = -1;
|
|
|
|
|
+ currentSprite = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (selectedLayerIndex > index)
|
|
|
|
|
+ {
|
|
|
|
|
+ selectedLayerIndex--;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果还有图层,选择第一个
|
|
|
|
|
+ if (spriteLayersList.Count > 0 && selectedLayerIndex == -1)
|
|
|
|
|
+ {
|
|
|
|
|
+ selectedLayerIndex = 0;
|
|
|
|
|
+ currentSprite = spriteLayersList[0].sprite;
|
|
|
|
|
+ UpdateSpriteSize();
|
|
|
|
|
+ CalculateGrid();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Repaint();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 向上移动图层
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void MoveLayerUp(int index)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (index <= 0) return;
|
|
|
|
|
+
|
|
|
|
|
+ var layer = spriteLayersList[index];
|
|
|
|
|
+ spriteLayersList.RemoveAt(index);
|
|
|
|
|
+ spriteLayersList.Insert(index - 1, layer);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新选中索引
|
|
|
|
|
+ if (selectedLayerIndex == index)
|
|
|
|
|
+ {
|
|
|
|
|
+ selectedLayerIndex = index - 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (selectedLayerIndex == index - 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ selectedLayerIndex = index;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新GameObject的渲染顺序
|
|
|
|
|
+ UpdateGameObjectRenderOrder();
|
|
|
|
|
+
|
|
|
|
|
+ Repaint();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 向下移动图层
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void MoveLayerDown(int index)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (index >= spriteLayersList.Count - 1) return;
|
|
|
|
|
+
|
|
|
|
|
+ var layer = spriteLayersList[index];
|
|
|
|
|
+ spriteLayersList.RemoveAt(index);
|
|
|
|
|
+ spriteLayersList.Insert(index + 1, layer);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新选中索引
|
|
|
|
|
+ if (selectedLayerIndex == index)
|
|
|
|
|
+ {
|
|
|
|
|
+ selectedLayerIndex = index + 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (selectedLayerIndex == index + 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ selectedLayerIndex = index;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新GameObject的渲染顺序
|
|
|
|
|
+ UpdateGameObjectRenderOrder();
|
|
|
|
|
+
|
|
|
|
|
+ Repaint();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 更新GameObject的渲染顺序
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void UpdateGameObjectRenderOrder()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (targetCanvas == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 按照图层列表的顺序重新排列GameObject
|
|
|
|
|
+ for (int i = 0; i < spriteLayersList.Count; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ var layer = spriteLayersList[i];
|
|
|
|
|
+ if (layer.sprite == null) continue;
|
|
|
|
|
+
|
|
|
|
|
+ string parentName = $"GridLayer_{layer.sprite.name}";
|
|
|
|
|
+ Transform layerParent = targetCanvas.transform.Find(parentName);
|
|
|
|
|
+ if (layerParent != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ // 设置SiblingIndex来控制渲染顺序
|
|
|
|
|
+ layerParent.SetSiblingIndex(i);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 清空所有图层
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void ClearAllLayers()
|
|
|
|
|
+ {
|
|
|
|
|
+ // 使用统一的清理方法
|
|
|
|
|
+ CleanupAllData();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 清理所有数据(保存后自动清理)
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void CleanupAllData()
|
|
|
|
|
+ {
|
|
|
|
|
+ // 清理生成的GameObject
|
|
|
|
|
+ CleanupGeneratedObjects();
|
|
|
|
|
+
|
|
|
|
|
+ // 清理图层列表
|
|
|
|
|
+ spriteLayersList.Clear();
|
|
|
|
|
+ selectedLayerIndex = -1;
|
|
|
|
|
+ currentSprite = null;
|
|
|
|
|
+ spriteSize = Vector2.zero;
|
|
|
|
|
+ gridColumns = 0;
|
|
|
|
|
+ gridRows = 0;
|
|
|
|
|
+
|
|
|
|
|
+ Repaint();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 清理生成的GameObject
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void CleanupGeneratedObjects()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (targetCanvas == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 删除所有以"GridLayer_"开头的子对象
|
|
|
|
|
+ for (int i = targetCanvas.transform.childCount - 1; i >= 0; i--)
|
|
|
|
|
+ {
|
|
|
|
|
+ Transform child = targetCanvas.transform.GetChild(i);
|
|
|
|
|
+ if (child.name.StartsWith("GridLayer_"))
|
|
|
|
|
+ {
|
|
|
|
|
+ DestroyImmediate(child.gameObject);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 清空数据
|
|
|
|
|
+ spriteLayers.Clear();
|
|
|
|
|
+ layerCanvasPositions.Clear();
|
|
|
|
|
+ Repaint();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // -------- 配置保存/加载 --------
|
|
|
|
|
+ private void SaveConfigToFile()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (targetCanvas == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ EditorUtility.DisplayDialog("提示", "请先选择Canvas后再保存配置", "确定");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var cfg = BuildCurrentConfig();
|
|
|
|
|
+ string fileName = $"GridMapConfig_{System.DateTime.Now:yyyyMMdd_HHmmss}.json";
|
|
|
|
|
+ string path = EditorUtility.SaveFilePanel("保存配置", "Assets", fileName, "json");
|
|
|
|
|
+ if (string.IsNullOrEmpty(path)) return;
|
|
|
|
|
+
|
|
|
|
|
+ string json = JsonUtility.ToJson(cfg, true);
|
|
|
|
|
+ System.IO.File.WriteAllText(path, json, System.Text.Encoding.UTF8);
|
|
|
|
|
+ AssetDatabase.Refresh();
|
|
|
|
|
+
|
|
|
|
|
+ // 保存后自动清理
|
|
|
|
|
+ CleanupAllData();
|
|
|
|
|
+ EditorUtility.DisplayDialog("保存成功", "配置已保存并已自动清理现场。", "确定");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private MapConfig BuildCurrentConfig()
|
|
|
|
|
+ {
|
|
|
|
|
+ var cfg = new MapConfig
|
|
|
|
|
+ {
|
|
|
|
|
+ canvasName = targetCanvas != null ? targetCanvas.name : string.Empty,
|
|
|
|
|
+ canvasWidth = canvasSize.x,
|
|
|
|
|
+ canvasHeight = canvasSize.y
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = 0; i < spriteLayersList.Count; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ var layer = spriteLayersList[i];
|
|
|
|
|
+ if (layer.sprite == null) continue;
|
|
|
|
|
+
|
|
|
|
|
+ var layerCfg = new LayerConfig
|
|
|
|
|
+ {
|
|
|
|
|
+ name = layer.name,
|
|
|
|
|
+ isVisible = layer.isVisible,
|
|
|
|
|
+ isPreviewVisible = layer.isPreviewVisible,
|
|
|
|
|
+ spriteAssetPath = AssetDatabase.GetAssetPath(layer.sprite),
|
|
|
|
|
+ resourcesPath = GetResourcesPath(layer.sprite)
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (spriteLayers.ContainsKey(layer.sprite))
|
|
|
|
|
+ {
|
|
|
|
|
+ var dict = spriteLayers[layer.sprite];
|
|
|
|
|
+ foreach (var kv in dict)
|
|
|
|
|
+ {
|
|
|
|
|
+ Vector2Int cell = kv.Key;
|
|
|
|
|
+ Vector2 pos;
|
|
|
|
|
+ if (layerCanvasPositions.ContainsKey(layer.sprite) && layerCanvasPositions[layer.sprite].TryGetValue(cell, out pos))
|
|
|
|
|
+ {
|
|
|
|
|
+ layerCfg.items.Add(new ItemConfig
|
|
|
|
|
+ {
|
|
|
|
|
+ cellX = cell.x,
|
|
|
|
|
+ cellY = cell.y,
|
|
|
|
|
+ posX = pos.x,
|
|
|
|
|
+ posY = pos.y
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ cfg.layers.Add(layerCfg);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return cfg;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void LoadConfigFromFile()
|
|
|
|
|
+ {
|
|
|
|
|
+ string path = EditorUtility.OpenFilePanel("加载配置", "Assets", "json");
|
|
|
|
|
+ if (string.IsNullOrEmpty(path)) return;
|
|
|
|
|
+
|
|
|
|
|
+ string json = System.IO.File.ReadAllText(path, System.Text.Encoding.UTF8);
|
|
|
|
|
+ LoadConfigFromJson(json);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 可在运行时调用
|
|
|
|
|
+ public void LoadConfigFromJson(string json)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (string.IsNullOrEmpty(json)) return;
|
|
|
|
|
+ var cfg = JsonUtility.FromJson<MapConfig>(json);
|
|
|
|
|
+ if (cfg == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 清空现有
|
|
|
|
|
+ ClearAllLayers();
|
|
|
|
|
+
|
|
|
|
|
+ // 恢复Canvas尺寸(仅编辑器显示用)
|
|
|
|
|
+ canvasSize = new Vector2(cfg.canvasWidth, cfg.canvasHeight);
|
|
|
|
|
+
|
|
|
|
|
+ // 重建图层
|
|
|
|
|
+ foreach (var layerCfg in cfg.layers)
|
|
|
|
|
+ {
|
|
|
|
|
+ Sprite sp = null;
|
|
|
|
|
+ // 优先从Asset路径加载(编辑器)
|
|
|
|
|
+ if (!string.IsNullOrEmpty(layerCfg.spriteAssetPath))
|
|
|
|
|
+ {
|
|
|
|
|
+ sp = AssetDatabase.LoadAssetAtPath<Sprite>(layerCfg.spriteAssetPath);
|
|
|
|
|
+ }
|
|
|
|
|
+ // 其次尝试Resources路径(运行时)
|
|
|
|
|
+ if (sp == null && !string.IsNullOrEmpty(layerCfg.resourcesPath))
|
|
|
|
|
+ {
|
|
|
|
|
+ sp = Resources.Load<Sprite>(layerCfg.resourcesPath);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (sp == null) continue;
|
|
|
|
|
+
|
|
|
|
|
+ AddLayer(sp);
|
|
|
|
|
+ var layer = spriteLayersList[spriteLayersList.Count - 1];
|
|
|
|
|
+ layer.name = string.IsNullOrEmpty(layerCfg.name) ? sp.name : layerCfg.name;
|
|
|
|
|
+ layer.isVisible = layerCfg.isVisible;
|
|
|
|
|
+ layer.isPreviewVisible = layerCfg.isPreviewVisible;
|
|
|
|
|
+
|
|
|
|
|
+ // 以当前选中图层的网格基准进行单元计算,但位置使用记录的Canvas位置
|
|
|
|
|
+ foreach (var item in layerCfg.items)
|
|
|
|
|
+ {
|
|
|
|
|
+ Vector2Int cell = new Vector2Int(item.cellX, item.cellY);
|
|
|
|
|
+ if (!spriteLayers.ContainsKey(sp))
|
|
|
|
|
+ {
|
|
|
|
|
+ spriteLayers[sp] = new System.Collections.Generic.Dictionary<Vector2Int, GameObject>();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!layerCanvasPositions.ContainsKey(sp))
|
|
|
|
|
+ {
|
|
|
|
|
+ layerCanvasPositions[sp] = new System.Collections.Generic.Dictionary<Vector2Int, Vector2>();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建GO
|
|
|
|
|
+ GameObject parent = GetOrCreateLayerParent(sp);
|
|
|
|
|
+ GameObject go = new GameObject($"Cell_{cell.x}_{cell.y}");
|
|
|
|
|
+ go.transform.SetParent(parent.transform, false);
|
|
|
|
|
+ var img = go.AddComponent<UnityEngine.UI.Image>();
|
|
|
|
|
+ img.sprite = sp;
|
|
|
|
|
+
|
|
|
|
|
+ var rt = go.GetComponent<RectTransform>();
|
|
|
|
|
+ rt.anchorMin = new Vector2(0.5f, 0.5f);
|
|
|
|
|
+ rt.anchorMax = new Vector2(0.5f, 0.5f);
|
|
|
|
|
+ rt.sizeDelta = new Vector2(sp.rect.width, sp.rect.height);
|
|
|
|
|
+ rt.anchoredPosition = new Vector2(item.posX, item.posY);
|
|
|
|
|
+
|
|
|
|
|
+ spriteLayers[sp][cell] = go;
|
|
|
|
|
+ layerCanvasPositions[sp][cell] = new Vector2(item.posX, item.posY);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 刷新
|
|
|
|
|
+ UpdateSpriteSize();
|
|
|
|
|
+ CalculateGrid();
|
|
|
|
|
+ UpdateGameObjectRenderOrder();
|
|
|
|
|
+ Repaint();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static string GetResourcesPath(Sprite sprite)
|
|
|
|
|
+ {
|
|
|
|
|
+ string assetPath = AssetDatabase.GetAssetPath(sprite);
|
|
|
|
|
+ if (string.IsNullOrEmpty(assetPath)) return string.Empty;
|
|
|
|
|
+ const string key = "/Resources/";
|
|
|
|
|
+ int idx = assetPath.LastIndexOf(key, System.StringComparison.OrdinalIgnoreCase);
|
|
|
|
|
+ if (idx < 0) return string.Empty;
|
|
|
|
|
+ string rel = assetPath.Substring(idx + key.Length);
|
|
|
|
|
+ int dot = rel.LastIndexOf('.');
|
|
|
|
|
+ if (dot >= 0) rel = rel.Substring(0, dot);
|
|
|
|
|
+ return rel.Replace('\\', '/');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|