NLDClient-yudde/ProjectNLD/Assets/Editor/LevelEditor/LevelEditorWindow.MainLife.cs

1581 lines
55 KiB
C#
Raw Normal View History

2025-12-10 17:39:34 +08:00
using TGS;
using UnityEditor;
using UnityEngine;
using System.Linq;
using Gameplay.Level;
using cfg;
using cfg.LevelCfg;
using Gameplay;
using Gameplay.Character;
using Gameplay.Utils;
using System.Collections.Generic;
2025-12-22 15:33:08 +08:00
using System.IO;
2025-12-10 17:39:34 +08:00
using static Gameplay.Level.LevelData;
public partial class LevelEditorWindow
{
#region MainLife
private void Awake()
{
SceneView.duringSceneGui += OnSceneGUI;
}
protected override async void OnEnable()
{
await TerrainTypeConfig.Load();
_enemyNpcObjectWaveDic = new Dictionary<int, Dictionary<NpcEditorInfo, GameObject>>();
_midNpcObjectWaveDic = new Dictionary<int, Dictionary<NpcEditorInfo, GameObject>>();
_selfNpcObjectDic = new Dictionary<Vector2Int, GameObject>();
_showNpcInfos = new List<NpcEditorInfo>();
_blockPrefabDic = new Dictionary<int, GameObject>();
base.OnEnable();
}
protected override void OnDisable()
{
OnSetRetreatRegionColor();
OnCloseCaptureRegionColor();
2025-12-10 19:41:23 +08:00
ClearShowRegionColor();
2025-12-10 17:39:34 +08:00
StopMonitoringSphere();
ShowHiddenObjects();
SaveBlockSphereInfo();
DestroyBlockSphere();
currWindow = null;
DestroyNpcPrefab();
_enemyNpcObjectWaveDic = null;
_midNpcObjectWaveDic = null;
_selfNpcObjectDic = null;
_blockPrefabDic = null;
base.OnDisable();
}
protected override void OnDestroy()
{
OnSetRetreatRegionColor();
OnCloseCaptureRegionColor();
2025-12-10 19:41:23 +08:00
ClearShowRegionColor();
2025-12-10 17:39:34 +08:00
StopMonitoringSphere();
ShowHiddenObjects();
SaveBlockSphereInfo();
DestroyBlockSphere();
DestroyAllBlockObj();
ClearMinesRegionColor();
ClearBlockRegionColor();
ClearDefenseRegionColor();
base.OnDestroy();
if (_curMap != null)
_curMap.TerrainGridSystem.OnMouseClickOnGrid -= OnCellClick;
SaveCurrentLevel();
SceneView.duringSceneGui -= OnSceneGUI;
}
protected override void OnGUI()
{
base.OnGUI();
if (_levelEditor == null || _mapManager == null || _mapManager.Map != _curMap ||
_levelEditor.LevelData != currentLevelData)
{
Debug.LogError("数据已变更,请重新打开关卡编辑器");
Close();
}
}
#endregion
#region 点击添加事件
private bool ResponseClickOnCell =>
isEditingEnemyNpc || isEditingSelfNpc || isEditingMidNpc || isEditingNpcPos || isEditingFlagStart ||
isEditingFlagEnd || isEditingCaptureInfo || isEditingFallbackReusable ||
isEditingRetreat || isEditingRoadInfo ||
isEditingAreaInfo || canEditorBlockArea || canEditorDefenseArea || canEditorMinesArea;
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 (isEditingFlagStart)
{
EditingFlagStart(clickPosition, true, opTrue);
}
else if (isEditingFlagEnd)
{
EditingFlagStart(clickPosition, false, opTrue);
}
else if (isEditingRetreat)
{
EditingRetreat(cellIndex, opTrue);
}
else if (isEditingFallbackReusable)
{
EditingFallBack(clickPosition, opTrue);
}
else if (isEditingCaptureInfo)
{
EditingCapture(cellIndex, opTrue);
}
else if (isEditingNpcPos)
{
if (opTrue)
ChangeNpcPos(clickPosition, npcTroopId);
}
else if (isEditingEnemyNpc)
{
EditEnemyNpc(clickPosition, opTrue);
}
else if (isEditingSelfNpc)
{
EditSelfNpc(clickPosition, opTrue);
}
else if (isEditingMidNpc)
{
EditMidNpc(clickPosition, opTrue);
}
else if (isEditingRoadInfo && isEditingRoad)
{
EditRoad(cellIndex, opTrue);
}
else if (isEditingAreaInfo && isEditingArea)
{
EditArea(cellIndex, opTrue);
}
else if (_isEditorSingleMinesRegion && _currEditMinesRegionArea != null)
{
EditingMinesRegion(cellIndex, opTrue);
}
else if (_isEditorSingleBlockRegion && _currEditBlockRegion != null)
{
EditingBlockRegion(cellIndex, opTrue);
}
else if (_isEditorSingleDefenseRegion && _currEditDefenseRegion != null)
{
EditingDefenseRegion(cellIndex, opTrue);
}
}
private void EditingMinesRegion(int clickPosition, bool isAdd)
{
if (isAdd)
_currEditMinesRegionArea.AddPosition(clickPosition);
else
_currEditMinesRegionArea.RemovePosition(clickPosition);
}
private void EditingBlockRegion(int clickPosition, bool isAdd)
{
if (isAdd)
_currEditBlockRegion.AddPosition(clickPosition);
else
_currEditBlockRegion.RemovePosition(clickPosition);
}
private void EditingDefenseRegion(int clickPosition, bool isAdd)
{
if (isAdd)
_currEditDefenseRegion.AddPosition(clickPosition);
else
_currEditDefenseRegion.RemovePosition(clickPosition);
}
private void EditingFallBack(Vector2Int clickPosition, bool isAdd)
{
if (isAdd && !enemyRetreatEditorInfo.fallbackReusablePoints.Contains(clickPosition))
{
DebugUtil.Log("复用撤离点添加: {0}", clickPosition);
enemyRetreatEditorInfo.fallbackReusablePoints.Add(clickPosition);
}
else if (!isAdd && enemyRetreatEditorInfo.fallbackReusablePoints.Contains(clickPosition))
{
DebugUtil.Log("复用撤离点移除: {0}", clickPosition);
enemyRetreatEditorInfo.fallbackReusablePoints.Remove(clickPosition);
}
}
private void EditingRetreat(int clickPosition, bool isAdd)
{
if (isAdd && retreatEditorInfo.All(x => x.retreatPoint != clickPosition))
{
retreatEditorInfo.Add(new RetreatEditorInfo
{
retreatPoint = clickPosition,
retreatFlag = false,
});
_curMap.TerrainGridSystem.CellToggleRegionSurface(clickPosition, true, RetreatRegionColor);
DebugUtil.Log("撤离区添加: {0}", clickPosition);
}
else if (!isAdd && retreatEditorInfo.Any(x => x.retreatPoint == clickPosition))
{
retreatEditorInfo =
retreatEditorInfo.Where(x => x.retreatPoint != clickPosition).ToList();
_curMap.TerrainGridSystem.CellHideRegionSurface(clickPosition);
DebugUtil.Log("撤离区移除: {0}", clickPosition);
}
}
private void EditingFlagStart(Vector2Int clickPosition, bool isEditingStart, bool isAdd)
{
if (isEditingStart)
{
if (isAdd && !flagEditorInfo.flagStartPoints.Contains(clickPosition))
{
DebugUtil.Log("扛旗起点添加: {0}", clickPosition);
flagEditorInfo.flagStartPoints.Add(clickPosition);
}
else if (!isAdd && flagEditorInfo.flagStartPoints.Contains(clickPosition))
{
DebugUtil.Log("扛旗起点移除: {0}", clickPosition);
flagEditorInfo.flagStartPoints.Remove(clickPosition);
}
}
else
{
if (isAdd && !flagEditorInfo.flagEndPoints.Contains(clickPosition))
{
DebugUtil.Log("扛旗终点添加: {0}", clickPosition);
flagEditorInfo.flagEndPoints.Add(clickPosition);
}
else if (!isAdd && flagEditorInfo.flagEndPoints.Contains(clickPosition))
{
DebugUtil.Log("扛旗终点移除: {0}", clickPosition);
flagEditorInfo.flagEndPoints.Remove(clickPosition);
}
}
}
private void EditingCapture(int clickPosition, bool isAdd)
{
if (_currEditingCaptureInfo == null) return;
if (isAdd && _currEditingCaptureInfo.capturePointEditorInfos.All(x => x.capturePoint != clickPosition))
{
_currEditingCaptureInfo.capturePointEditorInfos.Add(new CaptureEditorInfo.CapturePointEditorInfo
{
belongRegionID = _currEditingCaptureInfo.captureRegionID,
capturePoint = clickPosition,
captureFlag = false,
});
_curMap.TerrainGridSystem.CellToggleRegionSurface(clickPosition, true, CaptureRegionColor);
DebugUtil.Log($"占领区{_currEditingCaptureInfo.captureRegionID} 添加: {clickPosition}");
}
else if (!isAdd && _currEditingCaptureInfo.capturePointEditorInfos.Any(x => x.capturePoint == clickPosition))
{
_currEditingCaptureInfo.capturePointEditorInfos =
_currEditingCaptureInfo.capturePointEditorInfos.Where(x => x.capturePoint != clickPosition).ToList();
_curMap.TerrainGridSystem.CellHideRegionSurface(clickPosition);
DebugUtil.Log($"占领区{_currEditingCaptureInfo.captureRegionID} 移除: {clickPosition}");
}
}
private void ChangeNpcPos(Vector2Int clickPosition, int npcTroopID)
{
if (null == _currNpcEditorInfo)
return;
var oldPos = _currEditingNpcInfo.position;
if (oldPos == clickPosition)
return;
var newWorldPos = _curMap.BlockPosition2WorldPosition(clickPosition);
if (ETroopsId.Enemy == npcTroopID &&
_enemyNpcObjectWaveDic.TryGetValue(_currEditingNpcInfo.waveIndex, out var enemyNpcObjectDic))
{
if (enemyNpcObjectDic.TryGetValue(_currNpcEditorInfo, out var npcObj))
{
npcObj.transform.position = newWorldPos;
_currEditingNpcInfo.position = clickPosition;
DebugUtil.Log("怪物: [ {0} ] 移动到 [ {1} ]位置上", _currEditingNpcInfo.id, clickPosition);
}
}
else if (ETroopsId.Neutral == npcTroopID &&
_midNpcObjectWaveDic.TryGetValue(_currEditingNpcInfo.waveIndex, out var midNpcObjectDic))
{
if (midNpcObjectDic.TryGetValue(_currNpcEditorInfo, out var npcObj))
{
npcObj.transform.position = newWorldPos;
_currEditingNpcInfo.position = clickPosition;
DebugUtil.Log("怪物: [ {0} ] 移动到 [ {1} ]位置上", _currEditingNpcInfo.id, clickPosition);
}
}
else if (ETroopsId.Self == npcTroopID && _selfNpcObjectDic.TryGetValue(oldPos, out var selfNpcObj))
{
selfNpcObj.transform.position = newWorldPos;
_currEditingNpcInfo.position = clickPosition;
_selfNpcObjectDic.Remove(oldPos);
_selfNpcObjectDic.Add(clickPosition, selfNpcObj);
DebugUtil.Log("怪物: [ {0} ] 移动到 [ {1} ]位置上", _currEditingNpcInfo.id, clickPosition);
}
else
{
DebugUtil.LogError("需要更改的旧位置上没有怪物:{0}", oldPos);
}
}
#region 敌方NPC编辑方法
private void EditEnemyNpc(Vector2Int clickPosition, bool isAdd)
{
if (isAdd)
{
AddEnemyNpc(clickPosition);
}
else
{
2025-12-22 15:33:08 +08:00
if (Event.current != null && (Event.current.control || Event.current.command))
{
RemoveEnemyNpc(clickPosition);
}
else
{
Debug.LogWarning("删除NPC需要按住Ctrl键");
}
2025-12-10 17:39:34 +08:00
}
}
private void AddEnemyNpc(Vector2Int clickPosition)
{
if (enemyNpcObject == null || curEnemyMonsterID == 0)
{
DebugUtil.LogError("请选择正确的NPC");
return;
}
_enemyNpcObjectWaveDic ??= new Dictionary<int, Dictionary<NpcEditorInfo, GameObject>>();
NPCInfo npcInfo = new()
{
id = curEnemyMonsterID,
position = clickPosition,
index = currentEnemyNpcInfo.Count + 1,
troopId = ETroopsId.Enemy,
};
var level = 0;
var aiType = 0;
2025-12-22 15:33:08 +08:00
var monsterName = "";
2025-12-10 17:39:34 +08:00
var monsterConfigDic = MonsterCfg.DataMap;
if (monsterConfigDic.TryGetValue(curEnemyMonsterID, out var monster))
{
level = monster.MonsterLevel;
aiType = monster.AIType;
2025-12-22 15:33:08 +08:00
monsterName = monster.NameRead;
2025-12-10 17:39:34 +08:00
}
else
{
DebugUtil.LogError("获取怪物[{0}]配置数据错误", curEnemyMonsterID);
}
2025-12-22 15:33:08 +08:00
var npcEditor = new NpcEditorInfo(npcInfo, currentEnemyNpcInfo.Count + 1, level, aiType, ETroopsId.Enemy,
monsterName);
2025-12-10 17:39:34 +08:00
currentEnemyNpcInfo.Add(npcEditor);
var pos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
if (_enemyNpcObjectWaveDic.TryGetValue(npcInfo.waveIndex, out var npcObjectDic))
{
var obj = Instantiate(enemyNpcObject, pos, enemyNpcObject.transform.rotation);
2025-12-10 18:54:43 +08:00
if (enemyObjRoot != null)
obj.transform.SetParent(enemyObjRoot.transform);
2025-12-10 17:39:34 +08:00
npcObjectDic.Add(npcEditor, obj);
}
else
{
var obj = Instantiate(enemyNpcObject, pos, enemyNpcObject.transform.rotation);
2025-12-10 18:54:43 +08:00
if (enemyObjRoot != null)
obj.transform.SetParent(enemyObjRoot.transform);
2025-12-10 17:39:34 +08:00
npcObjectDic = new Dictionary<NpcEditorInfo, GameObject>
{ { npcEditor, obj } };
_enemyNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
}
DebugUtil.Log(
$"添加了第[ {npcInfo.waveIndex} ]波的[ {npcInfo.index} ]怪物到 [ {npcInfo.position} ]位置上, 其预制体为: {enemyNpcObject.name}");
2025-12-10 18:54:43 +08:00
RefreshEnemyNpcGroups();
2025-12-10 17:39:34 +08:00
}
private void RemoveEnemyNpc(Vector2Int clickPosition)
{
var npc = currentEnemyNpcInfo.LastOrDefault(npc => npc.npcInfo.position == clickPosition);
if (npc != null)
{
if (_enemyNpcObjectWaveDic.TryGetValue(npc.npcInfo.waveIndex, out var npcObjectDic))
{
if (npcObjectDic.TryGetValue(npc, out var obj))
{
DebugUtil.Log(
$"从[ {clickPosition} ]位置上移除了[ {npc.npcInfo.waveIndex} ]波的[{npc.id}]NPC, 其预制体名字为:{obj.name}");
DestroyImmediate(obj);
npcObjectDic.Remove(npc);
}
else
{
DebugUtil.LogError($"波次{npc.npcInfo.waveIndex}中没有怪物{npc.id}");
}
}
else
{
DebugUtil.LogError("没有波次{0}的怪物信息", npc.npcInfo.waveIndex);
}
currentEnemyNpcInfo.Remove(npc);
}
ReorderEnemyNpcID();
2025-12-10 18:54:43 +08:00
RefreshEnemyNpcGroups();
2025-12-10 17:39:34 +08:00
}
#endregion
2025-12-22 15:33:08 +08:00
#region 已方NPC编辑方法
2025-12-10 17:39:34 +08:00
private void EditSelfNpc(Vector2Int clickPosition, bool isAdd)
{
if (isAdd)
{
AddSelfNpc(clickPosition);
}
else
{
2025-12-22 15:33:08 +08:00
if (Event.current != null && (Event.current.control || Event.current.command))
{
RemoveSelfNpc(clickPosition);
}
else
{
Debug.LogWarning("删除NPC需要按住Ctrl键");
}
2025-12-10 17:39:34 +08:00
}
}
private void AddSelfNpc(Vector2Int clickPosition)
{
if (selfNpcObject == null || curSelfNpcID == 0)
{
DebugUtil.LogError("请选择正确的己方NPC");
return;
}
_selfNpcObjectDic ??= new Dictionary<Vector2Int, GameObject>();
if (_selfNpcObjectDic.ContainsKey(clickPosition))
{
DebugUtil.LogError("该位置已有NPC存在请重新选");
return;
}
NPCInfo npcInfo = new()
{
id = curSelfNpcID,
position = clickPosition,
troopId = ETroopsId.Self,
};
var level = 0;
var aiType = 0;
2025-12-22 15:33:08 +08:00
var monsterName = "";
2025-12-10 17:39:34 +08:00
var monsterConfigDic = MonsterCfg.DataMap;
if (monsterConfigDic.TryGetValue(curSelfNpcID, out var monster))
{
level = monster.MonsterLevel;
aiType = monster.AIType;
2025-12-22 15:33:08 +08:00
monsterName = monster.NameRead;
2025-12-10 17:39:34 +08:00
}
else
{
DebugUtil.LogError("获取己方Npc[{0}]配置数据错误", curSelfNpcID);
}
currentSelfNpcInfo.Add(new NpcEditorInfo(npcInfo, SelfID + currentSelfNpcInfo.Count + 1, level, aiType,
2025-12-22 15:33:08 +08:00
ETroopsId.Self, monsterName));
2025-12-10 17:39:34 +08:00
var pos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
2025-12-10 18:54:43 +08:00
var obj = Instantiate(selfNpcObject, pos, selfNpcObject.transform.rotation);
if (selfObjRoot != null)
{
obj.transform.SetParent(selfObjRoot.transform);
}
_selfNpcObjectDic.Add(npcInfo.position, obj);
2025-12-10 17:39:34 +08:00
DebugUtil.Log("添加了己方Npc: [ {0} ] 到 [ {1} ]位置上, 其预制体为: {2}", npcInfo.id, npcInfo.position, selfNpcObject.name);
}
private void RemoveSelfNpc(Vector2Int clickPosition)
{
if (_selfNpcObjectDic.TryGetValue(clickPosition, out var npcObj))
{
var npc = currentSelfNpcInfo.FirstOrDefault(npcFight => npcFight.npcInfo.position == clickPosition);
if (npc != null)
{
currentSelfNpcInfo.Remove(npc);
DebugUtil.Log("从[ {0} ]位置上移除了[{1}] 己方NPC", clickPosition, npcObj.name);
DestroyImmediate(npcObj);
_selfNpcObjectDic.Remove(clickPosition);
}
}
ReorderSelfNpcID();
}
#endregion
#region 中立NPC编辑方法
private void EditMidNpc(Vector2Int clickPosition, bool isAdd)
{
if (isAdd)
AddMidNpc(clickPosition);
else
2025-12-22 15:33:08 +08:00
{
if (Event.current != null && (Event.current.control || Event.current.command))
{
RemoveMidNpc(clickPosition);
}
else
{
Debug.LogWarning("删除NPC需要按住Ctrl键");
}
}
2025-12-10 17:39:34 +08:00
}
private void AddMidNpc(Vector2Int clickPosition)
{
if (midNpcObject == null || curMidMonsterID == 0)
{
DebugUtil.LogError("请选择正确的NPC");
return;
}
_midNpcObjectWaveDic ??= new Dictionary<int, Dictionary<NpcEditorInfo, GameObject>>();
NPCInfo npcInfo = new()
{
id = curMidMonsterID,
position = clickPosition,
index = currentMidNpcInfo.Count + 1,
troopId = ETroopsId.Neutral,
isOpenInvestigate = false,
};
var level = 0;
var aiType = 0;
2025-12-22 15:33:08 +08:00
var monsterName = "";
2025-12-10 17:39:34 +08:00
var monsterConfigDic = MonsterCfg.DataMap;
if (monsterConfigDic.TryGetValue(curMidMonsterID, out var monster))
{
level = monster.MonsterLevel;
aiType = monster.AIType;
2025-12-22 15:33:08 +08:00
monsterName = monster.NameRead;
2025-12-10 17:39:34 +08:00
}
else
{
DebugUtil.LogError("获取怪物[{0}]配置数据错误", curMidMonsterID);
}
2025-12-29 14:41:29 +08:00
var npcEditor = new NpcEditorInfo(npcInfo, MidID + currentMidNpcInfo.Count + 1, level, aiType, ETroopsId.Neutral,
2025-12-22 15:33:08 +08:00
monsterName);
2025-12-10 17:39:34 +08:00
currentMidNpcInfo.Add(npcEditor);
var pos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
if (_midNpcObjectWaveDic.TryGetValue(npcInfo.waveIndex, out var npcObjectDic))
{
var obj = Instantiate(midNpcObject, pos, midNpcObject.transform.rotation);
2025-12-10 18:54:43 +08:00
if (midObjRoot != null)
obj.transform.SetParent(midObjRoot.transform);
2025-12-10 17:39:34 +08:00
npcObjectDic.Add(npcEditor, obj);
}
else
{
var obj = Instantiate(midNpcObject, pos, midNpcObject.transform.rotation);
2025-12-10 18:54:43 +08:00
if (midObjRoot != null)
obj.transform.SetParent(midObjRoot.transform);
2025-12-10 17:39:34 +08:00
npcObjectDic = new Dictionary<NpcEditorInfo, GameObject>
{ { npcEditor, obj } };
_midNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
}
DebugUtil.Log(
2025-12-10 18:54:43 +08:00
$"添加了第[ {npcInfo.waveIndex} ]波的[ {npcInfo.index} ] 中立怪物到 [ {npcInfo.position} ]位置上, 其预制体为: {midNpcObject.name}");
2025-12-10 17:39:34 +08:00
}
private void RemoveMidNpc(Vector2Int clickPosition)
{
var npc = currentMidNpcInfo.LastOrDefault(npc => npc.npcInfo.position == clickPosition);
if (npc != null)
{
if (_midNpcObjectWaveDic.TryGetValue(npc.npcInfo.waveIndex, out var npcObjectDic))
{
if (npcObjectDic.TryGetValue(npc, out var obj))
{
DebugUtil.Log(
2025-12-10 18:54:43 +08:00
$"从[ {clickPosition} ]位置上移除了[ {npc.npcInfo.waveIndex} ]波的[{npc.id}] 中立NPC, 其预制体名字为:{obj.name}");
2025-12-10 17:39:34 +08:00
DestroyImmediate(obj);
npcObjectDic.Remove(npc);
}
else
{
DebugUtil.LogError($"波次{npc.npcInfo.waveIndex}中没有怪物{npc.id}");
}
}
else
{
DebugUtil.LogError("没有波次{0}的怪物信息", npc.npcInfo.waveIndex);
}
currentMidNpcInfo.Remove(npc);
}
ReorderMidNpcID();
}
#endregion
/// <summary>
/// 编辑路线
/// </summary>
/// <param name="clickPosition"></param>
/// <param name="isAdd"></param>
private void EditRoad(int clickPosition, bool isAdd)
{
if (_currEditingRoadInfo == null) return;
if (isAdd && !_currEditingRoadInfo.roadPoints.Any(x => x.cellIndex == clickPosition))
{
_currEditingRoadInfo.roadPoints.Add(new PatrolInfo()
{
cellIndex = clickPosition,
waitTime = 0f,
});
}
else if (!isAdd && _currEditingRoadInfo.roadPoints.Any(x => x.cellIndex == clickPosition))
{
_currEditingRoadInfo.roadPoints =
_currEditingRoadInfo.roadPoints.Where(x => x.cellIndex != clickPosition).ToList();
}
}
/// <summary>
/// 编辑区域
/// </summary>
/// <param name="clickPosition"></param>
/// <param name="isAdd"></param>
private void EditArea(int clickPosition, bool isAdd)
{
if (null == _currEditingAreaInfo)
return;
if (isAdd && !_currEditingAreaInfo.areaPoints.Any(x => x.cellIndex == clickPosition))
{
_currEditingAreaInfo.areaPoints.Add(new PatrolInfo()
{
cellIndex = clickPosition,
waitTime = 0f,
});
}
else if (!isAdd && _currEditingAreaInfo.areaPoints.Any(x => x.cellIndex == clickPosition))
{
_currEditingAreaInfo.areaPoints =
_currEditingAreaInfo.areaPoints.Where(x => x.cellIndex != clickPosition).ToList();
}
}
#endregion
#region 加载
private void LoadLevel(LevelData levelData)
{
DestroyNpcPrefab();
_enemyNpcObjectWaveDic.Clear();
_midNpcObjectWaveDic.Clear();
_showNpcInfos.Clear();
_selfNpcObjectDic.Clear();
currentLevelData = levelData;
_mapManager = FindObjectOfType<MapManager>();
_curMap = _mapManager.Map;
_curMap.TerrainGridSystem.OnMouseClickOnGrid += OnCellClick;
// 加载关卡任务
taskInfos = currentLevelData.taskInfos.Select(t => new TaskInfoEditor(t)).ToList();
// 加载胜利失败星级条件
/*successTaskID = currentLevelData.successTaskID;
2025-12-10 17:39:34 +08:00
failTaskID = currentLevelData.failTaskID;
starTaskIDs[0] = currentLevelData.starTaskIDs[0];
starTaskIDs[1] = currentLevelData.starTaskIDs[1];
starTaskIDs[2] = currentLevelData.starTaskIDs[2];
successTask = GetTaskName(currentLevelData.successTaskID);
failTask = GetTaskName(currentLevelData.failTaskID);
oneStarTask = GetTaskName(currentLevelData.starTaskIDs[0]);
twoStarTask = GetTaskName(currentLevelData.starTaskIDs[1]);
threeStarTask = GetTaskName(currentLevelData.starTaskIDs[2]);*/
2025-12-23 16:58:55 +08:00
2025-12-10 17:39:34 +08:00
// 加载评分条件
scoreTasks = currentLevelData.scoreTaskID.Select(s => new ScoreTaskEditor(s)).ToList();
// 加载触发器
levelTriggerData = currentLevelData.levelTriggerDatas.Select(e => new LevelTriggerDataEditor(e)).ToList();
//加载地区信息
curRegionData = new RegionData
{
preparingRegionId = currentLevelData.preparingRegionId,
preparingRegionToward = currentLevelData.preparingRegionToward,
pvpDefendRegionId = currentLevelData.pvpDefendRegionId,
pvpDefendToward = currentLevelData.pvpDefendToward
};
// 加载占领数据
captureEditorInfos.Clear();
foreach (var captureInfo in currentLevelData.CapturePoints)
{
var captureEditor = new CaptureEditorInfo()
{
captureRegionID = captureInfo.captureRegionID,
captureProgress = captureInfo.CaptureProgress,
captureEffectID = captureInfo.captureEffectID,
captureEffectPoint = captureInfo.captureEffectPoint,
};
foreach (var point in captureInfo.capturePoints)
{
var capturePoint = new CaptureEditorInfo.CapturePointEditorInfo()
{
belongRegionID = captureInfo.captureRegionID,
capturePoint = point,
captureFlag = point == captureInfo.captureFlag,
};
captureEditor.capturePointEditorInfos.Add(capturePoint);
}
captureEditorInfos.Add(captureEditor);
}
// 加载扛旗数据
flagEditorInfo = new FlagEditorInfo();
foreach (var flagStartPoint in currentLevelData.flagStartPoints)
{
var pos = _curMap.TGSCellIndex2BlockPosition(flagStartPoint);
flagEditorInfo.flagStartPoints.Add(pos);
}
foreach (var flagEndPoint in currentLevelData.flagEndPoints)
{
var pos = _curMap.TGSCellIndex2BlockPosition(flagEndPoint);
flagEditorInfo.flagEndPoints.Add(pos);
}
DestroyAllBlockObj();
UpdateMinesRegion();
UpdateBlockRegion();
UpdateDefenseRegion();
// 加载撤离数据
retreatEditorInfo.Clear();
foreach (var point in currentLevelData.retreatPoints.retreatPoints)
{
var retreatInfo = new RetreatEditorInfo()
{
retreatPoint = point,
retreatFlag = point == currentLevelData.retreatPoints.retreatFlag,
};
retreatEditorInfo.Add(retreatInfo);
}
// 加载敌人撤离数据
enemyRetreatEditorInfo = new EnemyRetreatEditorInfo();
foreach (var fallback in currentLevelData.enemyRetreatReusablePoints)
{
var pos = _curMap.TGSCellIndex2BlockPosition(fallback);
enemyRetreatEditorInfo.fallbackReusablePoints.Add(pos);
}
// 加载指定路线
roadEditorInfos = currentLevelData.patrolRoadData.Select(roadInfo => new PrescribedRoadEditorInfo
{
roadID = roadInfo.id,
roadPoints = roadInfo.patrolInfos,
}).ToList();
// 加载指定区域
areaEditorInfos = currentLevelData.patrolRegionData.Select(areaInfo => new PrescribedAreaEditorInfo
{
areaID = areaInfo.id,
areaPoints = areaInfo.patrolInfos,
}).ToList();
// 加载关卡进度数据
progressGuideDatas = currentLevelData.progressGuideDatas.Select(p => new ProgressGuideDataEditor(p)).ToList();
// 加载关卡引导立标数据
setFlagEditorInfos = currentLevelData.setFlagGuideDatas.Select(f => new SetFlagGuideDataEditor(f)).ToList();
// 加载信息板数据
infoPanelGuideDatas =
currentLevelData.infoPanelGuideDatas.Select(i => new InfoPanelGuideDataEditor(i)).ToList();
// 加载关卡广播
listAIBroadcastData = currentLevelData.listAIBroadcastData.Select(a => new AIBroadcastDataEditor(a)).ToList();
2025-12-22 15:33:08 +08:00
// 加载当前关卡的Monster配置
LoadAllNpcConfig();
2025-12-10 17:39:34 +08:00
// 加载敌方NPC到场景中
LoadEnemyNpcInfo();
// 加载中立NPC到场景中
LoadMidNpcInfo();
// 加载己方NPC到场景中
LoadSelfNpcInfo();
2025-12-29 19:07:03 +08:00
// 加载波次信息
LoadWaveInfo();
2025-12-10 17:39:34 +08:00
// 加载阻挡球
LoadBlockSphereInfo();
}
2025-12-22 15:33:08 +08:00
private void LoadAllNpcConfig()
{
var monsterConfigDic = MonsterCfg.DataList;
var levelName = Path.GetFileNameWithoutExtension(_levelEditor.LevelDataJson.name);
_allMonsterIDs.Clear();
foreach (var dataMonster in monsterConfigDic)
{
if (!dataMonster.LevelJsonName.EndsWith(levelName))
continue;
var monsterName = $"{dataMonster.MonsterID} - {dataMonster.NameRead}";
_allMonsterIDs.Add(monsterName, dataMonster.MonsterID);
}
}
2025-12-10 17:39:34 +08:00
private void LoadEnemyNpcInfo()
{
var monsterConfigDic = MonsterCfg.DataMap;
currentEnemyNpcInfo = new();
var index = 1;
foreach (var npcInfo in currentLevelData.npcInfos)
{
if (!monsterConfigDic.TryGetValue(npcInfo.id, out var monsterClassData))
{
DebugUtil.LogError("请检查配置,没有 {0} 的怪物类ID", npcInfo.id);
continue;
}
var npcEditor = new NpcEditorInfo(npcInfo, index, monsterClassData.MonsterLevel, monsterClassData.AIType,
2025-12-22 15:33:08 +08:00
ETroopsId.Enemy, monsterClassData.NameRead);
2025-12-10 17:39:34 +08:00
currentEnemyNpcInfo.Add(npcEditor);
var npcObj = LoadEnemyNpcPrefab(npcInfo.id);
if (npcObj != null)
{
var setPos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
var yaw = LevelUtils.SetCharaterToward(npcInfo.npcToward);
var obj = Instantiate(npcObj, setPos, Quaternion.Euler(0, yaw, 0));
if (enemyObjRoot == null)
enemyObjRoot = new GameObject("enemyObjRoot");
if (obj != null)
obj.transform.SetParent(enemyObjRoot.transform);
if (_enemyNpcObjectWaveDic.TryGetValue(npcInfo.waveIndex, out var npcObjectDic))
{
npcObjectDic.Add(npcEditor, obj);
}
else
{
npcObjectDic = new Dictionary<NpcEditorInfo, GameObject>
2025-12-10 18:54:43 +08:00
{
{ npcEditor, obj }
};
2025-12-10 17:39:34 +08:00
_enemyNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
}
}
index++;
}
2025-12-10 18:54:43 +08:00
// 敌方 NPC 加载完成后刷新一次分组视图
RefreshEnemyNpcGroups();
2025-12-10 17:39:34 +08:00
}
private void LoadMidNpcInfo()
{
var monsterConfigDic = MonsterCfg.DataMap;
currentMidNpcInfo = new();
2025-12-29 14:41:29 +08:00
var index = MidID + 1;
2025-12-10 17:39:34 +08:00
foreach (var npcInfo in currentLevelData.midNpcInfos)
{
if (!monsterConfigDic.TryGetValue(npcInfo.id, out var monsterClassData))
{
DebugUtil.LogError("请检查配置,没有 {0} 的怪物类ID", npcInfo.id);
continue;
}
var npcEditor = new NpcEditorInfo(npcInfo, index, monsterClassData.MonsterLevel, monsterClassData.AIType,
2025-12-22 15:33:08 +08:00
ETroopsId.Neutral, monsterClassData.NameRead);
2025-12-10 17:39:34 +08:00
currentMidNpcInfo.Add(npcEditor);
var npcObj = LoadMidNpcPrefab(npcInfo.id);
if (npcObj != null)
{
var setPos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
var yaw = LevelUtils.SetCharaterToward(npcInfo.npcToward);
var obj = Instantiate(npcObj, setPos, Quaternion.Euler(0, yaw, 0));
if (midObjRoot == null)
midObjRoot = new GameObject("midObjRoot");
if (obj != null)
obj.transform.SetParent(midObjRoot.transform);
if (_midNpcObjectWaveDic.TryGetValue(npcInfo.waveIndex, out var npcObjectDic))
{
npcObjectDic.Add(npcEditor, obj);
}
else
{
npcObjectDic = new Dictionary<NpcEditorInfo, GameObject>
{ { npcEditor, obj } };
_midNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
}
}
index++;
}
}
private void LoadSelfNpcInfo()
{
var monsterConfigDic = MonsterCfg.DataMap;
currentSelfNpcInfo = new();
var index = SelfID + 1;
foreach (var selfNpcInfo in currentLevelData.selfNpcInfos)
{
if (!monsterConfigDic.TryGetValue(selfNpcInfo.id, out var monsterClassData))
{
DebugUtil.LogError("请检查配置,没有 {0} 的怪物类ID, 已经删除对应", selfNpcInfo.id);
continue;
}
var npcEditor = new NpcEditorInfo(selfNpcInfo, index, monsterClassData.MonsterLevel,
2025-12-22 15:33:08 +08:00
monsterClassData.AIType, ETroopsId.Self, monsterClassData.NameRead);
2025-12-10 17:39:34 +08:00
currentSelfNpcInfo.Add(npcEditor);
var npcObj = LoadSelfNpcPrefab(selfNpcInfo.id);
if (npcObj != null)
{
var setPos = _curMap.BlockPosition2WorldPosition(selfNpcInfo.position);
var yaw = LevelUtils.SetCharaterToward(selfNpcInfo.npcToward);
var obj = Instantiate(npcObj, setPos, Quaternion.Euler(0, yaw, 0));
if (selfObjRoot == null)
selfObjRoot = new GameObject("selfObjRoot");
if (obj != null)
obj.transform.SetParent(selfObjRoot.transform);
_selfNpcObjectDic.Add(selfNpcInfo.position, obj);
}
index++;
}
}
#endregion
#region 检查关卡
private const string PreparingRegionErrorMessage = "关卡出战区设置错误,该地图没有:<{0}>号地区。\n";
private const string FixedMessage = "已将错误区域设置为默认<0>号地区。";
private void CheckCorrectLevel()
{
if (_curMap == null)
{
return;
}
var errorMessage = string.Empty;
if (!_curMap.MapData.IsRegionExist(curRegionData.preparingRegionId))
{
errorMessage += string.Format(PreparingRegionErrorMessage,
curRegionData.preparingRegionId);
curRegionData.preparingRegionId = 0;
}
if (!string.IsNullOrEmpty(errorMessage))
{
CustomPopUpWindow.PopUp(errorMessage + FixedMessage);
}
}
private bool CheckTask()
{
2025-12-11 18:02:28 +08:00
foreach (var taskInfoEditor in taskInfos)
{
2025-12-22 15:33:08 +08:00
if (taskInfoEditor.isErrorTaskID)
{
2025-12-11 18:02:28 +08:00
DebugUtil.LogError($"有错误任务ID {taskInfoEditor.taskID}, 无法保存关卡");
CustomPopUpWindow.PopUp($"有错误任务ID {taskInfoEditor.taskID}, 无法保存关卡");
return false;
}
}
2025-12-22 15:33:08 +08:00
/*if (!IsTaskInvalid(successTaskID))
2025-12-10 17:39:34 +08:00
{
DebugUtil.LogError("胜利条件错误, 无法保存关卡");
CustomPopUpWindow.PopUp("胜利条件错误, 无法保存关卡");
return false;
}
if (!IsTaskInvalid(failTaskID))
DebugUtil.LogError("失败条件错误");
for (var i = 0; i < starTaskIDs.Count; i++)
{
if (!IsTaskInvalid(starTaskIDs[i]))
DebugUtil.LogError($"{i + 1}星级条件错误");
}*/
2025-12-10 17:39:34 +08:00
return true;
}
#endregion
#region 内部方法
private void ProcessCondition()
{
foreach (var taskInfo in currentLevelData.taskInfos)
{
ProcessCondition(taskInfo.taskCondition);
}
}
private void ProcessCondition(ConditionModifier conditionModifier)
{
// 撤退数量特殊处理
if (conditionModifier.conditionType == ConditionType.CombatEvacuationCount &&
conditionModifier.conditionArgs.Count > 0)
{
currentLevelData.fallBackCount = (int)conditionModifier.conditionArgs[0].Arg;
}
conditionModifier._args = new();
foreach (var param in conditionModifier.conditionArgs)
{
conditionModifier._args.Add(param.Arg);
}
2026-01-04 13:40:25 +08:00
if (ConditionUtil.IsNeedUncertainParamCondition(conditionModifier.conditionType))
2025-12-10 17:39:34 +08:00
{
2026-01-04 13:40:25 +08:00
foreach (var param in conditionModifier.uncertainParam)
{
conditionModifier._args.Add(param);
}
2025-12-10 17:39:34 +08:00
}
}
/// <summary>
/// 关卡加载时 加载敌方预制体
/// </summary>
/// <param name="monsterID">Monster表ID</param>
/// <returns></returns>
private GameObject LoadEnemyNpcPrefab(int monsterID)
{
GameObject obj = null;
var curMonsterClassID = 0;
if (MonsterCfg.DataMap.TryGetValue(monsterID, out var monster))
{
curMonsterClassID = monster.MonsterClassID;
}
else
{
return null;
}
switch (monster.NpcType)
{
case EMonsterType.Vehicle:
{
if (VehicleCfg.DataMap.TryGetValue(curMonsterClassID, out var vehicle))
{
var prefabPath = PathEx.GetVehicleGameModelPath(vehicle.ID);
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (obj == null)
{
DebugUtil.LogError("请检查敌方载具类: {0}的预制体路径是否存在,对应的完整路径: {1}", curMonsterClassID, prefabPath);
}
}
else
{
DebugUtil.LogError("请检查敌方载具类: {0}的配置是否存在", curMonsterClassID);
}
return obj;
}
case EMonsterType.EnemyCharacter:
{
if (MonsterClassCfg.DataMap.TryGetValue(curMonsterClassID, out var monsterClass))
{
var prefabPath = PathEx.GetCharacterFightModelPath(monsterClass.NameId, monsterClass.WeaponId);
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (obj == null)
{
DebugUtil.LogError("请检查敌方怪物类: {0}的预制体路径是否存在,对应的完整路径: {1}", curMonsterClassID, prefabPath);
}
}
else
{
DebugUtil.LogError("请检查敌方怪物类: {0}的配置是否存在", curMonsterClassID);
}
return obj;
}
case EMonsterType.FightBuild:
{
if (FightBuildCfg.DataMap.TryGetValue(curMonsterClassID, out var buildData))
{
var prefabPath = PathEx.GetFightBuildingPath(buildData.ModelPath);
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (obj == null)
{
DebugUtil.LogError("请检查敌方建筑类: {0}的预制体路径是否存在,对应的完整路径: {1}", curMonsterClassID, prefabPath);
}
}
else
{
DebugUtil.LogError("请检查敌方建筑类: {0}的配置是否存在", curMonsterClassID);
}
return obj;
}
default: return null;
}
}
/// <summary>
/// 关卡加载时 加载中立预制体
/// </summary>
/// <param name="monsterID">Monster表ID</param>
/// <returns></returns>
private GameObject LoadMidNpcPrefab(int monsterID)
{
GameObject obj = null;
var curMonsterClassID = 0;
if (MonsterCfg.DataMap.TryGetValue(monsterID, out var monster))
{
curMonsterClassID = monster.MonsterClassID;
}
else
{
return null;
}
switch (monster.NpcType)
{
case EMonsterType.Vehicle:
{
if (VehicleCfg.DataMap.TryGetValue(curMonsterClassID, out var vehicle))
{
var prefabPath = PathEx.GetVehicleGameModelPath(vehicle.ID);
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (obj == null)
{
DebugUtil.LogError("请检查敌方载具类: {0}的预制体路径是否存在,对应的完整路径: {1}", curMonsterClassID, prefabPath);
}
}
else
{
DebugUtil.LogError("请检查敌方载具类: {0}的配置是否存在", curMonsterClassID);
}
return obj;
}
case EMonsterType.EnemyCharacter:
{
if (MonsterClassCfg.DataMap.TryGetValue(curMonsterClassID, out var monsterClass))
{
var prefabPath = PathEx.GetCharacterFightModelPath(monsterClass.NameId, monsterClass.WeaponId);
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (obj == null)
{
DebugUtil.LogError("请检查敌方怪物类: {0}的预制体路径是否存在,对应的完整路径: {1}", curMonsterClassID, prefabPath);
}
}
else
{
DebugUtil.LogError("请检查敌方怪物类: {0}的配置是否存在", curMonsterClassID);
}
return obj;
}
case EMonsterType.FightBuild:
{
if (FightBuildCfg.DataMap.TryGetValue(curMonsterClassID, out var buildData))
{
var prefabPath = PathEx.GetFightBuildingPath(buildData.ModelPath);
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (obj == null)
{
DebugUtil.LogError("请检查敌方建筑类: {0}的预制体路径是否存在,对应的完整路径: {1}", curMonsterClassID, prefabPath);
}
}
else
{
DebugUtil.LogError("请检查敌方建筑类: {0}的配置是否存在", curMonsterClassID);
}
return obj;
}
default: return null;
}
}
/// <summary>
/// 关卡加载时 加载己方预制体
/// </summary>
/// <param name="monsterID">Monster表ID</param>
/// <returns></returns>
private GameObject LoadSelfNpcPrefab(int monsterID)
{
GameObject obj = null;
var curFightNpcID = 0;
if (MonsterCfg.DataMap.TryGetValue(monsterID, out var monster))
{
curFightNpcID = monster.MonsterClassID;
}
else
{
return null;
}
switch (monster.NpcType)
{
case EMonsterType.Vehicle:
{
if (VehicleCfg.DataMap.TryGetValue(curFightNpcID, out var vehicle))
{
var prefabPath = PathEx.GetVehicleGameModelPath(vehicle.ID);
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (obj == null)
{
DebugUtil.LogError("请检查己方载具类: {0}的预制体路径是否存在,对应的完整路径: {1}", curFightNpcID, prefabPath);
}
}
else
{
DebugUtil.LogError("请检查己方载具类: {0}的配置是否存在", curFightNpcID);
}
return obj;
}
case EMonsterType.SelfCharacter:
{
if (FightNpcCfg.DataMap.TryGetValue(curFightNpcID, out var fightNpc))
{
var prefabPath = PathEx.GetCharacterFightModelPath(fightNpc.NameId, fightNpc.WeaponId);
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (obj == null)
{
DebugUtil.LogError("请检查己方NPC类: {0}的预制体路径是否存在,对应的完整路径: {1}", curFightNpcID, prefabPath);
}
}
else
{
DebugUtil.LogError("请检查己方NPC类: {0}的配置是否存在", curFightNpcID);
}
return obj;
}
case EMonsterType.FightBuild:
{
if (FightBuildCfg.DataMap.TryGetValue(curFightNpcID, out var buildData))
{
var prefabPath = PathEx.GetFightBuildingPath(buildData.ModelPath);
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (obj == null)
{
DebugUtil.LogError("请检查己方建筑类: {0}的预制体路径是否存在,对应的完整路径: {1}", curFightNpcID, prefabPath);
}
}
else
{
DebugUtil.LogError("请检查己方建筑类: {0}的配置是否存在", curFightNpcID);
}
return obj;
}
default: return null;
}
}
private void DestroyNpcPrefab()
{
if (selfObjRoot != null)
DestroyImmediate(selfObjRoot);
if (midObjRoot != null)
DestroyImmediate(midObjRoot);
if (enemyObjRoot != null)
DestroyImmediate(enemyObjRoot);
}
private void ChangeEnemyNpcToward(NpcEditorInfo npcEditorInfo, CharacterToward npcToward)
{
if (_enemyNpcObjectWaveDic.TryGetValue(npcEditorInfo.npcInfo.waveIndex, out var npcObjectDic))
{
if (npcObjectDic.TryGetValue(npcEditorInfo, out var npcObj))
{
var yaw = LevelUtils.SetCharaterToward(npcToward);
npcObj.transform.eulerAngles = new Vector3(0, yaw, 0);
}
}
}
private void ChangeMidNpcToward(NpcEditorInfo npcEditorInfo, CharacterToward npcToward)
{
if (_midNpcObjectWaveDic.TryGetValue(npcEditorInfo.npcInfo.waveIndex, out var npcObjectDic))
{
if (npcObjectDic.TryGetValue(npcEditorInfo, out var npcObj))
{
var yaw = LevelUtils.SetCharaterToward(npcToward);
npcObj.transform.eulerAngles = new Vector3(0, yaw, 0);
}
}
}
private void ChangeSelfNpcToward(Vector2Int pos, CharacterToward npcToward)
{
if (_selfNpcObjectDic.TryGetValue(pos, out var npcObj))
{
var yaw = LevelUtils.SetCharaterToward(npcToward);
npcObj.transform.eulerAngles = new Vector3(0, yaw, 0);
}
}
private bool CheckConditionSame(ConditionType conditionType)
{
if (currentLevelData.scoreTarget.Any(modifier => modifier.conditionType == conditionType))
{
return true;
}
return false;
}
#endregion
#region Draw
private bool IsShowNpcID => _showNpcInfos != null && _showNpcInfos.Count > 0;
private List<NpcEditorInfo> _showNpcInfos;
private void OnSceneGUI(SceneView sceneView)
{
DrawRoadPath();
DrawAreaPath();
DrawNpcID();
DrawSphereID();
}
private void DrawRoadPath()
{
if (null == _currEditingRoadInfo)
return;
Vector3 lastWorldPos = Vector3.zero;
for (int i = 0; i < _currEditingRoadInfo.roadPoints.Count; i++)
{
var gridPos = _currEditingRoadInfo.roadPoints[i];
var worldPos = _curMap.TGSCellIndex2WorldPosition(gridPos.cellIndex);
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;
}
}
private void DrawAreaPath()
{
if (null == _currEditingAreaInfo)
return;
for (int i = 0; i < _currEditingAreaInfo.areaPoints.Count; ++i)
{
var gridPos = _currEditingAreaInfo.areaPoints[i];
var worldPos = _curMap.TGSCellIndex2WorldPosition(gridPos.cellIndex);
Handles.color = Color.blue;
Handles.DrawSolidDisc(worldPos, Vector3.up, 1.4f);
}
}
private GUIStyle labelStyle;
private GUIStyle labelStyleSelf;
private GUIStyle labelStyleSphere;
private void DrawNpcID()
{
if (!IsShowNpcID)
return;
labelStyle ??= new GUIStyle
{
fontSize = 38,
fontStyle = FontStyle.Bold,
normal =
{
textColor = Color.red
}
};
labelStyleSelf ??= new GUIStyle
{
fontSize = 38,
fontStyle = FontStyle.Bold,
normal =
{
textColor = Color.green
}
};
foreach (var npc in _showNpcInfos)
{
if (isShowAllEnemyNpcID &&
_enemyNpcObjectWaveDic.TryGetValue(npc.npcInfo.waveIndex, out var enemyNpcObjectDic))
{
var waveID = npc.npcInfo.waveIndex;
if (enemyNpcObjectDic.TryGetValue(npc, out var npcObj))
{
var groupsText = npc.npcInfo.groups is { Count: > 0 }
? string.Join(",", npc.npcInfo.groups)
: "-";
var content = $"{waveID},{npc.id}|{groupsText}";
Handles.Label(npcObj.transform.position, content, labelStyle);
}
}
else if (isShowAllMidNpcID &&
_midNpcObjectWaveDic.TryGetValue(npc.npcInfo.waveIndex, out var midNpcObjectDic))
{
var waveID = npc.npcInfo.waveIndex;
if (midNpcObjectDic.TryGetValue(npc, out var npcObj))
{
var groupsText = npc.npcInfo.groups is { Count: > 0 }
? string.Join(",", npc.npcInfo.groups)
: "-";
var content = $"{waveID},{npc.id}|{groupsText}";
Handles.Label(npcObj.transform.position, content, labelStyle);
}
}
else if (isShowAllSelfNpcID && _selfNpcObjectDic.TryGetValue(npc.npcInfo.position, out var selfNpcObj))
{
Handles.Label(selfNpcObj.transform.position, npc.id.ToString(), labelStyleSelf);
}
}
}
private void DrawSphereID()
{
if (!canEditorBlockSphere)
return;
labelStyleSphere ??= new GUIStyle
{
fontSize = 30,
fontStyle = FontStyle.Bold,
normal =
{
textColor = Color.black
}
};
foreach (var blockSpereEditor in blockSphereEditorList)
{
if (blockSpereEditor.sphere != null)
{
var pos = new Vector3(blockSpereEditor.sphere.transform.position.x, Calibre,
blockSpereEditor.sphere.transform.position.z);
Handles.Label(pos, blockSpereEditor.cellIndex.ToString(),
labelStyleSphere);
}
}
}
#endregion
2025-12-29 19:07:03 +08:00
#region 波次信息加载
/// <summary>
/// 加载波次信息从NPC数据中重建
/// </summary>
private void LoadWaveInfo()
{
waveInfoEditors.Clear();
if (currentEnemyNpcInfo == null || currentEnemyNpcInfo.Count == 0)
{
DebugUtil.Log("没有敌方NPC无需加载波次信息");
return;
}
// 从NPC数据中收集波次信息
// 结构: waveID -> List<groupID>
var waveGroupsMap = new Dictionary<int, HashSet<int>>();
foreach (var npc in currentEnemyNpcInfo)
{
// 只处理标记为中途生成的NPC
if (npc.npcInfo.isHideOnLoad && npc.npcInfo.waveIndex > 0)
{
if (!waveGroupsMap.ContainsKey(npc.npcInfo.waveIndex))
{
waveGroupsMap[npc.npcInfo.waveIndex] = new HashSet<int>();
}
// 收集该NPC所属的所有组
if (npc.npcInfo.groups != null)
{
foreach (var groupId in npc.npcInfo.groups)
{
waveGroupsMap[npc.npcInfo.waveIndex].Add(groupId);
}
}
}
}
// 创建波次编辑器
foreach (var kvp in waveGroupsMap.OrderBy(x => x.Key))
{
var waveEditor = new WaveInfoEditor(kvp.Key, kvp.Value.ToList());
waveInfoEditors.Add(waveEditor);
}
DebugUtil.Log($"成功从NPC数据中重建 {waveInfoEditors.Count} 个波次信息");
}
#endregion
2025-12-10 17:39:34 +08:00
}