442 lines
14 KiB
C#
442 lines
14 KiB
C#
using cfg.LevelCfg;
|
||
using Framework;
|
||
using Gameplay;
|
||
using Gameplay.Level;
|
||
using Sirenix.OdinInspector;
|
||
using Sirenix.OdinInspector.Editor;
|
||
using Sirenix.Utilities;
|
||
using Sirenix.Utilities.Editor;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using TGS;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using UnityEngine.Serialization;
|
||
using Constants = Framework.Constants;
|
||
using ObjectFieldAlignment = Sirenix.OdinInspector.ObjectFieldAlignment;
|
||
|
||
//todo 序列化bug
|
||
public class LevelEditorWindow : OdinEditorWindow
|
||
{
|
||
private static readonly Color regionColor = MapUtils.GetRGBAColor("FF960072");
|
||
|
||
[Serializable]
|
||
public class RegionData
|
||
{
|
||
[LabelText("出战区")]
|
||
[OnValueChanged("OnRegionChanged")]
|
||
public int preparingRegionId;
|
||
|
||
[LabelText("出战区朝向")]
|
||
public LevelData.CharacterToward preparingRegionToward;
|
||
|
||
[LabelText("撤退区")]
|
||
public int fallbackRegionId;
|
||
|
||
[LabelText("敌方撤退区")]
|
||
public int enemyFallbackRegionId;
|
||
|
||
[LabelText("PVP防守区")]
|
||
public int pvpDefendRegionId;
|
||
|
||
[LabelText("PVP防守方朝向")]
|
||
public LevelData.CharacterToward pvpDefendToward;
|
||
|
||
private void OnRegionChanged()
|
||
{
|
||
var regionslist = currWindow._curMap.MapData.regions;
|
||
for (int i = 0; i < regionslist.Count; i++)
|
||
{
|
||
currWindow._curMap.SetRegionColor(i, false, regionColor);
|
||
}
|
||
|
||
currWindow._curMap.SetRegionColor(preparingRegionId, true, regionColor);
|
||
}
|
||
}
|
||
|
||
[Serializable]
|
||
public class NPCEditorInfo
|
||
{
|
||
[HideLabel]
|
||
public LevelData.NPCInfo npcInfo;
|
||
|
||
private bool _isEditing;
|
||
|
||
public NPCEditorInfo(LevelData.NPCInfo npcInfo)
|
||
{
|
||
this.npcInfo = npcInfo;
|
||
}
|
||
|
||
[Button("开始编辑巡逻信息")]
|
||
[HideIf("_isEditing")]
|
||
private void StartEditNPC()
|
||
{
|
||
LevelEditorWindow.currWindow.StartEditPatrolInfo(npcInfo);
|
||
_isEditing = true;
|
||
}
|
||
|
||
[Button("完成编辑巡逻信息")]
|
||
[ShowIf("_isEditing")]
|
||
private void FinishEditNPC()
|
||
{
|
||
LevelEditorWindow.currWindow.FinishEditPatrolInfo();
|
||
_isEditing = false;
|
||
}
|
||
}
|
||
|
||
// private static LevelPresentation currentLevelPresentation;
|
||
|
||
private MapManager _mapManager;
|
||
|
||
private Dictionary<Vector2Int, GameObject> _npcDic;
|
||
|
||
private Map _curMap;
|
||
|
||
public static LevelEditorWindow currWindow;
|
||
|
||
private LevelEditor _levelEditor;
|
||
|
||
private LevelData.NPCInfo _currEditingNPCInfo;
|
||
|
||
public bool IsEditingPatrolInfo => _currEditingNPCInfo != null;
|
||
|
||
private bool ResponseClickOnCell => isEditingNPC || IsEditingPatrolInfo;
|
||
|
||
// [LabelText("关卡名称"), Indent]
|
||
// public string levelName;
|
||
|
||
[LabelText("当前关卡数据"), Indent]
|
||
[OnValueChanged("OnCurrentLevelDataChanged")]
|
||
public LevelData currentLevelData = null;
|
||
|
||
[LabelText("当前地区数据"), Indent]
|
||
[OnValueChanged("OnCurrentLevelDataChanged")]
|
||
public RegionData curRegionData = null;
|
||
|
||
[LabelText("添加NPC")]
|
||
[LabelWidth(100)]
|
||
[SerializeField]
|
||
public bool isEditingNPC;
|
||
|
||
[PreviewField(150, ObjectFieldAlignment.Center)]
|
||
[AssetList(Path = (Constants.LEVEL_NPC_PATH_WITHOUT_ASSET), CustomFilterMethod = "IsNPCPrefabValid")]
|
||
[ShowIf("isEditingNPC")]
|
||
[LabelText("选择NPC")]
|
||
public GameObject npcObject;
|
||
|
||
private bool IsNPCPrefabValid(GameObject prefab)
|
||
{
|
||
var monsterClasses = EditorTableManager.instance.tables.MonsterClass;
|
||
var prefabPath = AssetDatabase.GetAssetPath(prefab);
|
||
foreach (var monsterClassCfg in monsterClasses.DataList)
|
||
{
|
||
if (monsterClassCfg.PrefabPath == prefabPath)
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public List<NPCEditorInfo> CurrentNpcInfo = null;
|
||
|
||
[Indent]
|
||
[Button("保存当前关卡")]
|
||
private void SaveCurrentLevel()
|
||
{
|
||
CheckCorrectLevel();
|
||
//var configFilePath = string.Format(Constants.LEVEL_CONFIG_FORMAT_PATH, currentLevelData.id);
|
||
currentLevelData.preparingRegionId = curRegionData.preparingRegionId;
|
||
currentLevelData.fallbackRegionId = curRegionData.fallbackRegionId;
|
||
currentLevelData.enemyFallbackRegionId = curRegionData.enemyFallbackRegionId;
|
||
currentLevelData.preparingRegionToward = curRegionData.preparingRegionToward;
|
||
currentLevelData.pvpDefendRegionId = curRegionData.pvpDefendRegionId;
|
||
currentLevelData.pvpDefendToward = curRegionData.pvpDefendToward;
|
||
|
||
//NPC列表
|
||
currentLevelData.npcInfos = CurrentNpcInfo.Select(editorNPCInfo => editorNPCInfo.npcInfo).ToList();
|
||
|
||
_levelEditor.SaveLevelData();
|
||
}
|
||
|
||
public static void OpenWindow(LevelEditor levelEditor)
|
||
{
|
||
MapEditorSettings.Init();
|
||
currWindow = GetWindow<LevelEditorWindow>();
|
||
currWindow.position = GUIHelper.GetEditorWindowRect().AlignCenter(800, 600);
|
||
currWindow.currentLevelData = null;
|
||
currWindow._levelEditor = levelEditor;
|
||
currWindow.LoadLevel(levelEditor.LevelData);
|
||
}
|
||
|
||
public const string preparingRegionErrorMessage = "关卡出战区设置错误,<{0}>地图没有:<{1}>号地区。\n";
|
||
public const string fallbackRegionErrorMessage = "关卡撤退区设置错误,<{0}>地图没有:<{1}>号地区。\n";
|
||
public const string enemyFallbackRegionErrorMessage = "关卡敌方撤退区设置错误,<{0}>地图没有:<{1}>号地区。\n";
|
||
public const string fixedMessage = "已将错误区域设置为<0>号地区。";
|
||
|
||
private void CheckCorrectLevel()
|
||
{
|
||
if (_curMap == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var regionList = _curMap.MapData.regions;
|
||
|
||
var errorMessage = string.Empty;
|
||
|
||
if (!_curMap.MapData.IsRegionExist(currentLevelData.preparingRegionId))
|
||
{
|
||
errorMessage += string.Format(preparingRegionErrorMessage, null,
|
||
currentLevelData.preparingRegionId);
|
||
currentLevelData.preparingRegionId = 0;
|
||
}
|
||
|
||
if (!_curMap.MapData.IsRegionExist(currentLevelData.fallbackRegionId))
|
||
{
|
||
errorMessage += string.Format(fallbackRegionErrorMessage, null,
|
||
currentLevelData.fallbackRegionId);
|
||
currentLevelData.fallbackRegionId = 0;
|
||
}
|
||
|
||
if (!_curMap.MapData.IsRegionExist(currentLevelData.enemyFallbackRegionId))
|
||
{
|
||
errorMessage += string.Format(enemyFallbackRegionErrorMessage, null,
|
||
currentLevelData.enemyFallbackRegionId);
|
||
currentLevelData.enemyFallbackRegionId = 0;
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(errorMessage))
|
||
{
|
||
CustomPopUpWindow.PopUp(errorMessage + fixedMessage);
|
||
}
|
||
}
|
||
|
||
private void Awake()
|
||
{
|
||
SceneView.duringSceneGui += OnSceneGUI;
|
||
}
|
||
|
||
protected override void OnEnable()
|
||
{
|
||
TerrainTypeConfig.Load();
|
||
_npcDic = new Dictionary<Vector2Int, GameObject>();
|
||
base.OnEnable();
|
||
}
|
||
|
||
protected override void OnDisable()
|
||
{
|
||
currWindow = null;
|
||
//MapEditorUtils.UnLoadMap(EditorMap.Instance);
|
||
//DestroyImmediate(GameObject.Find("Main Camera"));
|
||
DestroyNpcPrefab();
|
||
_npcDic = null;
|
||
base.OnDisable();
|
||
}
|
||
|
||
protected override void OnDestroy()
|
||
{
|
||
base.OnDestroy();
|
||
if (_curMap != null)
|
||
_curMap.TerrainGridSystem.OnMouseClickOnGrid -= OnCellClick;
|
||
SaveCurrentLevel();
|
||
SceneView.duringSceneGui -= OnSceneGUI;
|
||
}
|
||
|
||
private bool banSaveLevelList = false;
|
||
|
||
private void OnCurrentLevelDataChanged()
|
||
{
|
||
Debug.Log($"{GetType()}.OnCurrentLevelDataChanged");
|
||
}
|
||
|
||
private const string descKey = "LevelDesc";
|
||
private const string nameKey = "LevelName";
|
||
|
||
private void LoadLevel(LevelData levelData)
|
||
{
|
||
DestroyNpcPrefab();
|
||
_npcDic.Clear();
|
||
|
||
currentLevelData = levelData;
|
||
|
||
//加载地区信息
|
||
curRegionData = new RegionData();
|
||
curRegionData.preparingRegionId = currentLevelData.preparingRegionId;
|
||
curRegionData.fallbackRegionId = currentLevelData.fallbackRegionId;
|
||
curRegionData.enemyFallbackRegionId = currentLevelData.enemyFallbackRegionId;
|
||
curRegionData.preparingRegionToward = currentLevelData.preparingRegionToward;
|
||
curRegionData.pvpDefendRegionId = currentLevelData.pvpDefendRegionId;
|
||
curRegionData.pvpDefendToward = currentLevelData.pvpDefendToward;
|
||
|
||
|
||
_mapManager = FindObjectOfType<MapManager>();
|
||
_curMap = _mapManager.Map;
|
||
|
||
_curMap.TerrainGridSystem.OnMouseClickOnGrid += OnCellClick;
|
||
|
||
//加载NPC到场景中
|
||
CurrentNpcInfo = new();
|
||
foreach (var npcInfo in currentLevelData.npcInfos)
|
||
{
|
||
CurrentNpcInfo.Add(new(npcInfo));
|
||
|
||
string npcId = string.Format("P_E{0}", npcInfo.id.ToString().Substring(1));
|
||
var npc = AssetDatabase.LoadAssetAtPath<GameObject>(string.Format(Constants.LEVEL_NPC_PATH, npcId));
|
||
var position = _curMap.BlockPosition2WorldPosition(npcInfo.position);
|
||
_npcDic.Add(npcInfo.position, Instantiate(npc, position, npc.transform.rotation));
|
||
}
|
||
}
|
||
|
||
private void OnCellClick(TerrainGridSystem tgs, int cellindex, int buttonindex)
|
||
{
|
||
if (!ResponseClickOnCell) return;
|
||
|
||
|
||
var clickPosition = _curMap.TGSCellIndex2BlockPosition(cellindex);
|
||
|
||
int leftButton = 0;
|
||
int rightButton = 1;
|
||
|
||
if (buttonindex != leftButton && buttonindex != rightButton)
|
||
return;
|
||
bool opTrue = buttonindex == leftButton;
|
||
|
||
if (IsEditingPatrolInfo)
|
||
{
|
||
if (opTrue)
|
||
AddPatrolInfo(clickPosition);
|
||
else
|
||
RemovePatrolInfo(clickPosition);
|
||
}
|
||
else if (isEditingNPC)
|
||
{
|
||
if (opTrue)
|
||
{
|
||
AddNPC(clickPosition);
|
||
}
|
||
else
|
||
{
|
||
RemoveNPC(clickPosition);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void AddNPC(Vector2Int clickPosition)
|
||
{
|
||
if (npcObject == null)
|
||
{
|
||
DebugUtil.LogError("请选择正确的NPC!!");
|
||
return;
|
||
}
|
||
|
||
if (_npcDic.ContainsKey(clickPosition))
|
||
{
|
||
DebugUtil.LogError("该位置已有NPC存在,请重新选!!!");
|
||
return;
|
||
}
|
||
|
||
LevelData.NPCInfo npcInfo = new LevelData.NPCInfo();
|
||
string npcId = npcObject.name.Replace("P_E", "1");
|
||
|
||
npcInfo.id = int.Parse(npcId);
|
||
npcInfo.position = clickPosition;
|
||
|
||
//默认等级为1
|
||
npcInfo.level = 1;
|
||
CurrentNpcInfo.Add(new(npcInfo));
|
||
var position = _curMap.BlockPosition2WorldPosition(npcInfo.position);
|
||
_npcDic.Add(npcInfo.position, Instantiate(npcObject, position, npcObject.transform.rotation));
|
||
DebugUtil.Log("添加了[{0}]NPC到[ {1} ]位置上", npcObject.name, npcInfo.position);
|
||
}
|
||
|
||
private void RemoveNPC(Vector2Int clickPosition)
|
||
{
|
||
if (_npcDic.TryGetValue(clickPosition, out var npcObj))
|
||
{
|
||
var npc = CurrentNpcInfo.FirstOrDefault(npc => npc.npcInfo.position == clickPosition);
|
||
if (npc != null)
|
||
{
|
||
CurrentNpcInfo.Remove(npc);
|
||
DebugUtil.Log("从[ {0} ]位置上移除了[{1}]NPC", clickPosition, npcObj.name);
|
||
DestroyImmediate(npcObj);
|
||
_npcDic.Remove(clickPosition);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void AddPatrolInfo(Vector2Int clickPosition)
|
||
{
|
||
_currEditingNPCInfo.patrolInfos.Add(new LevelData.PatrolInfo()
|
||
{
|
||
patrolPoint = clickPosition,
|
||
});
|
||
}
|
||
|
||
private void RemovePatrolInfo(Vector2Int clickPosition)
|
||
{
|
||
var index = _currEditingNPCInfo.patrolInfos.FindLastIndex(pInfo => pInfo.patrolPoint == clickPosition);
|
||
if (index >= 0)
|
||
_currEditingNPCInfo.patrolInfos.RemoveAt(index);
|
||
}
|
||
|
||
public void StartEditPatrolInfo(LevelData.NPCInfo npcInfo)
|
||
{
|
||
_currEditingNPCInfo = npcInfo;
|
||
}
|
||
|
||
public void FinishEditPatrolInfo()
|
||
{
|
||
_currEditingNPCInfo = null;
|
||
}
|
||
|
||
private void DestroyNpcPrefab()
|
||
{
|
||
foreach (var npc in _npcDic)
|
||
{
|
||
DestroyImmediate(npc.Value);
|
||
}
|
||
}
|
||
|
||
protected override void OnGUI()
|
||
{
|
||
base.OnGUI();
|
||
if (_levelEditor == null || _mapManager == null || _mapManager.Map != _curMap ||
|
||
_levelEditor.LevelData != currentLevelData)
|
||
{
|
||
Debug.LogError("数据已变更,请重新打开关卡编辑器");
|
||
Close();
|
||
}
|
||
}
|
||
|
||
private void OnSceneGUI(SceneView sceneView)
|
||
{
|
||
DrawPatrolPath();
|
||
}
|
||
|
||
private void DrawPatrolPath()
|
||
{
|
||
if (!IsEditingPatrolInfo)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Vector3 lastWorldPos = Vector3.zero;
|
||
for (int i = 0; i < _currEditingNPCInfo.patrolInfos.Count; i++)
|
||
{
|
||
var gridPos = _currEditingNPCInfo.patrolInfos[i].patrolPoint;
|
||
var worldPos = _curMap.BlockPosition2WorldPosition(gridPos);
|
||
Handles.color = Color.blue;
|
||
Handles.DrawSolidDisc(worldPos, Vector3.up, 0.4f);
|
||
if (i > 0)
|
||
{
|
||
Handles.color = Color.yellow;
|
||
EditorUtil.DrawArrowInScene(lastWorldPos, worldPos, 0.5f);
|
||
}
|
||
|
||
//Handles.color = Color.black;
|
||
Handles.Label(worldPos, i.ToString());
|
||
lastWorldPos = worldPos;
|
||
}
|
||
}
|
||
} |