2023-12-15 18:32:21 +08:00
|
|
|
|
using System.IO;
|
2024-11-20 19:55:55 +08:00
|
|
|
|
using cfg.FightCfg;
|
2023-12-15 18:32:21 +08:00
|
|
|
|
using Gameplay;
|
|
|
|
|
|
using MapEditor;
|
|
|
|
|
|
using Sirenix.OdinInspector.Editor;
|
|
|
|
|
|
using TGS;
|
|
|
|
|
|
using UnityEditor;
|
|
|
|
|
|
using UnityEditor.SceneManagement;
|
|
|
|
|
|
using UnityEngine;
|
2024-08-08 18:57:34 +08:00
|
|
|
|
using UnityEngine.Rendering.Universal;
|
2025-08-25 17:34:39 +08:00
|
|
|
|
using UnityEngine.SceneManagement;
|
2024-08-08 18:57:34 +08:00
|
|
|
|
using Constants = Framework.Constants;
|
2023-12-15 18:32:21 +08:00
|
|
|
|
using Object = UnityEngine.Object;
|
|
|
|
|
|
|
|
|
|
|
|
[CustomEditor(typeof(MapManager))]
|
|
|
|
|
|
public class MapManagerInspector : OdinEditor
|
|
|
|
|
|
{
|
2023-12-21 15:05:08 +08:00
|
|
|
|
private enum ECameraCheckCorner
|
|
|
|
|
|
{
|
|
|
|
|
|
None,
|
|
|
|
|
|
LeftDown,
|
|
|
|
|
|
RightDown,
|
|
|
|
|
|
LeftUp,
|
|
|
|
|
|
RightUp,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private readonly string[] checkCornerType = new[]
|
|
|
|
|
|
{
|
|
|
|
|
|
"无",
|
|
|
|
|
|
"左下角",
|
|
|
|
|
|
"右下角",
|
|
|
|
|
|
"左上角",
|
|
|
|
|
|
"右上角",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-12-15 18:32:21 +08:00
|
|
|
|
private readonly string[] environmentType = new[]
|
|
|
|
|
|
{
|
|
|
|
|
|
"平原",
|
|
|
|
|
|
"山地",
|
|
|
|
|
|
"草原",
|
|
|
|
|
|
"林地",
|
|
|
|
|
|
"雪地",
|
|
|
|
|
|
"泥地",
|
|
|
|
|
|
"戈壁",
|
|
|
|
|
|
"沙漠",
|
|
|
|
|
|
"高原",
|
2025-09-16 13:09:32 +08:00
|
|
|
|
"竞技场",
|
2025-12-10 16:44:38 +08:00
|
|
|
|
"火车关",
|
2023-12-15 18:32:21 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
private MapManager Target => target as MapManager;
|
|
|
|
|
|
|
|
|
|
|
|
private TerrainGridSystem TGS => Target.TGS;
|
|
|
|
|
|
|
|
|
|
|
|
private MapData MapData => Target.MapData;
|
2024-01-12 14:17:17 +08:00
|
|
|
|
|
|
|
|
|
|
private Map Map => Target.Map;
|
2024-08-08 18:57:34 +08:00
|
|
|
|
|
2023-12-15 18:32:21 +08:00
|
|
|
|
private bool _isEditingGrid;
|
2023-12-21 15:05:08 +08:00
|
|
|
|
private bool _isEditingCameraOriginPos;
|
|
|
|
|
|
private bool _isCheckingCamera;
|
2024-08-12 13:01:56 +08:00
|
|
|
|
private bool _isEditingCameraInfo;
|
2023-12-15 18:32:21 +08:00
|
|
|
|
|
2023-12-21 15:05:08 +08:00
|
|
|
|
private ECameraCheckCorner _checkType;
|
2023-12-15 18:32:21 +08:00
|
|
|
|
|
2025-08-25 17:34:39 +08:00
|
|
|
|
private const string LegionMapConfigPrefix = "LegionMapData_";
|
2024-07-02 18:13:12 +08:00
|
|
|
|
private const string MapConfigPrefix = "MapData_";
|
2024-08-08 18:57:34 +08:00
|
|
|
|
private const string UICanvasCheck = "Assets/Editor/MapEditor/LevelUICheck.prefab";
|
|
|
|
|
|
private GameObject _uiLevelCheck;
|
2024-07-02 18:13:12 +08:00
|
|
|
|
|
2023-12-21 15:05:08 +08:00
|
|
|
|
//scene gui
|
2023-12-15 18:32:21 +08:00
|
|
|
|
private bool _isHitGround;
|
|
|
|
|
|
private Vector3 _mouseHitPosition;
|
|
|
|
|
|
private Event _currEvent;
|
|
|
|
|
|
private GameObject _ground;
|
|
|
|
|
|
private RaycastHit[] _hits = new RaycastHit[1];
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnEnable()
|
|
|
|
|
|
{
|
|
|
|
|
|
base.OnEnable();
|
|
|
|
|
|
var allTgs = Object.FindObjectsOfType<TerrainGridSystem>();
|
|
|
|
|
|
foreach (var tgs in allTgs)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tgs != TGS)
|
|
|
|
|
|
{
|
2023-12-18 20:31:58 +08:00
|
|
|
|
foreach (Transform child in tgs.transform)
|
|
|
|
|
|
{
|
|
|
|
|
|
child.gameObject.SetActive(false);
|
|
|
|
|
|
}
|
2023-12-15 18:32:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-12-18 19:42:46 +08:00
|
|
|
|
|
|
|
|
|
|
DeActiveScene();
|
2026-01-13 12:42:16 +08:00
|
|
|
|
ApplyInitialCameraPosition();
|
2023-12-18 19:42:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void DeActiveScene()
|
|
|
|
|
|
{
|
|
|
|
|
|
var sceneManagers = FindObjectsOfType<NLDSceneManager>();
|
|
|
|
|
|
foreach (var sceneManager in sceneManagers)
|
|
|
|
|
|
{
|
2023-12-20 12:47:57 +08:00
|
|
|
|
SceneVisibilityManager.instance.DisablePicking(sceneManager.Art.gameObject, true);
|
2023-12-18 19:42:46 +08:00
|
|
|
|
}
|
2023-12-15 18:32:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnDisable()
|
|
|
|
|
|
{
|
2024-08-08 18:57:34 +08:00
|
|
|
|
if (_uiLevelCheck)
|
|
|
|
|
|
DestroyImmediate(_uiLevelCheck);
|
2024-08-12 13:01:56 +08:00
|
|
|
|
if (GameObject.Find("LevelUICheck(Clone)"))
|
2024-08-08 18:57:34 +08:00
|
|
|
|
DestroyImmediate(GameObject.Find("LevelUICheck(Clone)"));
|
2023-12-15 18:32:21 +08:00
|
|
|
|
base.OnDisable();
|
|
|
|
|
|
Target.SaveMapData();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-13 12:42:16 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 加载地图后应用存储的相机初始位置与FOV
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ApplyInitialCameraPosition()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Application.isPlaying)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
if (Target == null || Target.CameraController == null || Map == null || TGS == null)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
var camera = Target.CameraController;
|
|
|
|
|
|
camera.CameraBounds = TGS.bounds;
|
|
|
|
|
|
var initPos = Map.BlockPosition2WorldPosition(MapData.initialCameraPosition);
|
|
|
|
|
|
camera.InitializeCameraPosition(
|
|
|
|
|
|
initPos,
|
|
|
|
|
|
MapData.cameraMarginInfo,
|
|
|
|
|
|
camera.rightMargin,
|
|
|
|
|
|
camera.leftMargin,
|
|
|
|
|
|
camera.topMargin,
|
|
|
|
|
|
camera.bottomMargin);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-15 18:32:21 +08:00
|
|
|
|
public override void OnInspectorGUI()
|
|
|
|
|
|
{
|
2023-12-21 15:05:08 +08:00
|
|
|
|
if (Application.isPlaying)
|
2023-12-20 12:47:57 +08:00
|
|
|
|
return;
|
2023-12-15 18:32:21 +08:00
|
|
|
|
base.OnInspectorGUI();
|
|
|
|
|
|
DrawCreateMapData();
|
|
|
|
|
|
DrawMapData();
|
2023-12-20 12:47:57 +08:00
|
|
|
|
EditorSceneManager.MarkSceneDirty(Target.gameObject.scene);
|
2023-12-15 18:32:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void DrawCreateMapData()
|
|
|
|
|
|
{
|
2023-12-21 19:08:04 +08:00
|
|
|
|
if (GUILayout.Button("创建地图配置"))
|
2023-12-15 18:32:21 +08:00
|
|
|
|
{
|
2023-12-21 19:08:04 +08:00
|
|
|
|
var mapDataDir = Path.GetDirectoryName(Constants.MAP_CONFIG_FORMAT_PATH);
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
|
|
|
|
|
var mapDataName = Target.isLegionMap
|
|
|
|
|
|
? $"{mapDataDir}/{LegionMapConfigPrefix + Target.gameObject.scene.name}.json"
|
|
|
|
|
|
: $"{mapDataDir}/{MapConfigPrefix + Target.gameObject.scene.name}.json";
|
|
|
|
|
|
var dataPath = EditorUtil.GetAssetUniquePath(mapDataName, out var textName);
|
2023-12-21 19:08:04 +08:00
|
|
|
|
using (var fileStream = File.Create(dataPath))
|
2023-12-15 18:32:21 +08:00
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-21 19:08:04 +08:00
|
|
|
|
AssetDatabase.Refresh();
|
|
|
|
|
|
var textAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(dataPath);
|
|
|
|
|
|
EditorGUIUtility.PingObject(textAsset);
|
2023-12-26 17:05:08 +08:00
|
|
|
|
Target.MapDataJson = textAsset;
|
2023-12-15 18:32:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void DrawMapData()
|
|
|
|
|
|
{
|
2026-01-13 12:42:16 +08:00
|
|
|
|
var mapAsset = Target.MapDataJson;
|
|
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
|
|
mapAsset = EditorGUILayout.ObjectField("地图配置", mapAsset, typeof(TextAsset), false) as TextAsset;
|
|
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
|
|
|
|
{
|
|
|
|
|
|
Target.MapDataJson = mapAsset;
|
|
|
|
|
|
ApplyInitialCameraPosition();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-15 18:32:21 +08:00
|
|
|
|
if (Target.MapDataJson != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorGUI.BeginChangeCheck();
|
2023-12-18 19:42:46 +08:00
|
|
|
|
var column = EditorGUILayout.DelayedIntField("网格宽度", TGS.columnCount);
|
|
|
|
|
|
var raw = EditorGUILayout.DelayedIntField("网格高度", TGS.rowCount);
|
2023-12-15 18:32:21 +08:00
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
|
|
|
|
{
|
2023-12-18 19:42:46 +08:00
|
|
|
|
var width = column;
|
|
|
|
|
|
var height = raw;
|
|
|
|
|
|
MapData.Width = width;
|
|
|
|
|
|
MapData.Height = height;
|
2023-12-15 18:32:21 +08:00
|
|
|
|
TGS.columnCount = width;
|
|
|
|
|
|
TGS.rowCount = height;
|
2023-12-18 19:42:46 +08:00
|
|
|
|
Target.SaveMapData();
|
|
|
|
|
|
Target.Refresh();
|
2023-12-20 12:47:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
|
|
var gridOffset = EditorGUILayout.Vector2Field("网格偏移",
|
|
|
|
|
|
new Vector2(TGS.transform.position.x, TGS.transform.position.z));
|
|
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
|
|
|
|
{
|
|
|
|
|
|
var transform = TGS.transform;
|
|
|
|
|
|
transform.position = new Vector3(gridOffset.x, 0, gridOffset.y);
|
|
|
|
|
|
MapData.position = transform.position;
|
2023-12-15 18:32:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MapData.environmentType =
|
2024-11-20 19:55:55 +08:00
|
|
|
|
(ELevelTerrainType)EditorGUILayout.Popup("环境类型", (int)MapData.environmentType, environmentType);
|
2023-12-18 19:42:46 +08:00
|
|
|
|
|
2023-12-21 15:30:37 +08:00
|
|
|
|
DrawCheckCamera();
|
|
|
|
|
|
DrawCameraOriginPosition();
|
2023-12-21 15:05:08 +08:00
|
|
|
|
|
2023-12-21 15:30:37 +08:00
|
|
|
|
if (GUILayout.Button("打开网格编辑器"))
|
2023-12-21 15:05:08 +08:00
|
|
|
|
{
|
2025-08-25 17:34:39 +08:00
|
|
|
|
MapGridWindow.OpenWindow(Target, Target.isLegionMap);
|
2023-12-21 15:05:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-08 18:37:04 +08:00
|
|
|
|
// DrawCameraInfo();
|
2023-12-21 15:30:37 +08:00
|
|
|
|
if (GUILayout.Button("保存"))
|
2023-12-21 15:05:08 +08:00
|
|
|
|
{
|
2023-12-21 15:30:37 +08:00
|
|
|
|
Target.SaveMapData();
|
2023-12-21 15:05:08 +08:00
|
|
|
|
}
|
2023-12-21 15:30:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-12-21 15:05:08 +08:00
|
|
|
|
|
2026-01-08 18:37:04 +08:00
|
|
|
|
/*private void DrawCameraInfo()
|
2024-08-12 13:01:56 +08:00
|
|
|
|
{
|
|
|
|
|
|
_isEditingCameraInfo = EditorGUILayout.Toggle("编辑相机间距", _isEditingCameraInfo);
|
|
|
|
|
|
if (_isEditingCameraInfo)
|
|
|
|
|
|
{
|
|
|
|
|
|
MapData.cameraMarginInfo.leftMarginInFight =
|
|
|
|
|
|
EditorGUILayout.FloatField("战斗中相机左间距", MapData.cameraMarginInfo.leftMarginInFight);
|
|
|
|
|
|
MapData.cameraMarginInfo.rightMarginInFight =
|
|
|
|
|
|
EditorGUILayout.FloatField("战斗中相机右间距", MapData.cameraMarginInfo.rightMarginInFight);
|
|
|
|
|
|
MapData.cameraMarginInfo.topMarginInFight =
|
|
|
|
|
|
EditorGUILayout.FloatField("战斗中相机上间距", MapData.cameraMarginInfo.topMarginInFight);
|
|
|
|
|
|
MapData.cameraMarginInfo.bottomMarginInFight =
|
|
|
|
|
|
EditorGUILayout.FloatField("战斗中相机下间距", MapData.cameraMarginInfo.bottomMarginInFight);
|
|
|
|
|
|
|
|
|
|
|
|
MapData.cameraMarginInfo.leftMarginInPre =
|
|
|
|
|
|
EditorGUILayout.FloatField("准备前相机左间距", MapData.cameraMarginInfo.leftMarginInPre);
|
|
|
|
|
|
MapData.cameraMarginInfo.rightMarginInPre =
|
|
|
|
|
|
EditorGUILayout.FloatField("准备前相机右间距", MapData.cameraMarginInfo.rightMarginInPre);
|
|
|
|
|
|
MapData.cameraMarginInfo.topMarginInInPre =
|
|
|
|
|
|
EditorGUILayout.FloatField("准备前相机上间距", MapData.cameraMarginInfo.topMarginInInPre);
|
|
|
|
|
|
MapData.cameraMarginInfo.bottomMarginInPre =
|
|
|
|
|
|
EditorGUILayout.FloatField("准备前相机下间距", MapData.cameraMarginInfo.bottomMarginInPre);
|
|
|
|
|
|
}
|
2026-01-08 18:37:04 +08:00
|
|
|
|
}*/
|
2024-08-12 13:01:56 +08:00
|
|
|
|
|
2023-12-21 15:30:37 +08:00
|
|
|
|
private void DrawCheckCamera()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_isCheckingCamera && GUILayout.Button("检查相机可否超出地图"))
|
|
|
|
|
|
{
|
|
|
|
|
|
_isCheckingCamera = true;
|
2025-04-29 15:30:55 +08:00
|
|
|
|
Target.CameraController.GetComponent<Camera>().orthographicSize = Target.CameraController.CameraMaxSize;
|
2024-08-08 18:57:34 +08:00
|
|
|
|
if (_uiLevelCheck == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var ui = AssetDatabase.LoadAssetAtPath<GameObject>(UICanvasCheck);
|
|
|
|
|
|
_uiLevelCheck = Instantiate(ui);
|
|
|
|
|
|
var uiCamera = _uiLevelCheck.transform.Find("UICamera").GetComponent<Camera>();
|
|
|
|
|
|
var baseCameraData = Camera.main?.GetComponent<UniversalAdditionalCameraData>();
|
|
|
|
|
|
baseCameraData?.cameraStack.Add(uiCamera);
|
|
|
|
|
|
}
|
2024-08-12 13:01:56 +08:00
|
|
|
|
}
|
2023-12-21 15:30:37 +08:00
|
|
|
|
|
|
|
|
|
|
if (_isCheckingCamera && GUILayout.Button("退出相机检查"))
|
|
|
|
|
|
{
|
2024-08-08 18:57:34 +08:00
|
|
|
|
if (_uiLevelCheck)
|
|
|
|
|
|
DestroyImmediate(_uiLevelCheck);
|
2023-12-21 15:30:37 +08:00
|
|
|
|
_isCheckingCamera = false;
|
2025-04-29 15:30:55 +08:00
|
|
|
|
Target.CameraController.GetComponent<Camera>().orthographicSize = Target.CameraController.CameraMinSize;
|
2023-12-21 15:30:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (_isCheckingCamera)
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
|
|
_checkType = (ECameraCheckCorner)EditorGUILayout.Popup("检查角落", (int)_checkType, checkCornerType);
|
|
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
2023-12-21 15:05:08 +08:00
|
|
|
|
{
|
2023-12-21 15:30:37 +08:00
|
|
|
|
switch (_checkType)
|
2023-12-21 15:05:08 +08:00
|
|
|
|
{
|
2023-12-21 15:30:37 +08:00
|
|
|
|
case ECameraCheckCorner.None:
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ECameraCheckCorner.LeftDown:
|
|
|
|
|
|
var leftDownPoint = new Vector2Int(0, 0);
|
2024-08-08 18:57:34 +08:00
|
|
|
|
Target.CameraController.InitializeCameraPosition(
|
|
|
|
|
|
Map.BlockPosition2WorldPosition(leftDownPoint));
|
2023-12-21 15:30:37 +08:00
|
|
|
|
break;
|
|
|
|
|
|
case ECameraCheckCorner.RightDown:
|
|
|
|
|
|
var rightDownPoint = new Vector2Int(0, TGS.columnCount - 1);
|
2024-08-08 18:57:34 +08:00
|
|
|
|
Target.CameraController.InitializeCameraPosition(
|
|
|
|
|
|
Map.BlockPosition2WorldPosition(rightDownPoint));
|
2023-12-21 15:30:37 +08:00
|
|
|
|
break;
|
|
|
|
|
|
case ECameraCheckCorner.LeftUp:
|
|
|
|
|
|
var leftUpPoint = new Vector2Int(TGS.rowCount - 1, 0);
|
2024-01-12 14:17:17 +08:00
|
|
|
|
Target.CameraController.InitializeCameraPosition(Map.BlockPosition2WorldPosition(leftUpPoint));
|
2023-12-21 15:30:37 +08:00
|
|
|
|
break;
|
|
|
|
|
|
case ECameraCheckCorner.RightUp:
|
|
|
|
|
|
var rightUpPoint = new Vector2Int(TGS.rowCount - 1, TGS.columnCount - 1);
|
2024-01-12 14:17:17 +08:00
|
|
|
|
Target.CameraController.InitializeCameraPosition(Map.BlockPosition2WorldPosition(rightUpPoint));
|
2023-12-21 15:30:37 +08:00
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
break;
|
2023-12-21 15:05:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-12-21 15:30:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-12-21 15:05:08 +08:00
|
|
|
|
|
2023-12-21 15:30:37 +08:00
|
|
|
|
private void DrawCameraOriginPosition()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_isEditingCameraOriginPos)
|
|
|
|
|
|
{
|
2025-04-08 16:01:38 +08:00
|
|
|
|
if (GUILayout.Button("编辑相机初始位置和大小"))
|
2023-12-15 18:32:21 +08:00
|
|
|
|
{
|
2023-12-21 15:30:37 +08:00
|
|
|
|
_isEditingCameraOriginPos = true;
|
2026-01-09 13:06:02 +08:00
|
|
|
|
// 编辑模式下提前设置相机包围盒,避免 CameraBounds 为空导致红框不显示/不更新
|
|
|
|
|
|
if (Target?.CameraController != null && Target?.TGS != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Target.CameraController.CameraBounds = Target.TGS.bounds;
|
|
|
|
|
|
}
|
2023-12-15 18:32:21 +08:00
|
|
|
|
}
|
2023-12-21 15:30:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-04-08 16:01:38 +08:00
|
|
|
|
// 添加相机FOV和正交大小设置
|
|
|
|
|
|
EditorGUI.BeginChangeCheck();
|
2025-08-25 17:34:39 +08:00
|
|
|
|
MapData.cameraMarginInfo.initialFOV = EditorGUILayout.Slider("初始FOV", MapData.cameraMarginInfo.initialFOV,
|
|
|
|
|
|
Target.CameraController.CameraMinFOV, Target.CameraController.CameraMaxFOV);
|
2025-04-08 16:01:38 +08:00
|
|
|
|
// MapData.cameraMarginInfo.initialOrthographicSize = EditorGUILayout.Slider("初始正交大小", MapData.cameraMarginInfo.initialOrthographicSize, Target.CameraController.CameraMinSize, Target.CameraController.CameraMaxSize);
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-08 16:01:38 +08:00
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
|
|
|
|
{
|
|
|
|
|
|
// 实时预览FOV设置效果
|
|
|
|
|
|
var camera = Target.CameraController.MainCamera;
|
|
|
|
|
|
if (camera != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (camera.orthographic)
|
|
|
|
|
|
{
|
|
|
|
|
|
camera.orthographicSize = MapData.cameraMarginInfo.initialOrthographicSize;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-04-08 18:51:32 +08:00
|
|
|
|
Target.CameraController.SetCameraFOVSize(MapData.cameraMarginInfo.initialFOV);
|
2025-08-25 17:34:39 +08:00
|
|
|
|
camera.fieldOfView = Mathf.Clamp(MapData.cameraMarginInfo.initialFOV,
|
|
|
|
|
|
Target.CameraController.CameraMinFOV,
|
2025-04-29 15:30:55 +08:00
|
|
|
|
Target.CameraController.CameraMaxFOV);
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-08 16:01:38 +08:00
|
|
|
|
// 强制刷新场景视图
|
|
|
|
|
|
SceneView.RepaintAll();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2023-12-21 15:30:37 +08:00
|
|
|
|
EditorGUILayout.LabelField("请在场景中点击网格来选取相机初始位置,并在Game窗口中查看");
|
|
|
|
|
|
if (GUILayout.Button("退出编辑相机初始位置"))
|
2023-12-15 18:32:21 +08:00
|
|
|
|
{
|
2023-12-21 15:30:37 +08:00
|
|
|
|
_isEditingCameraOriginPos = false;
|
2023-12-15 18:32:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-12-21 15:05:08 +08:00
|
|
|
|
|
|
|
|
|
|
private void OnSceneGUI()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_isEditingCameraOriginPos)
|
|
|
|
|
|
return;
|
|
|
|
|
|
ResetSceneGUIState(Event.current);
|
|
|
|
|
|
DrawMouse();
|
|
|
|
|
|
HandleInput();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-25 17:34:39 +08:00
|
|
|
|
private void ResetSceneGUIState(Event e)
|
2023-12-21 15:05:08 +08:00
|
|
|
|
{
|
|
|
|
|
|
_isHitGround = false;
|
|
|
|
|
|
_mouseHitPosition = Vector3.zero;
|
|
|
|
|
|
_currEvent = e;
|
|
|
|
|
|
_ground = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-25 17:34:39 +08:00
|
|
|
|
private void DrawMouse()
|
2023-12-21 15:05:08 +08:00
|
|
|
|
{
|
|
|
|
|
|
var mousePosSS = _currEvent.mousePosition;
|
|
|
|
|
|
var ray = HandleUtility.GUIPointToWorldRay(mousePosSS);
|
|
|
|
|
|
var count = Physics.RaycastNonAlloc(ray, _hits, 1000, 1 << LayerMask.NameToLayer("Ground"));
|
|
|
|
|
|
if (count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
var hit = _hits[0];
|
|
|
|
|
|
_mouseHitPosition = hit.point;
|
|
|
|
|
|
Handles.color = Color.yellow;
|
|
|
|
|
|
Handles.DrawSolidDisc(_mouseHitPosition, Vector3.up, 1);
|
|
|
|
|
|
_isHitGround = true;
|
|
|
|
|
|
_ground = hit.collider.gameObject;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-25 17:34:39 +08:00
|
|
|
|
private void HandleInput()
|
2023-12-21 15:05:08 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (!_isHitGround)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var controlID = GUIUtility.GetControlID(FocusType.Passive);
|
|
|
|
|
|
var eventType = _currEvent.GetTypeForControl(controlID);
|
|
|
|
|
|
|
|
|
|
|
|
switch (eventType)
|
|
|
|
|
|
{
|
|
|
|
|
|
case EventType.MouseDown:
|
|
|
|
|
|
if (_currEvent.button == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
GUIUtility.hotControl = controlID;
|
|
|
|
|
|
if (_isEditingCameraOriginPos)
|
|
|
|
|
|
{
|
|
|
|
|
|
var cell = TGS.CellGetAtPosition(_mouseHitPosition, true);
|
|
|
|
|
|
if (cell == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("点击点附件没有网格!设置相机初始位置失败!");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MapData.initialCameraPosition = MapData.BlockIndex2Position(cell.index);
|
2026-01-09 13:06:02 +08:00
|
|
|
|
// 确保相机包围盒使用当前地图的网格范围
|
|
|
|
|
|
if (Target?.CameraController != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Target.CameraController.CameraBounds = TGS.bounds;
|
|
|
|
|
|
}
|
2024-08-08 18:57:34 +08:00
|
|
|
|
Target.CameraController.InitializeCameraPosition(
|
|
|
|
|
|
Map.BlockPosition2WorldPosition(MapData.initialCameraPosition));
|
2023-12-21 15:05:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
case EventType.MouseUp:
|
|
|
|
|
|
if (GUIUtility.hotControl == controlID && _currEvent.button == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
GUIUtility.hotControl = 0;
|
|
|
|
|
|
_currEvent.Use();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-04-03 15:47:16 +08:00
|
|
|
|
|
2025-08-25 17:34:39 +08:00
|
|
|
|
#region 网格参考图导出功能
|
|
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
private void CaptureGridImage()
|
|
|
|
|
|
{
|
|
|
|
|
|
var tgs = TGS;
|
|
|
|
|
|
if (tgs == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("无法找到TerrainGridSystem组件");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取网格尺寸信息
|
|
|
|
|
|
int columnCount = tgs.columnCount;
|
|
|
|
|
|
int rowCount = tgs.rowCount;
|
|
|
|
|
|
|
|
|
|
|
|
if (columnCount <= 0 || rowCount <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("网格尺寸无效");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorUtility.DisplayProgressBar("导出网格参考图", "初始化...", 0.1f);
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 询问用户选择网格颜色
|
2025-08-25 17:34:39 +08:00
|
|
|
|
bool useHighContrast = EditorUtility.DisplayDialog("选择网格颜色",
|
|
|
|
|
|
"请选择导出的网格线颜色:",
|
2025-04-03 15:47:16 +08:00
|
|
|
|
"默认颜色", "高对比度(白底黑线)");
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 获取网格的实际世界空间边界
|
|
|
|
|
|
Bounds gridBounds = tgs.bounds;
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 计算网格的实际世界空间大小(单位:Unity单位)
|
|
|
|
|
|
float worldWidth = gridBounds.size.x;
|
|
|
|
|
|
float worldHeight = gridBounds.size.z; // 注意z轴是Unity中的垂直方向
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 计算图片像素尺寸 = 世界空间大小 * 100
|
|
|
|
|
|
int textureWidth = Mathf.CeilToInt(worldWidth * 100);
|
|
|
|
|
|
int textureHeight = Mathf.CeilToInt(worldHeight * 100);
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 显示计算结果并确认
|
|
|
|
|
|
if (!EditorUtility.DisplayDialog("确认导出",
|
|
|
|
|
|
$"网格真实尺寸: {worldWidth:F2}x{worldHeight:F2} 单位\n" +
|
|
|
|
|
|
$"导出图片分辨率: {textureWidth}x{textureHeight} 像素\n\n" +
|
|
|
|
|
|
"继续导出?",
|
|
|
|
|
|
"导出", "取消"))
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EditorUtility.DisplayProgressBar("导出网格参考图", "准备相机...", 0.2f);
|
|
|
|
|
|
|
|
|
|
|
|
// 创建临时相机
|
|
|
|
|
|
GameObject tempCameraObj = new GameObject("TempGridCamera");
|
|
|
|
|
|
Camera tempCamera = tempCameraObj.AddComponent<Camera>();
|
|
|
|
|
|
tempCamera.clearFlags = CameraClearFlags.SolidColor;
|
|
|
|
|
|
tempCamera.backgroundColor = useHighContrast ? Color.white : new Color(1, 1, 1, 0); // 高对比度时使用白底
|
|
|
|
|
|
tempCamera.orthographic = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 将相机放置在网格正上方
|
|
|
|
|
|
tempCamera.transform.position = new Vector3(
|
|
|
|
|
|
gridBounds.center.x,
|
|
|
|
|
|
gridBounds.max.y + 10, // 确保相机在网格上方足够高
|
|
|
|
|
|
gridBounds.center.z
|
|
|
|
|
|
);
|
|
|
|
|
|
tempCamera.transform.rotation = Quaternion.Euler(90, 0, 0); // 俯视
|
|
|
|
|
|
|
|
|
|
|
|
// 设置相机的视图大小,使其刚好包含整个网格
|
|
|
|
|
|
tempCamera.orthographicSize = worldHeight * 0.5f; // 正交相机尺寸是高度的一半
|
|
|
|
|
|
tempCamera.aspect = worldWidth / worldHeight;
|
|
|
|
|
|
|
|
|
|
|
|
EditorUtility.DisplayProgressBar("导出网格参考图", "创建渲染纹理...", 0.3f);
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 创建RenderTexture
|
2025-08-25 17:34:39 +08:00
|
|
|
|
RenderTexture renderTexture =
|
|
|
|
|
|
new RenderTexture(textureWidth, textureHeight, 24, RenderTextureFormat.ARGB32);
|
2025-04-03 15:47:16 +08:00
|
|
|
|
renderTexture.antiAliasing = 1;
|
|
|
|
|
|
renderTexture.filterMode = FilterMode.Point; // 避免模糊
|
|
|
|
|
|
renderTexture.Create();
|
|
|
|
|
|
|
|
|
|
|
|
// 保存原始状态
|
|
|
|
|
|
bool originalShowCells = tgs.showCells;
|
|
|
|
|
|
bool originalShowTerritories = tgs.showTerritories;
|
|
|
|
|
|
Color originalCellBorderColor = tgs.cellBorderColor;
|
|
|
|
|
|
float originalCellBorderThickness = tgs.cellBorderThickness;
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorUtility.DisplayProgressBar("导出网格参考图", "设置网格渲染参数...", 0.4f);
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 临时设置网格渲染参数
|
|
|
|
|
|
tgs.showCells = true;
|
|
|
|
|
|
tgs.showTerritories = false;
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 设置网格线颜色和宽度
|
|
|
|
|
|
if (useHighContrast)
|
|
|
|
|
|
{
|
|
|
|
|
|
tgs.cellBorderColor = Color.black;
|
|
|
|
|
|
tgs.cellBorderThickness = Mathf.Max(2.0f, tgs.cellBorderThickness); // 确保线条足够粗
|
|
|
|
|
|
}
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
EditorUtility.DisplayProgressBar("导出网格参考图", "渲染网格...", 0.6f);
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 设置并渲染
|
|
|
|
|
|
RenderTexture originalRenderTexture = RenderTexture.active;
|
|
|
|
|
|
tempCamera.targetTexture = renderTexture;
|
|
|
|
|
|
RenderTexture.active = renderTexture;
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 设置相机渲染遮罩,只渲染网格层
|
|
|
|
|
|
tempCamera.cullingMask = 1 << tgs.gameObject.layer;
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 渲染场景到RenderTexture
|
|
|
|
|
|
tempCamera.Render();
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
EditorUtility.DisplayProgressBar("导出网格参考图", "创建图片...", 0.7f);
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 创建纹理并读取像素
|
|
|
|
|
|
Texture2D texture2D = new Texture2D(textureWidth, textureHeight, TextureFormat.RGBA32, false);
|
|
|
|
|
|
texture2D.ReadPixels(new Rect(0, 0, textureWidth, textureHeight), 0, 0);
|
|
|
|
|
|
texture2D.Apply();
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
EditorUtility.DisplayProgressBar("导出网格参考图", "保存图片...", 0.8f);
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 保存为PNG
|
|
|
|
|
|
byte[] bytes = texture2D.EncodeToPNG();
|
|
|
|
|
|
string fileName = $"GridMap_{textureWidth}x{textureHeight}_{System.DateTime.Now:yyyyMMdd_HHmmss}.png";
|
|
|
|
|
|
string path = EditorUtility.SaveFilePanel("保存网格图片", Application.dataPath, fileName, "png");
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
if (!string.IsNullOrEmpty(path))
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
System.IO.File.WriteAllBytes(path, bytes);
|
|
|
|
|
|
EditorUtility.DisplayProgressBar("导出网格参考图", "刷新资源...", 0.9f);
|
|
|
|
|
|
AssetDatabase.Refresh();
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 如果路径在Assets文件夹内,获取相对路径并高亮显示导出的贴图
|
|
|
|
|
|
if (path.StartsWith(Application.dataPath))
|
|
|
|
|
|
{
|
2025-08-25 17:34:39 +08:00
|
|
|
|
var assetPath = "Assets" + path.Substring(Application.dataPath.Length);
|
2025-04-03 15:47:16 +08:00
|
|
|
|
Texture2D exportedTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
|
|
|
|
|
|
if (exportedTexture != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorGUIUtility.PingObject(exportedTexture);
|
|
|
|
|
|
Selection.activeObject = exportedTexture;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 显示成功信息和使用说明
|
2025-08-25 17:34:39 +08:00
|
|
|
|
EditorUtility.DisplayDialog("导出成功",
|
2025-04-03 15:47:16 +08:00
|
|
|
|
$"网格图片已导出\n" +
|
|
|
|
|
|
$"网格实际尺寸: {worldWidth:F2}x{worldHeight:F2} 单位\n" +
|
|
|
|
|
|
$"导出图片分辨率: {textureWidth}x{textureHeight} 像素\n" +
|
|
|
|
|
|
$"缩放比例: 100像素/单位\n" +
|
|
|
|
|
|
$"路径: {path}\n\n" +
|
|
|
|
|
|
"使用说明:\n" +
|
|
|
|
|
|
"1. 在美术软件中直接使用此图片作为参考层\n" +
|
|
|
|
|
|
"2. 绘制的美术资源与网格完全对齐即可\n" +
|
2025-08-25 17:34:39 +08:00
|
|
|
|
"3. 导入到Unity后无需调整Scale,将自动匹配网格",
|
2025-04-03 15:47:16 +08:00
|
|
|
|
"确定");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (System.Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorUtility.DisplayDialog("导出失败", $"保存图片时发生错误: {e.Message}", "确定");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 清理资源
|
|
|
|
|
|
RenderTexture.active = originalRenderTexture;
|
|
|
|
|
|
tempCamera.targetTexture = null;
|
|
|
|
|
|
Object.DestroyImmediate(texture2D);
|
|
|
|
|
|
Object.DestroyImmediate(renderTexture);
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
// 恢复原始状态
|
|
|
|
|
|
tgs.showCells = originalShowCells;
|
|
|
|
|
|
tgs.showTerritories = originalShowTerritories;
|
|
|
|
|
|
tgs.cellBorderColor = originalCellBorderColor;
|
|
|
|
|
|
tgs.cellBorderThickness = originalCellBorderThickness;
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
2025-04-03 15:47:16 +08:00
|
|
|
|
// 清理相机
|
|
|
|
|
|
Object.DestroyImmediate(tempCameraObj);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (System.Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogException(e);
|
|
|
|
|
|
EditorUtility.DisplayDialog("导出失败", $"导出过程中发生错误: {e.Message}", "确定");
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
// 无论如何都清除进度条
|
|
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-25 17:34:39 +08:00
|
|
|
|
|
|
|
|
|
|
#endregion
|
2023-12-15 18:32:21 +08:00
|
|
|
|
}
|