GridMapEditor.cs 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352
  1. using UnityEngine;
  2. using UnityEditor;
  3. namespace MapEditor
  4. {
  5. /// <summary>
  6. /// 网格地图编辑器 - 全新实现
  7. /// </summary>
  8. public class GridMapEditor : EditorWindow
  9. {
  10. // Canvas相关
  11. private Canvas targetCanvas;
  12. private Vector2 canvasSize;
  13. // Sprite相关
  14. private Sprite currentSprite;
  15. private Vector2 spriteSize;
  16. // 图层系统
  17. [System.Serializable]
  18. public class SpriteLayer
  19. {
  20. public Sprite sprite;
  21. public string name;
  22. public bool isVisible = true;
  23. public bool isPreviewVisible = true; // 在预览窗体中是否可见
  24. public SpriteLayer(Sprite sprite)
  25. {
  26. this.sprite = sprite;
  27. this.name = sprite != null ? sprite.name : "未命名图层";
  28. }
  29. }
  30. private System.Collections.Generic.List<SpriteLayer> spriteLayersList = new System.Collections.Generic.List<SpriteLayer>();
  31. private int selectedLayerIndex = -1;
  32. private Vector2 layerListScrollPosition;
  33. private int draggedLayerIndex = -1;
  34. // 网格相关
  35. private int gridColumns = 0;
  36. private int gridRows = 0;
  37. // UI相关
  38. private Vector2 scrollPosition;
  39. private Rect previewRect;
  40. private float previewScale = 1f;
  41. private bool isPaintingEnabled = true; // 左键绘制,右键擦除
  42. private bool showOtherLayers = true; // 是否显示其他图层(半透明)
  43. // 颜色
  44. private Color gridColor = new Color(0.3f, 0.3f, 0.3f, 0.8f); // 更明显的网格线
  45. private Color backgroundColor = new Color(0.2f, 0.2f, 0.2f, 1f);
  46. // 配置数据模型(仅用于JSON序列化)
  47. [System.Serializable]
  48. private class MapConfig
  49. {
  50. public string canvasName;
  51. public float canvasWidth;
  52. public float canvasHeight;
  53. public System.Collections.Generic.List<LayerConfig> layers = new System.Collections.Generic.List<LayerConfig>();
  54. }
  55. [System.Serializable]
  56. private class LayerConfig
  57. {
  58. public string name;
  59. public string spriteAssetPath; // 仅编辑器可用
  60. public string resourcesPath; // 运行时可用(Assets/Resources 下)
  61. public bool isVisible;
  62. public bool isPreviewVisible;
  63. public System.Collections.Generic.List<ItemConfig> items = new System.Collections.Generic.List<ItemConfig>();
  64. }
  65. [System.Serializable]
  66. private class ItemConfig
  67. {
  68. public int cellX;
  69. public int cellY;
  70. public float posX; // Canvas坐标(以中心为原点)
  71. public float posY;
  72. }
  73. // 放置的数据:按Sprite区分的"图层"
  74. 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>>();
  75. // 每个图层的Canvas绝对位置记录
  76. 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>>();
  77. // 单元格间距(Canvas单位,像素),默认0
  78. private Vector2 cellGap = Vector2.zero;
  79. [MenuItem("工具/网格地图编辑器")]
  80. public static void ShowWindow()
  81. {
  82. GridMapEditor window = GetWindow<GridMapEditor>("网格地图编辑器");
  83. window.minSize = new Vector2(600, 600);
  84. }
  85. private void OnGUI()
  86. {
  87. DrawToolbar();
  88. EditorGUILayout.Space(10);
  89. if (targetCanvas != null && spriteLayersList.Count > 0)
  90. {
  91. DrawPreviewArea();
  92. }
  93. else
  94. {
  95. DrawHelpBox();
  96. }
  97. }
  98. /// <summary>
  99. /// 绘制工具栏
  100. /// </summary>
  101. private void DrawToolbar()
  102. {
  103. EditorGUILayout.BeginVertical("box");
  104. GUILayout.Label("网格地图编辑器", EditorStyles.boldLabel);
  105. EditorGUILayout.Space(5);
  106. // Canvas选择
  107. EditorGUI.BeginChangeCheck();
  108. Canvas newCanvas = (Canvas)EditorGUILayout.ObjectField(
  109. "目标Canvas",
  110. targetCanvas,
  111. typeof(Canvas),
  112. true
  113. );
  114. if (EditorGUI.EndChangeCheck())
  115. {
  116. targetCanvas = newCanvas;
  117. UpdateCanvasSize();
  118. CalculateGrid();
  119. }
  120. if (targetCanvas != null)
  121. {
  122. EditorGUILayout.LabelField("Canvas大小", $"{canvasSize.x} x {canvasSize.y}");
  123. }
  124. EditorGUILayout.Space(5);
  125. // 图层管理
  126. DrawLayerManagement();
  127. EditorGUILayout.Space(5);
  128. // 网格信息
  129. if (gridColumns > 0 && gridRows > 0)
  130. {
  131. EditorGUILayout.LabelField("网格信息", $"{gridColumns} 列 x {gridRows} 行 = {gridColumns * gridRows} 个单元格");
  132. }
  133. EditorGUILayout.Space(5);
  134. // 单元间距设置
  135. EditorGUI.BeginChangeCheck();
  136. Vector2 newGap = EditorGUILayout.Vector2Field("单元间距", cellGap);
  137. if (EditorGUI.EndChangeCheck())
  138. {
  139. newGap.x = Mathf.Max(0, newGap.x);
  140. newGap.y = Mathf.Max(0, newGap.y);
  141. cellGap = newGap;
  142. CalculateGrid();
  143. }
  144. // 预览缩放
  145. previewScale = EditorGUILayout.Slider("预览缩放", previewScale, 0.1f, 2f);
  146. // 其他图层显示开关
  147. showOtherLayers = EditorGUILayout.Toggle("显示其他图层", showOtherLayers);
  148. EditorGUILayout.Space(10);
  149. // 配置保存/加载
  150. EditorGUILayout.BeginHorizontal();
  151. if (GUILayout.Button("保存配置", GUILayout.Height(24)))
  152. {
  153. SaveConfigToFile();
  154. }
  155. if (GUILayout.Button("加载配置", GUILayout.Height(24)))
  156. {
  157. LoadConfigFromFile();
  158. }
  159. EditorGUILayout.EndHorizontal();
  160. EditorGUILayout.Space(5);
  161. // 保存按钮
  162. GUI.enabled = targetCanvas != null && HasPlacedSprites();
  163. if (GUILayout.Button("保存为纹理贴图", GUILayout.Height(30)))
  164. {
  165. SaveAsTexture();
  166. }
  167. GUI.enabled = true;
  168. EditorGUILayout.EndVertical();
  169. }
  170. /// <summary>
  171. /// 绘制图层管理UI
  172. /// </summary>
  173. private void DrawLayerManagement()
  174. {
  175. EditorGUILayout.BeginVertical("box");
  176. GUILayout.Label("图层管理", EditorStyles.boldLabel);
  177. // 添加新图层按钮
  178. EditorGUILayout.BeginHorizontal();
  179. if (GUILayout.Button("添加图层", GUILayout.Height(25)))
  180. {
  181. // 打开Sprite选择对话框
  182. string path = EditorUtility.OpenFilePanel("选择Sprite", "Assets", "png");
  183. if (!string.IsNullOrEmpty(path))
  184. {
  185. // 将绝对路径转换为相对路径
  186. if (path.StartsWith(Application.dataPath))
  187. {
  188. path = "Assets" + path.Substring(Application.dataPath.Length);
  189. }
  190. // 加载Sprite
  191. Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path);
  192. if (sprite != null)
  193. {
  194. AddLayer(sprite);
  195. }
  196. else
  197. {
  198. EditorUtility.DisplayDialog("错误", "无法加载Sprite,请确保选择的是有效的图片文件", "确定");
  199. }
  200. }
  201. }
  202. if (GUILayout.Button("清空所有图层", GUILayout.Height(25)))
  203. {
  204. if (EditorUtility.DisplayDialog("确认清空", "确定要清空所有图层吗?", "确定", "取消"))
  205. {
  206. ClearAllLayers();
  207. }
  208. }
  209. EditorGUILayout.EndHorizontal();
  210. EditorGUILayout.Space(5);
  211. // 图层列表
  212. if (spriteLayersList.Count > 0)
  213. {
  214. layerListScrollPosition = EditorGUILayout.BeginScrollView(layerListScrollPosition, GUILayout.Height(150));
  215. for (int i = 0; i < spriteLayersList.Count; i++)
  216. {
  217. DrawLayerItem(i);
  218. }
  219. EditorGUILayout.EndScrollView();
  220. }
  221. else
  222. {
  223. EditorGUILayout.HelpBox("暂无图层,请添加图层开始编辑", MessageType.Info);
  224. }
  225. EditorGUILayout.EndVertical();
  226. }
  227. /// <summary>
  228. /// 绘制单个图层项
  229. /// </summary>
  230. private void DrawLayerItem(int index)
  231. {
  232. if (index >= spriteLayersList.Count) return;
  233. var layer = spriteLayersList[index];
  234. bool isSelected = selectedLayerIndex == index;
  235. EditorGUILayout.BeginHorizontal();
  236. // 选择按钮
  237. if (GUILayout.Button(isSelected ? "●" : "○", GUILayout.Width(20), GUILayout.Height(20)))
  238. {
  239. selectedLayerIndex = index;
  240. currentSprite = layer.sprite;
  241. UpdateSpriteSize();
  242. CalculateGrid();
  243. Repaint();
  244. }
  245. // 场景可见性切换
  246. EditorGUILayout.LabelField("场景", GUILayout.Width(30));
  247. layer.isVisible = EditorGUILayout.Toggle(layer.isVisible, GUILayout.Width(20));
  248. // 预览可见性切换
  249. EditorGUILayout.LabelField("预览", GUILayout.Width(30));
  250. layer.isPreviewVisible = EditorGUILayout.Toggle(layer.isPreviewVisible, GUILayout.Width(20));
  251. // 图层名称
  252. EditorGUILayout.LabelField(layer.name, isSelected ? EditorStyles.boldLabel : EditorStyles.label);
  253. // 上下移动按钮
  254. EditorGUILayout.BeginVertical();
  255. if (GUILayout.Button("↑", GUILayout.Width(20), GUILayout.Height(15)))
  256. {
  257. MoveLayerUp(index);
  258. }
  259. if (GUILayout.Button("↓", GUILayout.Width(20), GUILayout.Height(15)))
  260. {
  261. MoveLayerDown(index);
  262. }
  263. EditorGUILayout.EndVertical();
  264. // 删除按钮
  265. if (GUILayout.Button("×", GUILayout.Width(20), GUILayout.Height(20)))
  266. {
  267. if (EditorUtility.DisplayDialog("删除图层", $"确定要删除图层 '{layer.name}' 吗?", "确定", "取消"))
  268. {
  269. RemoveLayer(index);
  270. }
  271. }
  272. EditorGUILayout.EndHorizontal();
  273. // 拖拽处理
  274. HandleLayerDrag(index);
  275. EditorGUILayout.Space(2);
  276. }
  277. /// <summary>
  278. /// 处理图层拖拽(简化版本)
  279. /// </summary>
  280. private void HandleLayerDrag(int index)
  281. {
  282. // 暂时禁用拖拽功能,避免卡顿
  283. // 可以通过上下箭头按钮来调整顺序
  284. }
  285. /// <summary>
  286. /// 绘制预览区域
  287. /// </summary>
  288. private void DrawPreviewArea()
  289. {
  290. EditorGUILayout.BeginVertical("box");
  291. GUILayout.Label("预览区域", EditorStyles.boldLabel);
  292. // 计算预览区域(取Canvas与网格尺寸中较大者)
  293. Vector2 cellSizeCanvas = new Vector2(spriteSize.x + cellGap.x, spriteSize.y + cellGap.y);
  294. float gridCanvasWidth = Mathf.Max(1, gridColumns) * cellSizeCanvas.x;
  295. float gridCanvasHeight = Mathf.Max(1, gridRows) * cellSizeCanvas.y;
  296. float drawCanvasWidth = Mathf.Max(canvasSize.x, gridCanvasWidth);
  297. float drawCanvasHeight = Mathf.Max(canvasSize.y, gridCanvasHeight);
  298. float scaledWidth = drawCanvasWidth * previewScale;
  299. float scaledHeight = drawCanvasHeight * previewScale;
  300. // 创建滚动视图
  301. scrollPosition = EditorGUILayout.BeginScrollView(
  302. scrollPosition,
  303. GUILayout.ExpandWidth(true),
  304. GUILayout.ExpandHeight(true)
  305. );
  306. // 获取预览区域的矩形
  307. previewRect = GUILayoutUtility.GetRect(
  308. scaledWidth,
  309. scaledHeight,
  310. GUILayout.ExpandWidth(false),
  311. GUILayout.ExpandHeight(false)
  312. );
  313. // 绘制背景(保持透明,不覆盖网格线)
  314. // EditorGUI.DrawRect(previewRect, backgroundColor);
  315. // 绘制网格
  316. DrawGrid(previewRect);
  317. // 处理鼠标
  318. HandleMouseInPreview(previewRect);
  319. EditorGUILayout.EndScrollView();
  320. EditorGUILayout.EndVertical();
  321. }
  322. /// <summary>
  323. /// 绘制网格
  324. /// </summary>
  325. private void DrawGrid(Rect rect)
  326. {
  327. if (gridColumns <= 0 || gridRows <= 0) return;
  328. Handles.BeginGUI();
  329. // 将Canvas单位映射到预览区域,保持等比(使用绘制区域的Canvas尺寸)
  330. Vector2 cellSizeCanvas = new Vector2(spriteSize.x + cellGap.x, spriteSize.y + cellGap.y);
  331. float gridCanvasWidth = Mathf.Max(1, gridColumns) * cellSizeCanvas.x;
  332. float gridCanvasHeight = Mathf.Max(1, gridRows) * cellSizeCanvas.y;
  333. float drawCanvasWidth = Mathf.Max(canvasSize.x, gridCanvasWidth);
  334. float drawCanvasHeight = Mathf.Max(canvasSize.y, gridCanvasHeight);
  335. float scaleX = rect.width / drawCanvasWidth;
  336. float scaleY = rect.height / drawCanvasHeight;
  337. // 计算Canvas在预览区域中的位置(居中显示)
  338. float canvasOffsetX = (rect.width - drawCanvasWidth * scaleX) / 2f;
  339. float canvasOffsetY = (rect.height - drawCanvasHeight * scaleY) / 2f;
  340. float cellWidthCanvas = spriteSize.x + cellGap.x;
  341. float cellHeightCanvas = spriteSize.y + cellGap.y;
  342. float cellWidth = cellWidthCanvas * scaleX;
  343. float cellHeight = cellHeightCanvas * scaleY;
  344. float gridWidth = gridColumns * cellWidth;
  345. float gridHeight = gridRows * cellHeight;
  346. // 网格起始位置(考虑Canvas偏移)
  347. float gridStartX = rect.x + canvasOffsetX;
  348. float gridStartY = rect.y + canvasOffsetY;
  349. // 绘制垂直线
  350. for (int x = 0; x <= gridColumns; x++)
  351. {
  352. float xPos = gridStartX + x * cellWidth;
  353. Handles.color = gridColor;
  354. Handles.DrawLine(
  355. new Vector3(xPos, gridStartY, 0),
  356. new Vector3(xPos, gridStartY + gridHeight, 0)
  357. );
  358. }
  359. // 绘制水平线
  360. for (int y = 0; y <= gridRows; y++)
  361. {
  362. float yPos = gridStartY + y * cellHeight;
  363. Handles.color = gridColor;
  364. Handles.DrawLine(
  365. new Vector3(gridStartX, yPos, 0),
  366. new Vector3(gridStartX + gridWidth, yPos, 0)
  367. );
  368. }
  369. // 绘制已放置的Sprite预览
  370. if (currentSprite != null)
  371. {
  372. DrawSpriteGrid(rect, cellWidth, cellHeight);
  373. }
  374. Handles.EndGUI();
  375. }
  376. /// <summary>
  377. /// 在网格中绘制Sprite
  378. /// </summary>
  379. private void DrawSpriteGrid(Rect rect, float cellWidth, float cellHeight)
  380. {
  381. if (spriteLayersList.Count == 0) return;
  382. // 计算Canvas在预览区域中的缩放和偏移
  383. Vector2 cellSizeCanvas = new Vector2(spriteSize.x + cellGap.x, spriteSize.y + cellGap.y);
  384. float gridCanvasWidth = Mathf.Max(1, gridColumns) * cellSizeCanvas.x;
  385. float gridCanvasHeight = Mathf.Max(1, gridRows) * cellSizeCanvas.y;
  386. float drawCanvasWidth = Mathf.Max(canvasSize.x, gridCanvasWidth);
  387. float drawCanvasHeight = Mathf.Max(canvasSize.y, gridCanvasHeight);
  388. float scaleX = rect.width / drawCanvasWidth;
  389. float scaleY = rect.height / drawCanvasHeight;
  390. // 计算Canvas在预览区域中的位置(居中显示)
  391. float canvasOffsetX = (rect.width - drawCanvasWidth * scaleX) / 2f;
  392. float canvasOffsetY = (rect.height - drawCanvasHeight * scaleY) / 2f;
  393. // 渲染所有图层(按顺序,从下到上)
  394. for (int layerIndex = 0; layerIndex < spriteLayersList.Count; layerIndex++)
  395. {
  396. var layer = spriteLayersList[layerIndex];
  397. if (!layer.isVisible || !layer.isPreviewVisible || layer.sprite == null) continue;
  398. bool isSelectedLayer = layerIndex == selectedLayerIndex;
  399. // 根据开关决定是否显示其他图层
  400. if (!isSelectedLayer && !showOtherLayers) continue;
  401. float layerOpacity = isSelectedLayer ? 1f : 0.5f; // 非选中图层半透明
  402. // 设置透明度
  403. Color originalColor = GUI.color;
  404. GUI.color = new Color(1f, 1f, 1f, layerOpacity);
  405. // 绘制该图层的所有Sprite(使用原始尺寸)
  406. DrawLayerSpritesWithOriginalSize(rect, layer, canvasOffsetX, canvasOffsetY, scaleX, scaleY);
  407. // 恢复颜色
  408. GUI.color = originalColor;
  409. }
  410. }
  411. /// <summary>
  412. /// 绘制单个图层的所有Sprite(使用记录的Canvas绝对位置)
  413. /// </summary>
  414. private void DrawLayerSpritesWithOriginalSize(Rect rect, SpriteLayer layer, float canvasOffsetX, float canvasOffsetY, float scaleX, float scaleY)
  415. {
  416. if (layer.sprite == null) return;
  417. Texture2D texture = layer.sprite.texture;
  418. Rect spriteRect = layer.sprite.rect;
  419. // 计算UV坐标
  420. Rect uv = new Rect(
  421. spriteRect.x / texture.width,
  422. spriteRect.y / texture.height,
  423. spriteRect.width / texture.width,
  424. spriteRect.height / texture.height
  425. );
  426. // 获取该图层的Sprite数据
  427. if (!spriteLayers.ContainsKey(layer.sprite)) return;
  428. var layerData = spriteLayers[layer.sprite];
  429. // 绘制该图层中已放置的Sprite
  430. foreach (var kvp in layerData)
  431. {
  432. Vector2Int cell = kvp.Key;
  433. // 使用记录的Canvas绝对位置
  434. if (!layerCanvasPositions.ContainsKey(layer.sprite) || !layerCanvasPositions[layer.sprite].ContainsKey(cell))
  435. {
  436. continue; // 没有位置记录,跳过
  437. }
  438. Vector2 canvasPosition = layerCanvasPositions[layer.sprite][cell];
  439. // 将Canvas坐标转换为预览区域坐标
  440. // Canvas坐标系:中心为原点,向右为+X,向上为+Y
  441. // 预览区域坐标系:左上角为原点,向右为+X,向下为+Y
  442. float previewX = rect.x + canvasOffsetX + (canvasPosition.x + canvasSize.x / 2f) * scaleX;
  443. float previewY = rect.y + canvasOffsetY + (canvasSize.y / 2f - canvasPosition.y) * scaleY;
  444. // 使用该图层Sprite的原始尺寸
  445. Vector2 originalSpriteSize = new Vector2(spriteRect.width, spriteRect.height);
  446. // 计算Sprite在预览区域中的实际尺寸(保持原始比例)
  447. float spriteScaleX = originalSpriteSize.x * scaleX;
  448. float spriteScaleY = originalSpriteSize.y * scaleY;
  449. // 计算居中位置
  450. float centerX = previewX - spriteScaleX / 2f;
  451. float centerY = previewY - spriteScaleY / 2f;
  452. Rect cellRect = new Rect(centerX, centerY, spriteScaleX, spriteScaleY);
  453. GUI.DrawTextureWithTexCoords(cellRect, texture, uv);
  454. }
  455. }
  456. /// <summary>
  457. /// 绘制单个图层的所有Sprite(旧方法,保持兼容)
  458. /// </summary>
  459. private void DrawLayerSprites(Rect rect, SpriteLayer layer, float canvasOffsetX, float canvasOffsetY, float scaleX, float scaleY)
  460. {
  461. if (layer.sprite == null) return;
  462. Texture2D texture = layer.sprite.texture;
  463. Rect spriteRect = layer.sprite.rect;
  464. // 计算UV坐标
  465. Rect uv = new Rect(
  466. spriteRect.x / texture.width,
  467. spriteRect.y / texture.height,
  468. spriteRect.width / texture.width,
  469. spriteRect.height / texture.height
  470. );
  471. // 获取该图层的Sprite数据
  472. if (!spriteLayers.ContainsKey(layer.sprite)) return;
  473. var layerData = spriteLayers[layer.sprite];
  474. float cellWidthCanvas = spriteSize.x + cellGap.x;
  475. float cellHeightCanvas = spriteSize.y + cellGap.y;
  476. // 绘制该图层中已放置的Sprite
  477. foreach (var kvp in layerData)
  478. {
  479. Vector2Int cell = kvp.Key;
  480. Rect cellRect = new Rect(
  481. rect.x + canvasOffsetX + cell.x * cellWidthCanvas * scaleX,
  482. rect.y + canvasOffsetY + cell.y * cellHeightCanvas * scaleY,
  483. spriteSize.x * scaleX,
  484. spriteSize.y * scaleY
  485. );
  486. GUI.DrawTextureWithTexCoords(cellRect, texture, uv);
  487. }
  488. }
  489. /// <summary>
  490. /// 处理预览区域内的鼠标事件(左键放置,右键删除)
  491. /// </summary>
  492. private void HandleMouseInPreview(Rect rect)
  493. {
  494. if (!isPaintingEnabled || selectedLayerIndex < 0 || selectedLayerIndex >= spriteLayersList.Count || targetCanvas == null) return;
  495. var selectedLayer = spriteLayersList[selectedLayerIndex];
  496. if (selectedLayer.sprite == null) return;
  497. Event e = Event.current;
  498. if (e == null) return;
  499. if (e.isMouse && (e.type == EventType.MouseDown || e.type == EventType.MouseDrag))
  500. {
  501. if (!rect.Contains(e.mousePosition)) return;
  502. // 只在MouseDown或按住拖动时响应
  503. int button = e.button; // 0左 1右
  504. Vector2Int? cell = ScreenToCell(rect, e.mousePosition);
  505. if (!cell.HasValue) return;
  506. if (button == 0)
  507. {
  508. PlaceSpriteAtCell(cell.Value, selectedLayer.sprite);
  509. e.Use();
  510. }
  511. else if (button == 1)
  512. {
  513. RemoveSpriteAtCell(cell.Value, selectedLayer.sprite);
  514. e.Use();
  515. }
  516. }
  517. }
  518. /// <summary>
  519. /// 将预览坐标转换为网格坐标
  520. /// </summary>
  521. private Vector2Int? ScreenToCell(Rect rect, Vector2 mousePos)
  522. {
  523. if (gridColumns <= 0 || gridRows <= 0) return null;
  524. Vector2 cellSizeCanvas = new Vector2(spriteSize.x + cellGap.x, spriteSize.y + cellGap.y);
  525. float gridCanvasWidth = Mathf.Max(1, gridColumns) * cellSizeCanvas.x;
  526. float gridCanvasHeight = Mathf.Max(1, gridRows) * cellSizeCanvas.y;
  527. float drawCanvasWidth = Mathf.Max(canvasSize.x, gridCanvasWidth);
  528. float drawCanvasHeight = Mathf.Max(canvasSize.y, gridCanvasHeight);
  529. float scaleX = rect.width / drawCanvasWidth;
  530. float scaleY = rect.height / drawCanvasHeight;
  531. // 计算Canvas在预览区域中的位置(居中显示)
  532. float canvasOffsetX = (rect.width - drawCanvasWidth * scaleX) / 2f;
  533. float canvasOffsetY = (rect.height - drawCanvasHeight * scaleY) / 2f;
  534. float cellWidthCanvas = cellSizeCanvas.x;
  535. float cellHeightCanvas = cellSizeCanvas.y;
  536. // 转换为Canvas坐标系
  537. float localXCanvas = (mousePos.x - rect.x - canvasOffsetX) / scaleX;
  538. float localYCanvas = (mousePos.y - rect.y - canvasOffsetY) / scaleY;
  539. int col = Mathf.FloorToInt(localXCanvas / cellWidthCanvas);
  540. int row = Mathf.FloorToInt(localYCanvas / cellHeightCanvas);
  541. col = Mathf.Clamp(col, 0, gridColumns - 1);
  542. row = Mathf.Clamp(row, 0, gridRows - 1);
  543. return new Vector2Int(col, row);
  544. }
  545. /// <summary>
  546. /// 在Canvas上指定单元放置一个Image
  547. /// </summary>
  548. private void PlaceSpriteAtCell(Vector2Int cell, Sprite sprite)
  549. {
  550. if (sprite == null) return;
  551. if (!spriteLayers.ContainsKey(sprite))
  552. {
  553. spriteLayers[sprite] = new System.Collections.Generic.Dictionary<Vector2Int, GameObject>();
  554. layerCanvasPositions[sprite] = new System.Collections.Generic.Dictionary<Vector2Int, Vector2>();
  555. }
  556. if (spriteLayers[sprite].ContainsKey(cell)) return; // 已存在
  557. GameObject parent = GetOrCreateLayerParent(sprite);
  558. GameObject go = new GameObject($"Cell_{cell.x}_{cell.y}");
  559. go.transform.SetParent(parent.transform, false);
  560. var img = go.AddComponent<UnityEngine.UI.Image>();
  561. img.sprite = sprite;
  562. RectTransform rectTransform = go.GetComponent<RectTransform>();
  563. rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
  564. rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
  565. // 使用该Sprite的原始尺寸
  566. Vector2 spriteSize = new Vector2(sprite.rect.width, sprite.rect.height);
  567. rectTransform.sizeDelta = spriteSize;
  568. // 计算该单元在Canvas中的锚定位置(以Canvas中心为原点,右为+X,上为+Y)
  569. // 使用当前选中图层的网格尺寸来计算位置
  570. float cellWCanvas = this.spriteSize.x + cellGap.x;
  571. float cellHCanvas = this.spriteSize.y + cellGap.y;
  572. float x = -canvasSize.x / 2f + cell.x * cellWCanvas + this.spriteSize.x / 2f;
  573. float y = canvasSize.y / 2f - cell.y * cellHCanvas - this.spriteSize.y / 2f;
  574. // 记录Canvas绝对位置
  575. Vector2 canvasPosition = new Vector2(x, y);
  576. layerCanvasPositions[sprite][cell] = canvasPosition;
  577. rectTransform.anchoredPosition = canvasPosition;
  578. spriteLayers[sprite][cell] = go;
  579. Repaint();
  580. }
  581. /// <summary>
  582. /// 删除指定单元的Image
  583. /// </summary>
  584. private void RemoveSpriteAtCell(Vector2Int cell, Sprite sprite)
  585. {
  586. if (sprite == null || !spriteLayers.ContainsKey(sprite)) return;
  587. if (!spriteLayers[sprite].ContainsKey(cell)) return;
  588. var go = spriteLayers[sprite][cell];
  589. if (go != null)
  590. {
  591. GameObject.DestroyImmediate(go);
  592. }
  593. spriteLayers[sprite].Remove(cell);
  594. // 清理位置记录
  595. if (layerCanvasPositions.ContainsKey(sprite) && layerCanvasPositions[sprite].ContainsKey(cell))
  596. {
  597. layerCanvasPositions[sprite].Remove(cell);
  598. }
  599. Repaint();
  600. }
  601. /// <summary>
  602. /// 按当前Sprite获取或创建一个层级父节点
  603. /// </summary>
  604. private GameObject GetOrCreateLayerParent(Sprite sprite)
  605. {
  606. string parentName = $"GridLayer_{sprite.name}";
  607. Transform existing = targetCanvas.transform.Find(parentName);
  608. if (existing != null) return existing.gameObject;
  609. GameObject parent = new GameObject(parentName);
  610. parent.transform.SetParent(targetCanvas.transform, false);
  611. return parent;
  612. }
  613. /// <summary>
  614. /// 绘制帮助框
  615. /// </summary>
  616. private void DrawHelpBox()
  617. {
  618. EditorGUILayout.BeginVertical("box");
  619. EditorGUILayout.HelpBox(
  620. "请先完成以下设置:\n\n" +
  621. "1. 选择一个Canvas(用于确定画布大小)\n" +
  622. "2. 添加图层并选择Sprite图片(用于确定网格单元大小)\n\n" +
  623. "编辑器会自动计算网格数量并铺满Canvas",
  624. MessageType.Info
  625. );
  626. EditorGUILayout.EndVertical();
  627. }
  628. /// <summary>
  629. /// 更新Canvas大小
  630. /// </summary>
  631. private void UpdateCanvasSize()
  632. {
  633. if (targetCanvas == null)
  634. {
  635. canvasSize = Vector2.zero;
  636. return;
  637. }
  638. RectTransform rectTransform = targetCanvas.GetComponent<RectTransform>();
  639. if (rectTransform != null)
  640. {
  641. canvasSize = rectTransform.sizeDelta;
  642. }
  643. else
  644. {
  645. canvasSize = new Vector2(800, 600); // 默认值
  646. }
  647. }
  648. /// <summary>
  649. /// 更新Sprite大小
  650. /// </summary>
  651. private void UpdateSpriteSize()
  652. {
  653. if (currentSprite == null)
  654. {
  655. spriteSize = Vector2.zero;
  656. return;
  657. }
  658. // 使用Sprite的像素大小
  659. spriteSize = new Vector2(currentSprite.rect.width, currentSprite.rect.height);
  660. }
  661. /// <summary>
  662. /// 计算网格
  663. /// </summary>
  664. private void CalculateGrid()
  665. {
  666. if (targetCanvas == null || currentSprite == null)
  667. {
  668. gridColumns = 0;
  669. gridRows = 0;
  670. return;
  671. }
  672. if (spriteSize.x <= 0 || spriteSize.y <= 0)
  673. {
  674. gridColumns = 0;
  675. gridRows = 0;
  676. return;
  677. }
  678. // 计算需要多少个单元以覆盖至少Canvas尺寸(向上取整)
  679. float cellW = spriteSize.x + cellGap.x;
  680. float cellH = spriteSize.y + cellGap.y;
  681. gridColumns = Mathf.Max(1, Mathf.CeilToInt(canvasSize.x / Mathf.Max(1f, cellW)));
  682. gridRows = Mathf.Max(1, Mathf.CeilToInt(canvasSize.y / Mathf.Max(1f, cellH)));
  683. Repaint();
  684. }
  685. /// <summary>
  686. /// 检查是否有放置的Sprite
  687. /// </summary>
  688. private bool HasPlacedSprites()
  689. {
  690. foreach (var layer in spriteLayers.Values)
  691. {
  692. if (layer.Count > 0) return true;
  693. }
  694. return false;
  695. }
  696. /// <summary>
  697. /// 保存为纹理贴图
  698. /// </summary>
  699. private void SaveAsTexture()
  700. {
  701. if (targetCanvas == null || currentSprite == null) return;
  702. // 直接使用Canvas的实际尺寸作为纹理尺寸,不进行任何缩放
  703. int textureWidth = Mathf.RoundToInt(canvasSize.x);
  704. int textureHeight = Mathf.RoundToInt(canvasSize.y);
  705. // Canvas坐标系:中心为原点,向右为+X,向上为+Y
  706. // 纹理坐标系:左下角为原点,向右为+X,向上为+Y
  707. float minX = -canvasSize.x / 2f;
  708. float minY = -canvasSize.y / 2f;
  709. float maxX = canvasSize.x / 2f;
  710. float maxY = canvasSize.y / 2f;
  711. // 收集所有已放置的Sprite信息(按图层顺序)
  712. var entries = new System.Collections.Generic.List<(Sprite sprite, Texture2D tex, Rect spriteRect, Vector2 size, Vector2 center)>();
  713. // 按图层列表的顺序收集Sprite(确保渲染顺序正确)
  714. foreach (var layerInfo in spriteLayersList)
  715. {
  716. if (!layerInfo.isVisible || layerInfo.sprite == null) continue;
  717. if (spriteLayers.ContainsKey(layerInfo.sprite))
  718. {
  719. var dict = spriteLayers[layerInfo.sprite];
  720. foreach (var kvp in dict)
  721. {
  722. GameObject go = kvp.Value;
  723. if (go == null) continue;
  724. var rt = go.GetComponent<RectTransform>();
  725. var img = go.GetComponent<UnityEngine.UI.Image>();
  726. if (rt == null || img == null || img.sprite == null) continue;
  727. Vector2 size = rt.sizeDelta; // 实际显示尺寸(像素)
  728. Vector2 center = rt.anchoredPosition; // 以Canvas中心为原点
  729. var sp = img.sprite;
  730. entries.Add((sp, sp.texture, sp.rect, size, center));
  731. }
  732. }
  733. }
  734. // 创建纹理
  735. Texture2D finalTexture = new Texture2D(textureWidth, textureHeight, TextureFormat.RGBA32, false);
  736. // 填充透明背景(确保整个Canvas范围都是透明的)
  737. Color[] pixels = new Color[textureWidth * textureHeight];
  738. for (int i = 0; i < pixels.Length; i++) pixels[i] = Color.clear;
  739. finalTexture.SetPixels(pixels);
  740. // 2) 绘制每个已放置的Sprite元素:只渲染在Canvas范围内的部分
  741. foreach (var e in entries)
  742. {
  743. // 计算Sprite在Canvas坐标系中的边界
  744. float spriteLeft = e.center.x - e.size.x / 2f;
  745. float spriteRight = e.center.x + e.size.x / 2f;
  746. float spriteBottom = e.center.y - e.size.y / 2f;
  747. float spriteTop = e.center.y + e.size.y / 2f;
  748. // 检查Sprite是否与Canvas范围有交集
  749. if (spriteRight <= minX || spriteLeft >= maxX || spriteTop <= minY || spriteBottom >= maxY)
  750. {
  751. continue; // 完全超出Canvas范围,跳过
  752. }
  753. // 计算在Canvas范围内的裁剪区域
  754. float clipLeft = Mathf.Max(spriteLeft, minX);
  755. float clipRight = Mathf.Min(spriteRight, maxX);
  756. float clipBottom = Mathf.Max(spriteBottom, minY);
  757. float clipTop = Mathf.Min(spriteTop, maxY);
  758. // 转换为纹理坐标(左下角为原点)
  759. int startX = Mathf.RoundToInt(clipLeft - minX);
  760. int startY = Mathf.RoundToInt(clipBottom - minY);
  761. int endX = Mathf.RoundToInt(clipRight - minX);
  762. int endY = Mathf.RoundToInt(clipTop - minY);
  763. // 计算源纹理的UV偏移(由于裁剪)
  764. float uvOffsetX = (clipLeft - spriteLeft) / e.size.x;
  765. float uvOffsetY = (clipBottom - spriteBottom) / e.size.y;
  766. float uvScaleX = (clipRight - clipLeft) / e.size.x;
  767. float uvScaleY = (clipTop - clipBottom) / e.size.y;
  768. // 源UV
  769. Rect spriteRect = e.spriteRect;
  770. Texture2D spriteTexture = e.tex;
  771. Rect baseUv = new Rect(
  772. spriteRect.x / spriteTexture.width,
  773. spriteRect.y / spriteTexture.height,
  774. spriteRect.width / spriteTexture.width,
  775. spriteRect.height / spriteTexture.height
  776. );
  777. // 应用裁剪后的UV
  778. Rect uv = new Rect(
  779. baseUv.x + uvOffsetX * baseUv.width,
  780. baseUv.y + uvOffsetY * baseUv.height,
  781. baseUv.width * uvScaleX,
  782. baseUv.height * uvScaleY
  783. );
  784. // 确保坐标在有效范围内
  785. startX = Mathf.Clamp(startX, 0, textureWidth - 1);
  786. startY = Mathf.Clamp(startY, 0, textureHeight - 1);
  787. endX = Mathf.Clamp(endX, 0, textureWidth);
  788. endY = Mathf.Clamp(endY, 0, textureHeight);
  789. for (int y = startY; y < endY; y++)
  790. {
  791. for (int x = startX; x < endX; x++)
  792. {
  793. float u = (x - startX) / (float)(endX - startX) * uv.width + uv.x;
  794. float v = (y - startY) / (float)(endY - startY) * uv.height + uv.y;
  795. int sx = Mathf.Clamp(Mathf.RoundToInt(u * spriteTexture.width), 0, spriteTexture.width - 1);
  796. int sy = Mathf.Clamp(Mathf.RoundToInt(v * spriteTexture.height), 0, spriteTexture.height - 1);
  797. Color pc = spriteTexture.GetPixel(sx, sy);
  798. int fi = y * textureWidth + x;
  799. if (pc.a > 0)
  800. {
  801. pixels[fi] = Color.Lerp(pixels[fi], pc, pc.a);
  802. }
  803. }
  804. }
  805. }
  806. // 应用像素到纹理
  807. finalTexture.SetPixels(pixels);
  808. finalTexture.Apply();
  809. // 保存纹理文件
  810. string fileName = $"GridMap_{System.DateTime.Now:yyyyMMdd_HHmmss}.png";
  811. string filePath = EditorUtility.SaveFilePanel("保存网格地图纹理", "Assets", fileName, "png");
  812. if (!string.IsNullOrEmpty(filePath))
  813. {
  814. byte[] pngData = finalTexture.EncodeToPNG();
  815. System.IO.File.WriteAllBytes(filePath, pngData);
  816. // 刷新资源数据库
  817. AssetDatabase.Refresh();
  818. // 自动清理生成的GameObject和图层数据
  819. CleanupAllData();
  820. EditorUtility.DisplayDialog("保存成功", $"网格地图已保存为纹理贴图:\n{filePath}\n\n纹理尺寸:{textureWidth}x{textureHeight}\n(与Canvas尺寸完全一致,超出部分不渲染)\n\n已自动清理生成的GameObject和图层数据", "确定");
  821. }
  822. // 清理临时纹理
  823. if (finalTexture != null)
  824. {
  825. DestroyImmediate(finalTexture);
  826. }
  827. }
  828. /// <summary>
  829. /// 添加新图层
  830. /// </summary>
  831. public void AddLayer(Sprite sprite)
  832. {
  833. if (sprite == null) return;
  834. var newLayer = new SpriteLayer(sprite);
  835. spriteLayersList.Add(newLayer);
  836. selectedLayerIndex = spriteLayersList.Count - 1;
  837. currentSprite = sprite;
  838. UpdateSpriteSize();
  839. CalculateGrid();
  840. // 更新GameObject的渲染顺序
  841. UpdateGameObjectRenderOrder();
  842. Repaint();
  843. }
  844. /// <summary>
  845. /// 删除图层
  846. /// </summary>
  847. private void RemoveLayer(int index)
  848. {
  849. if (index < 0 || index >= spriteLayersList.Count) return;
  850. var layer = spriteLayersList[index];
  851. // 清理该图层的GameObject
  852. if (targetCanvas != null)
  853. {
  854. string parentName = $"GridLayer_{layer.sprite.name}";
  855. Transform existing = targetCanvas.transform.Find(parentName);
  856. if (existing != null)
  857. {
  858. DestroyImmediate(existing.gameObject);
  859. }
  860. }
  861. // 从列表中移除
  862. spriteLayersList.RemoveAt(index);
  863. // 更新选中索引
  864. if (selectedLayerIndex == index)
  865. {
  866. selectedLayerIndex = -1;
  867. currentSprite = null;
  868. }
  869. else if (selectedLayerIndex > index)
  870. {
  871. selectedLayerIndex--;
  872. }
  873. // 如果还有图层,选择第一个
  874. if (spriteLayersList.Count > 0 && selectedLayerIndex == -1)
  875. {
  876. selectedLayerIndex = 0;
  877. currentSprite = spriteLayersList[0].sprite;
  878. UpdateSpriteSize();
  879. CalculateGrid();
  880. }
  881. Repaint();
  882. }
  883. /// <summary>
  884. /// 向上移动图层
  885. /// </summary>
  886. private void MoveLayerUp(int index)
  887. {
  888. if (index <= 0) return;
  889. var layer = spriteLayersList[index];
  890. spriteLayersList.RemoveAt(index);
  891. spriteLayersList.Insert(index - 1, layer);
  892. // 更新选中索引
  893. if (selectedLayerIndex == index)
  894. {
  895. selectedLayerIndex = index - 1;
  896. }
  897. else if (selectedLayerIndex == index - 1)
  898. {
  899. selectedLayerIndex = index;
  900. }
  901. // 更新GameObject的渲染顺序
  902. UpdateGameObjectRenderOrder();
  903. Repaint();
  904. }
  905. /// <summary>
  906. /// 向下移动图层
  907. /// </summary>
  908. private void MoveLayerDown(int index)
  909. {
  910. if (index >= spriteLayersList.Count - 1) return;
  911. var layer = spriteLayersList[index];
  912. spriteLayersList.RemoveAt(index);
  913. spriteLayersList.Insert(index + 1, layer);
  914. // 更新选中索引
  915. if (selectedLayerIndex == index)
  916. {
  917. selectedLayerIndex = index + 1;
  918. }
  919. else if (selectedLayerIndex == index + 1)
  920. {
  921. selectedLayerIndex = index;
  922. }
  923. // 更新GameObject的渲染顺序
  924. UpdateGameObjectRenderOrder();
  925. Repaint();
  926. }
  927. /// <summary>
  928. /// 更新GameObject的渲染顺序
  929. /// </summary>
  930. private void UpdateGameObjectRenderOrder()
  931. {
  932. if (targetCanvas == null) return;
  933. // 按照图层列表的顺序重新排列GameObject
  934. for (int i = 0; i < spriteLayersList.Count; i++)
  935. {
  936. var layer = spriteLayersList[i];
  937. if (layer.sprite == null) continue;
  938. string parentName = $"GridLayer_{layer.sprite.name}";
  939. Transform layerParent = targetCanvas.transform.Find(parentName);
  940. if (layerParent != null)
  941. {
  942. // 设置SiblingIndex来控制渲染顺序
  943. layerParent.SetSiblingIndex(i);
  944. }
  945. }
  946. }
  947. /// <summary>
  948. /// 清空所有图层
  949. /// </summary>
  950. private void ClearAllLayers()
  951. {
  952. // 使用统一的清理方法
  953. CleanupAllData();
  954. }
  955. /// <summary>
  956. /// 清理所有数据(保存后自动清理)
  957. /// </summary>
  958. private void CleanupAllData()
  959. {
  960. // 清理生成的GameObject
  961. CleanupGeneratedObjects();
  962. // 清理图层列表
  963. spriteLayersList.Clear();
  964. selectedLayerIndex = -1;
  965. currentSprite = null;
  966. spriteSize = Vector2.zero;
  967. gridColumns = 0;
  968. gridRows = 0;
  969. Repaint();
  970. }
  971. /// <summary>
  972. /// 清理生成的GameObject
  973. /// </summary>
  974. private void CleanupGeneratedObjects()
  975. {
  976. if (targetCanvas == null) return;
  977. // 删除所有以"GridLayer_"开头的子对象
  978. for (int i = targetCanvas.transform.childCount - 1; i >= 0; i--)
  979. {
  980. Transform child = targetCanvas.transform.GetChild(i);
  981. if (child.name.StartsWith("GridLayer_"))
  982. {
  983. DestroyImmediate(child.gameObject);
  984. }
  985. }
  986. // 清空数据
  987. spriteLayers.Clear();
  988. layerCanvasPositions.Clear();
  989. Repaint();
  990. }
  991. // -------- 配置保存/加载 --------
  992. private void SaveConfigToFile()
  993. {
  994. if (targetCanvas == null)
  995. {
  996. EditorUtility.DisplayDialog("提示", "请先选择Canvas后再保存配置", "确定");
  997. return;
  998. }
  999. var cfg = BuildCurrentConfig();
  1000. string fileName = $"GridMapConfig_{System.DateTime.Now:yyyyMMdd_HHmmss}.json";
  1001. string path = EditorUtility.SaveFilePanel("保存配置", "Assets", fileName, "json");
  1002. if (string.IsNullOrEmpty(path)) return;
  1003. string json = JsonUtility.ToJson(cfg, true);
  1004. System.IO.File.WriteAllText(path, json, System.Text.Encoding.UTF8);
  1005. AssetDatabase.Refresh();
  1006. // 保存后自动清理
  1007. CleanupAllData();
  1008. EditorUtility.DisplayDialog("保存成功", "配置已保存并已自动清理现场。", "确定");
  1009. }
  1010. private MapConfig BuildCurrentConfig()
  1011. {
  1012. var cfg = new MapConfig
  1013. {
  1014. canvasName = targetCanvas != null ? targetCanvas.name : string.Empty,
  1015. canvasWidth = canvasSize.x,
  1016. canvasHeight = canvasSize.y
  1017. };
  1018. for (int i = 0; i < spriteLayersList.Count; i++)
  1019. {
  1020. var layer = spriteLayersList[i];
  1021. if (layer.sprite == null) continue;
  1022. var layerCfg = new LayerConfig
  1023. {
  1024. name = layer.name,
  1025. isVisible = layer.isVisible,
  1026. isPreviewVisible = layer.isPreviewVisible,
  1027. spriteAssetPath = AssetDatabase.GetAssetPath(layer.sprite),
  1028. resourcesPath = GetResourcesPath(layer.sprite)
  1029. };
  1030. if (spriteLayers.ContainsKey(layer.sprite))
  1031. {
  1032. var dict = spriteLayers[layer.sprite];
  1033. foreach (var kv in dict)
  1034. {
  1035. Vector2Int cell = kv.Key;
  1036. Vector2 pos;
  1037. if (layerCanvasPositions.ContainsKey(layer.sprite) && layerCanvasPositions[layer.sprite].TryGetValue(cell, out pos))
  1038. {
  1039. layerCfg.items.Add(new ItemConfig
  1040. {
  1041. cellX = cell.x,
  1042. cellY = cell.y,
  1043. posX = pos.x,
  1044. posY = pos.y
  1045. });
  1046. }
  1047. }
  1048. }
  1049. cfg.layers.Add(layerCfg);
  1050. }
  1051. return cfg;
  1052. }
  1053. private void LoadConfigFromFile()
  1054. {
  1055. string path = EditorUtility.OpenFilePanel("加载配置", "Assets", "json");
  1056. if (string.IsNullOrEmpty(path)) return;
  1057. string json = System.IO.File.ReadAllText(path, System.Text.Encoding.UTF8);
  1058. LoadConfigFromJson(json);
  1059. }
  1060. // 可在运行时调用
  1061. public void LoadConfigFromJson(string json)
  1062. {
  1063. if (string.IsNullOrEmpty(json)) return;
  1064. var cfg = JsonUtility.FromJson<MapConfig>(json);
  1065. if (cfg == null) return;
  1066. // 清空现有
  1067. ClearAllLayers();
  1068. // 恢复Canvas尺寸(仅编辑器显示用)
  1069. canvasSize = new Vector2(cfg.canvasWidth, cfg.canvasHeight);
  1070. // 重建图层
  1071. foreach (var layerCfg in cfg.layers)
  1072. {
  1073. Sprite sp = null;
  1074. // 优先从Asset路径加载(编辑器)
  1075. if (!string.IsNullOrEmpty(layerCfg.spriteAssetPath))
  1076. {
  1077. sp = AssetDatabase.LoadAssetAtPath<Sprite>(layerCfg.spriteAssetPath);
  1078. }
  1079. // 其次尝试Resources路径(运行时)
  1080. if (sp == null && !string.IsNullOrEmpty(layerCfg.resourcesPath))
  1081. {
  1082. sp = Resources.Load<Sprite>(layerCfg.resourcesPath);
  1083. }
  1084. if (sp == null) continue;
  1085. AddLayer(sp);
  1086. var layer = spriteLayersList[spriteLayersList.Count - 1];
  1087. layer.name = string.IsNullOrEmpty(layerCfg.name) ? sp.name : layerCfg.name;
  1088. layer.isVisible = layerCfg.isVisible;
  1089. layer.isPreviewVisible = layerCfg.isPreviewVisible;
  1090. // 以当前选中图层的网格基准进行单元计算,但位置使用记录的Canvas位置
  1091. foreach (var item in layerCfg.items)
  1092. {
  1093. Vector2Int cell = new Vector2Int(item.cellX, item.cellY);
  1094. if (!spriteLayers.ContainsKey(sp))
  1095. {
  1096. spriteLayers[sp] = new System.Collections.Generic.Dictionary<Vector2Int, GameObject>();
  1097. }
  1098. if (!layerCanvasPositions.ContainsKey(sp))
  1099. {
  1100. layerCanvasPositions[sp] = new System.Collections.Generic.Dictionary<Vector2Int, Vector2>();
  1101. }
  1102. // 创建GO
  1103. GameObject parent = GetOrCreateLayerParent(sp);
  1104. GameObject go = new GameObject($"Cell_{cell.x}_{cell.y}");
  1105. go.transform.SetParent(parent.transform, false);
  1106. var img = go.AddComponent<UnityEngine.UI.Image>();
  1107. img.sprite = sp;
  1108. var rt = go.GetComponent<RectTransform>();
  1109. rt.anchorMin = new Vector2(0.5f, 0.5f);
  1110. rt.anchorMax = new Vector2(0.5f, 0.5f);
  1111. rt.sizeDelta = new Vector2(sp.rect.width, sp.rect.height);
  1112. rt.anchoredPosition = new Vector2(item.posX, item.posY);
  1113. spriteLayers[sp][cell] = go;
  1114. layerCanvasPositions[sp][cell] = new Vector2(item.posX, item.posY);
  1115. }
  1116. }
  1117. // 刷新
  1118. UpdateSpriteSize();
  1119. CalculateGrid();
  1120. UpdateGameObjectRenderOrder();
  1121. Repaint();
  1122. }
  1123. private static string GetResourcesPath(Sprite sprite)
  1124. {
  1125. string assetPath = AssetDatabase.GetAssetPath(sprite);
  1126. if (string.IsNullOrEmpty(assetPath)) return string.Empty;
  1127. const string key = "/Resources/";
  1128. int idx = assetPath.LastIndexOf(key, System.StringComparison.OrdinalIgnoreCase);
  1129. if (idx < 0) return string.Empty;
  1130. string rel = assetPath.Substring(idx + key.Length);
  1131. int dot = rel.LastIndexOf('.');
  1132. if (dot >= 0) rel = rel.Substring(0, dot);
  1133. return rel.Replace('\\', '/');
  1134. }
  1135. }
  1136. }