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

3487 lines
111 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using TGS;
using System;
using Gameplay;
using Framework;
using System.IO;
using UnityEditor;
using UnityEngine;
using System.Linq;
using Gameplay.Utils;
using Gameplay.Level;
using Sirenix.Utilities;
using Sirenix.OdinInspector;
using Sirenix.Utilities.Editor;
using System.Collections.Generic;
using cfg;
using cfg.AICfg;
using cfg.FightCfg;
using cfg.GuideCfg;
using cfg.LevelCfg;
using cfg.MonsterCfg;
using cfg.VehicleCfg;
using Sirenix.OdinInspector.Editor;
using Level = cfg.LevelCfg.Level;
using ObjectFieldAlignment = Sirenix.OdinInspector.ObjectFieldAlignment;
using static Gameplay.Level.LevelData;
using Gameplay.Character;
public class LevelEditorWindow : OdinEditorWindow
{
[MenuItem("Tools/*地图场景/关卡编辑器", false, (int)NLDMenuID.LevelEditor)]
private static void OpenLevelEditorWindow()
{
var window = GetWindow<LevelEditorWindow>();
window.position = GUIHelper.GetEditorWindowRect().AlignCenter(800, 600);
window.titleContent = new GUIContent("关卡编辑器");
}
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);
var level = Path.GetFileNameWithoutExtension(levelEditor.LevelDataJson.name);
currWindow.levelID = level.Substring(level.Length - 6);
//currWindow.GetLevelMonsterClass();
DebugUtil.Log("关卡数据:{0}", currWindow.levelID);
}
private static LevelEditorWindow currWindow;
private MapManager _mapManager;
private Map _curMap;
private LevelEditor _levelEditor;
#region 配置表
private Level LevelCfg => EditorTableManager.instance.tables.Level;
private Monster MonsterCfg => EditorTableManager.instance.tables.Monster;
private MonsterClass MonsterClassCfg => EditorTableManager.instance.tables.MonsterClass;
private VehicleConfig VehicleCfg => EditorTableManager.instance.tables.VehicleConfig;
private FightNPCConfig FightNpcCfg => EditorTableManager.instance.tables.FightNPCConfig;
private BuildConfig FightBuildCfg => EditorTableManager.instance.tables.BuildConfig;
#endregion
#region 地块颜色
private static readonly Color RegionColor = MapUtils.GetRGBAColor("FF960072");
#endregion
#region 关卡数据
[BoxGroup("当前关卡数据")] [LabelText("当前关卡数据"), Indent]
public LevelData currentLevelData = null;
#endregion
#region 关卡进度
[BoxGroup("关卡引导")] [LabelText("关卡进度"), Indent]
public List<LevelData.ProgressGuideData> progressGuideDatas = new();
#endregion
#region 引导立标
[Serializable]
public class SetFlagGuideEditorInfo
{
public SetFlagGuideEditorInfo(LevelData.SetFlagGuideData flagGuideData)
{
this.flagGuideData = flagGuideData;
guideTagID = flagGuideData.guideTagID;
if (GuideTagConfig.DataMap.TryGetValue(guideTagID, out var guideData))
{
flagGuideData.guideTagPath = guideData.ImagePath;
guideTagName = guideData.Name;
var sprite = AssetDatabase.LoadAssetAtPath<Sprite>(guideData.ImagePath);
if (sprite != null)
guideTagSprite = sprite;
}
}
private GuideTagConfig GuideTagConfig => EditorTableManager.instance.tables.GuideTagConfig;
[HorizontalGroup("1")] [LabelText("立标配置ID")] [ValueDropdown("GetGuideTagIDs")] [OnValueChanged("ChangeTagID")]
public int guideTagID;
[HorizontalGroup("1")] [LabelText("立标名字")] [ReadOnly]
public string guideTagName;
[HorizontalGroup("2")] [LabelText("预览图")] [PreviewField(150, ObjectFieldAlignment.Center), Indent]
public Sprite guideTagSprite;
[HorizontalGroup("3")] [LabelText("具体数据")]
public LevelData.SetFlagGuideData flagGuideData;
private IEnumerable<int> GetGuideTagIDs()
{
return GuideTagConfig.DataMap.Keys;
}
private void ChangeTagID()
{
if (GuideTagConfig.DataMap.TryGetValue(guideTagID, out var guideData))
{
flagGuideData.guideTagPath = guideData.ImagePath;
flagGuideData.guideTagID = guideTagID;
guideTagName = guideData.Name;
var sprite = AssetDatabase.LoadAssetAtPath<Sprite>(guideData.ImagePath);
if (sprite != null)
guideTagSprite = sprite;
}
}
}
[BoxGroup("关卡引导")] [LabelText("立标数据"), Indent]
public List<SetFlagGuideEditorInfo> setFlagEditorInfos = new();
#endregion
#region 关卡进度
[BoxGroup("关卡引导")] [LabelText("信息板数据"), Indent]
public List<LevelData.InfoPanelGuideData> infoPanelGuideDatas = new();
#endregion
#region 关卡AI广播
[BoxGroup("关卡AI广播")] [LabelText("AI广播数据"), Indent]
public List<AIBroadcastData> listAIBroadcastData = new();
#endregion
#region 占领相关
[Serializable]
public class CaptureEditorInfo
{
[LabelText("占领点位")] public Vector2Int capturePoint;
[LabelText("占领时间")] public int captureProgress;
}
[BoxGroup("占领相关")] [VerticalGroup("占领相关/数据")] [LabelText("当前占领数据"), Indent]
public List<CaptureEditorInfo> captureEditorInfo = new();
[BoxGroup("占领相关")] [VerticalGroup("占领相关/编辑")] [LabelText("编辑占领数据"), Indent]
public bool isEditingCapture = false;
#endregion
#region 扛旗相关
[Serializable]
public class FlagEditorInfo
{
[LabelText("扛旗起点")] public List<Vector2Int> flagStartPoints = new();
[LabelText("扛旗终点")] public List<Vector2Int> flagEndPoints = new();
[LabelText("添加起点")] [OnValueChanged("EditingStartPoints")]
public bool isEditingStart;
[LabelText("添加终点")] [OnValueChanged("EditingEndPoints")]
public bool isEditingEnd;
private void EditingStartPoints()
{
currWindow.isEditingFlagStart = isEditingStart;
if (isEditingStart)
{
isEditingEnd = false;
currWindow.isEditingFlagEnd = false;
}
}
private void EditingEndPoints()
{
currWindow.isEditingFlagEnd = isEditingEnd;
if (isEditingEnd)
{
isEditingStart = false;
currWindow.isEditingFlagStart = false;
}
}
}
private bool isEditingFlagStart;
private bool isEditingFlagEnd;
[BoxGroup("扛棋相关")] [LabelText("当前扛旗数据"), Indent]
public FlagEditorInfo flagEditorInfo = new();
#endregion
#region 撤离相关
[Serializable]
public class FallbackEditorInfo
{
[LabelText("单次撤离点")] public List<Vector2Int> fallbackSinglePoints = new();
[LabelText("复用撤离点")] public List<Vector2Int> fallbackReusablePoints = new();
[LabelText("添加单次撤离点")] [OnValueChanged("EditingFallbackPoints")]
public bool isEditingSinglePoints;
[LabelText("添加复用撤离点")] [OnValueChanged("EditingMultiplexPoint")]
public bool isEditingReusablePoints;
private void EditingFallbackPoints()
{
currWindow.isEditingFallbackSingle = isEditingSinglePoints;
if (isEditingSinglePoints)
{
isEditingReusablePoints = false;
currWindow.isEditingFallbackReusable = false;
}
}
private void EditingMultiplexPoint()
{
currWindow.isEditingFallbackReusable = isEditingReusablePoints;
if (isEditingReusablePoints)
{
isEditingSinglePoints = false;
currWindow.isEditingFallbackSingle = false;
}
}
}
private bool isEditingFallbackSingle;
private bool isEditingFallbackReusable;
[BoxGroup("撤离相关")] [LabelText("当前撤离数据"), Indent]
public FallbackEditorInfo fallbackEditorInfo = new();
#endregion
#region 地区相关
[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);
}
}
[BoxGroup("地区相关")] [LabelText("当前地区数据"), Indent]
public RegionData curRegionData = null;
#endregion
#region NPC刷新点位相关
[HideInInspector] [BoxGroup("刷新点位")] [LabelText("编辑刷新点"), Indent, OnValueChanged("OnIsEditingRefreshIDChanged")]
public bool isEditingRefreshID;
[HideInInspector] [BoxGroup("刷新点位")] [LabelText("刷新点位"), Indent] [ShowIf("isEditingRefreshID")]
public List<int> refreshPoints = new List<int>();
private void OnIsEditingRefreshIDChanged()
{
if (!isEditingRefreshID)
{
foreach (var point in refreshPoints)
{
_curMap.TerrainGridSystem.CellHideRegionSurface(point);
}
}
else
{
foreach (var point in refreshPoints)
{
_curMap.TerrainGridSystem.CellToggleRegionSurface(point, true, RegionColor);
}
}
}
#endregion
#region 指定路线
[Serializable]
public class PrescribedRoadEditorInfo
{
[LabelText("路线ID")] public int roadID;
[LabelText("路线点位")] public List<PatrolInfo> roadPoints = new();
private bool isEditingRoadInfo;
public PrescribedRoadEditorInfo()
{
roadID = currWindow.roadEditorInfos.Count + 1;
}
[Button("开始编辑路线"), Indent]
[HideIf("isEditingRoadInfo")]
private void StartEditRoad()
{
isEditingRoadInfo = true;
currWindow.StartEditRoadInfo(this);
}
[Button("完成路线编辑"), Indent]
[ShowIf("isEditingRoadInfo")]
private void EndEditRoad()
{
isEditingRoadInfo = false;
currWindow.FinishEditRoadInfo();
}
}
[BoxGroup("制定路线")] [LabelText("编辑指定路径"), Indent]
public bool isEditingRoad;
[BoxGroup("制定路线")] [LabelText("编辑指定路线"), Indent] [ShowIf("isEditingRoad")]
public List<PrescribedRoadEditorInfo> roadEditorInfos = new();
private bool isEditingRoadInfo;
private PrescribedRoadEditorInfo _currEditingRoadInfo;
private void StartEditRoadInfo(PrescribedRoadEditorInfo roadInfo)
{
isEditingRoadInfo = true;
_currEditingRoadInfo = roadInfo;
}
private void FinishEditRoadInfo()
{
isEditingRoadInfo = false;
_currEditingRoadInfo = null;
}
#endregion
#region 指定区域
[Serializable]
public class PrescribedAreaEditorInfo
{
[LabelText("区域ID")] public int areaID;
[LabelText("区域点位")] public List<PatrolInfo> areaPoints = new();
private bool isEditingAreaInfo;
public PrescribedAreaEditorInfo()
{
areaID = currWindow.areaEditorInfos.Count + 1;
}
[Button("开始编辑区域"), Indent]
[HideIf("isEditingAreaInfo")]
private void StartEditArea()
{
isEditingAreaInfo = true;
currWindow.StartEditAreaInfo(this);
}
[Button("完成区域编辑"), Indent]
[ShowIf("isEditingAreaInfo")]
private void EndEditArea()
{
isEditingAreaInfo = false;
currWindow.FinishEditAreaInfo();
}
}
[BoxGroup("制定区域")] [LabelText("编辑指定区域"), Indent]
public bool isEditingArea;
[BoxGroup("制定区域")] [LabelText("编辑指定区域"), Indent] [ShowIf("isEditingArea")]
public List<PrescribedAreaEditorInfo> areaEditorInfos = new();
private bool isEditingAreaInfo;
private PrescribedAreaEditorInfo _currEditingAreaInfo;
private void StartEditAreaInfo(PrescribedAreaEditorInfo roadInfo)
{
isEditingAreaInfo = true;
_currEditingAreaInfo = roadInfo;
}
private void FinishEditAreaInfo()
{
isEditingAreaInfo = false;
_currEditingAreaInfo = null;
}
#endregion
#region 显示地图索引
[BoxGroup("显示地格索引")]
[LabelText("显示类型"), Indent]
[OnValueChanged("ShowCellIndex")]
[LabelWidth(100)]
[SerializeField]
private CellDebugInfo cellDebugInfo = CellDebugInfo.Nothing;
private void ShowCellIndex()
{
if (_curMap == null || _curMap.TerrainGridSystem == null) return;
_curMap.TerrainGridSystem.displayCellDebugInfo = cellDebugInfo;
var obj = FindObjectOfType<TerrainGridSystem>().gameObject;
Selection.activeGameObject = obj;
}
#endregion
#region NPC编辑器类
[Serializable]
public class NpcEditorInfo
{
private AITypeMonsterConfig AITypeCfg => EditorTableManager.instance.tables.AITypeMonsterConfig;
[HideInInspector] public int npcTroopId;
[ReadOnly] [LabelText("序号ID")] public int id;
[FoldoutGroup("详细信息")] [LabelText("MonsterID")] [ValueDropdown("GetMonsterIDs")] [OnValueChanged("ChangeNpcID")]
public int monsterId;
[FoldoutGroup("详细信息")] [LabelText("等级")] [ReadOnly]
public int level;
private IEnumerable<int> GetMonsterIDs()
{
return npcTroopId switch
{
ETroopsId.Neutral => currWindow.GetMidMonsterIDs(), // 中立
ETroopsId.Self => currWindow.GetSelfNpcIDs(), // 友方
ETroopsId.Enemy => currWindow.GetEnemyMonsterIDs(), // 敌人
_ => null
};
}
private void ChangeNpcID()
{
switch (npcTroopId)
{
case ETroopsId.Neutral: // 中立
currWindow.ChangeMidNpcID(this, monsterId);
break;
case ETroopsId.Self: // 友方
currWindow.ChangeSelfNpcID(npcInfo, monsterId);
break;
case ETroopsId.Enemy: // 敌人
currWindow.ChangeEnemyNpcID(this, monsterId);
break;
default:
DebugUtil.LogError($"未处理类型:{npcTroopId}");
break;
}
}
[FoldoutGroup("详细信息")] [LabelText("朝向")] [OnValueChanged("NpcTowardOnChange")]
public CharacterToward npcToward;
private void NpcTowardOnChange()
{
npcInfo.npcToward = npcToward;
switch (npcTroopId)
{
case ETroopsId.Neutral: // 中立
currWindow.ChangeMidNpcToward(this, npcToward);
break;
case ETroopsId.Self: // 友方
currWindow.ChangeSelfNpcToward(npcInfo.position, npcToward);
break;
case ETroopsId.Enemy: // 敌人
currWindow.ChangeEnemyNpcToward(this, npcToward);
break;
default:
DebugUtil.LogError($"未处理类型:{npcTroopId}");
break;
}
}
[FoldoutGroup("详细信息")] [HideLabel] public NPCInfo npcInfo;
[FoldoutGroup("详细信息")] [LabelText("AI预设")] [ReadOnly]
public string aiType;
public NpcEditorInfo(NPCInfo npcInfo, int index, int level, int aiType, int npcTroopId)
{
this.npcInfo = npcInfo;
this.level = level;
this.aiType = GetAITypeData(aiType);
monsterId = npcInfo.id;
id = index;
this.npcTroopId = npcTroopId;
npcToward = this.npcInfo.npcToward;
}
[FoldoutGroup("详细信息")] [LabelText("位置调整")] [OnValueChanged("ChangeNpcPos")]
public bool isChangePos;
private void ChangeNpcPos()
{
if (isChangePos)
{
if (currWindow._currEditingNpcInfo != null)
DebugUtil.LogWarning("检查其他怪物是否处于位置调整或编辑巡逻信息状态");
}
currWindow._currNpcEditorInfo = isChangePos ? this : null;
currWindow.isEditingNpcPos = isChangePos;
currWindow.npcTroopId = npcTroopId;
currWindow._currEditingNpcInfo = isChangePos ? npcInfo : null;
}
[FoldoutGroup("详细信息")] [LabelText("显示该怪物序号")] [OnValueChanged("ShowNpcID")]
public bool isShowNpcID;
private void ShowNpcID()
{
if (isShowNpcID && !currWindow._showNpcInfos.Contains(this))
currWindow._showNpcInfos.Add(this);
else if (!isShowNpcID)
{
currWindow._showNpcInfos.Remove(this);
}
}
private string GetAITypeData(int aiTypeID)
{
if (AITypeCfg.DataMap.TryGetValue(aiTypeID, out var dataAIType))
{
return dataAIType.Name;
}
return $"无效AI类型: {aiTypeID},请检查配置表";
}
}
private NPCInfo _currEditingNpcInfo;
private NpcEditorInfo _currNpcEditorInfo;
private bool isEditingNpcPos;
private int npcTroopId;
#endregion
#region 敌方Npc
private GameObject enemyObjRoot;
/// <summary>
/// 波次/怪物ID对应的NPC预制体
/// </summary>
private Dictionary<int, Dictionary<NpcEditorInfo, GameObject>> _enemyNpcObjectWaveDic;
[BoxGroup("敌方Npc")]
[LabelText("编辑NPC"), Indent]
[LabelWidth(100)]
[SerializeField]
[OnValueChanged("OnIsEditingEnemyNpcChanged")]
public bool isEditingEnemyNpc;
[BoxGroup("敌方Npc")] [LabelText("显示所有怪物序号ID"), Indent] [OnValueChanged("ShowAllEnemyNpcId")] [SerializeField]
public bool isShowAllEnemyNpcID;
[BoxGroup("敌方Npc")] [LabelText("筛选怪物ID"), Indent, LabelWidth(100)] [SerializeField] [ShowIf("isEditingEnemyNpc")]
public string selectEnemyMonsterId;
[BoxGroup("敌方Npc")]
[LabelText("怪物ID"), Indent, LabelWidth(100)]
[SerializeField]
[OnValueChanged("EnemyMonsterIDChanged")]
[ValueDropdown("GetEnemyMonsterIDs")]
[ShowIf("isEditingEnemyNpc")]
public int curEnemyMonsterID;
[BoxGroup("敌方Npc")]
[PreviewField(150, ObjectFieldAlignment.Center), Indent, ReadOnly]
[LabelText("NPC预制体预览图"), ShowIf("isEditingEnemyNpc")]
public GameObject enemyNpcObject;
[BoxGroup("敌方Npc")]
[LabelText("当前怪物列表"), Indent, ShowIf("isEditingEnemyNpc")]
[ListDrawerSettings(HideAddButton = true, HideRemoveButton = true, ShowFoldout = true)]
public List<NpcEditorInfo> currentEnemyNpcInfo = null;
/// <summary>
/// 该关卡的敌方NPCID
/// </summary>
[HideInInspector] public List<int> enemyMonsterIDs;
private IEnumerable<int> GetEnemyMonsterIDs()
{
enemyMonsterIDs ??= new List<int>();
enemyMonsterIDs.Clear();
if (null == selectEnemyMonsterId)
return enemyMonsterIDs;
foreach (var monster in MonsterCfg.DataMap)
{
string strID = monster.Key.ToString();
if (!strID.StartsWith(selectEnemyMonsterId))
continue;
switch (monster.Value.NpcType)
{
case EMonsterType.EnemyCharacter:
case EMonsterType.Vehicle:
case EMonsterType.FightBuild:
{
enemyMonsterIDs.Add(monster.Key);
}
break;
case EMonsterType.SelfCharacter:
break;
default:
DebugUtil.LogError($"未处理类型:{monster.Value.NpcType}");
break;
}
}
return enemyMonsterIDs;
}
private void EnemyMonsterIDChanged()
{
var obj = LoadEnemyNpcPrefab(curEnemyMonsterID);
if (obj != null)
enemyNpcObject = obj;
}
private void OnIsEditingEnemyNpcChanged()
{
if (isEditingEnemyNpc && isEditingSelfNpc && isEditingMidNpc)
{
isEditingSelfNpc = false;
isEditingMidNpc = false;
}
}
private void ShowAllEnemyNpcId()
{
foreach (var npc in currentEnemyNpcInfo)
{
npc.isShowNpcID = isShowAllEnemyNpcID;
if (isShowAllEnemyNpcID)
{
if (!_showNpcInfos.Contains(npc))
{
_showNpcInfos.Add(npc);
}
}
else
{
_showNpcInfos.Remove(npc);
}
}
}
#endregion
#region 己方Npc
private const int SelfID = 200;
private GameObject selfObjRoot;
private Dictionary<Vector2Int, GameObject> _selfNpcObjectDic;
[BoxGroup("己方Npc")]
[LabelText("编辑己方NPC"), Indent]
[LabelWidth(100)]
[OnValueChanged("OnIsEditingSelfNpcChanged")]
[SerializeField]
public bool isEditingSelfNpc;
[BoxGroup("己方Npc")] [LabelText("显示所有己方序号ID"), Indent] [OnValueChanged("ShowAllSelfNpcId")] [SerializeField]
public bool isShowAllSelfNpcID;
[BoxGroup("己方Npc")] [LabelText("筛选怪物ID"), Indent, LabelWidth(100)] [SerializeField] [ShowIf("isEditingSelfNpc")]
public string selectSelfMonsterId;
[BoxGroup("己方Npc")]
[LabelText("己方NpcID"), Indent, LabelWidth(100)]
[SerializeField]
[OnValueChanged("SelfNpcIDChanged")]
[ValueDropdown("GetSelfNpcIDs")]
[ShowIf("isEditingSelfNpc")]
public int curSelfNpcID;
[BoxGroup("己方Npc")]
[PreviewField(150, ObjectFieldAlignment.Center), Indent, ReadOnly]
[LabelText("己方NPC预制体预览图"), ShowIf("isEditingSelfNpc")]
public GameObject selfNpcObject;
[BoxGroup("己方Npc")]
[LabelText("当前己方NPC列表"), Indent, ShowIf("isEditingSelfNpc")]
[ListDrawerSettings(HideAddButton = true, HideRemoveButton = true)]
public List<NpcEditorInfo> currentSelfNpcInfo = null;
/// <summary>
/// 该关卡的己方NPCID
/// </summary>
[HideInInspector] public List<int> selfMonsterIDs;
private IEnumerable<int> GetSelfNpcIDs()
{
selfMonsterIDs ??= new List<int>();
selfMonsterIDs.Clear();
if (null == selectSelfMonsterId)
return selfMonsterIDs;
foreach (var monster in MonsterCfg.DataMap)
{
string strID = monster.Key.ToString();
if (!strID.StartsWith(selectSelfMonsterId))
continue;
switch (monster.Value.NpcType)
{
case EMonsterType.SelfCharacter:
case EMonsterType.Vehicle:
case EMonsterType.FightBuild:
{
selfMonsterIDs.Add(monster.Key);
}
break;
case EMonsterType.EnemyCharacter:
break;
default:
DebugUtil.LogError($"未处理类型:{monster.Value.NpcType}");
break;
}
}
return selfMonsterIDs;
}
private void SelfNpcIDChanged()
{
var obj = LoadSelfNpcPrefab(curSelfNpcID);
if (obj != null)
selfNpcObject = obj;
}
private void OnIsEditingSelfNpcChanged()
{
if (isEditingEnemyNpc && isEditingSelfNpc && isEditingMidNpc)
{
isEditingEnemyNpc = false;
isEditingMidNpc = false;
}
}
private void ShowAllSelfNpcId()
{
foreach (var npc in currentSelfNpcInfo)
{
npc.isShowNpcID = isShowAllSelfNpcID;
if (isShowAllSelfNpcID)
{
if (!_showNpcInfos.Contains(npc))
{
_showNpcInfos.Add(npc);
}
}
else
{
_showNpcInfos.Remove(npc);
}
}
}
#endregion
#region 中立Npc
private GameObject midObjRoot;
/// <summary>
/// 中立 波次/怪物ID对应的NPC预制体
/// </summary>
private Dictionary<int, Dictionary<NpcEditorInfo, GameObject>> _midNpcObjectWaveDic;
[BoxGroup("中立Npc")]
[LabelText("编辑NPC"), Indent]
[LabelWidth(100)]
[SerializeField]
[OnValueChanged("OnIsEditingMidNpcChanged")]
public bool isEditingMidNpc;
[BoxGroup("中立Npc")] [LabelText("显示所有怪物序号ID"), Indent] [OnValueChanged("ShowAllMidNpcId")] [SerializeField]
public bool isShowAllMidNpcID;
[BoxGroup("中立Npc")] [LabelText("筛选怪物ID"), Indent, LabelWidth(100)] [SerializeField] [ShowIf("isEditingMidNpc")]
public string selectMidMonsterId;
[BoxGroup("中立Npc")]
[LabelText("怪物ID"), Indent, LabelWidth(100)]
[SerializeField]
[OnValueChanged("MidMonsterIDChanged")]
[ValueDropdown("GetMidMonsterIDs")]
[ShowIf("isEditingMidNpc")]
public int curMidMonsterID;
[BoxGroup("中立Npc")]
[PreviewField(150, ObjectFieldAlignment.Center), Indent, ReadOnly]
[LabelText("NPC预制体预览图"), ShowIf("isEditingMidNpc")]
public GameObject midNpcObject;
[BoxGroup("中立Npc")]
[LabelText("当前怪物列表"), Indent, ShowIf("isEditingMidNpc")]
[ListDrawerSettings(HideAddButton = true, HideRemoveButton = true, ShowFoldout = true)]
public List<NpcEditorInfo> currentMidNpcInfo = null;
/// <summary>
/// 该关卡的中立NPCID
/// </summary>
[HideInInspector] public List<int> midMonsterIDs;
private IEnumerable<int> GetMidMonsterIDs()
{
midMonsterIDs ??= new List<int>();
midMonsterIDs.Clear();
if (null == selectMidMonsterId)
return midMonsterIDs;
foreach (var monster in MonsterCfg.DataMap)
{
string strID = monster.Key.ToString();
if (!strID.StartsWith(selectMidMonsterId))
continue;
switch (monster.Value.NpcType)
{
case EMonsterType.Vehicle:
case EMonsterType.FightBuild:
{
midMonsterIDs.Add(monster.Key);
}
break;
case EMonsterType.EnemyCharacter:
case EMonsterType.SelfCharacter:
break;
default:
DebugUtil.LogError($"未处理类型:{monster.Value.NpcType}");
break;
}
}
return midMonsterIDs;
}
private void MidMonsterIDChanged()
{
var obj = LoadMidNpcPrefab(curMidMonsterID);
if (obj != null)
midNpcObject = obj;
}
private void OnIsEditingMidNpcChanged()
{
if (isEditingEnemyNpc && isEditingSelfNpc && isEditingMidNpc)
{
isEditingEnemyNpc = false;
isEditingSelfNpc = false;
}
}
private void ShowAllMidNpcId()
{
foreach (var npc in currentMidNpcInfo)
{
npc.isShowNpcID = isShowAllMidNpcID;
if (isShowAllMidNpcID)
{
if (!_showNpcInfos.Contains(npc))
{
_showNpcInfos.Add(npc);
}
}
else
{
_showNpcInfos.Remove(npc);
}
}
}
#endregion
#region 阻挡球编辑
/// <summary>
/// 球体根节点
/// </summary>
private GameObject blockSphereRoot;
/// <summary>
/// 已修改阻挡球字典
/// </summary>
private readonly Dictionary<int, MapData.BlockSphereInfo> blockSphereInfo = new();
/// <summary>
/// 被隐藏的物体列表,用于后续恢复显示
/// </summary>
private readonly List<GameObject> hideObjs = new List<GameObject>();
/// <summary>
/// 所有禁止攻击的格子
/// </summary>
private readonly List<int> banAttackCells = new();
/// <summary>
/// 标准六边形内接圆直径
/// </summary>
private static readonly float Calibre = 3f;
/// <summary>
/// 编辑时颜色
/// </summary>
private static readonly Color EditingSphereColor = Color.red;
/// <summary>
/// 已编辑过的数据颜色
/// </summary>
private static readonly Color ChangedSphereColor = Color.green;
[Serializable]
public class BlockSpereEditorInfo
{
[HideInInspector] public MapData.BlockSphereInfo blockSphereInfo;
#region 可视字段
[HorizontalGroup("1", 0.1f)] [LabelText("地格")] [LabelWidth(30)] [ReadOnly]
public int cellIndex;
[HorizontalGroup("1", 0.12f)]
[LabelText("缩放")]
[LabelWidth(30)]
[EnableIf("canEditor")]
[ShowIf("@isChanged||canEditor")]
[OnValueChanged("OnScaleValueChanged")]
public float scale = 1f;
[HorizontalGroup("1", 0.25f)]
[LabelText("偏移")]
[LabelWidth(30)]
[EnableIf("canEditor")]
[ShowIf("@isChanged||canEditor")]
[OnValueChanged("OnOffestValueChanged")]
public Vector2 offest;
[HorizontalGroup("1", 0.25f)]
[LabelText("备注")]
[LabelWidth(30)]
[EnableIf("canEditor")]
// [ShowIf("@isChanged||canEditor")]
[OnValueChanged("OnNoteValueChanged")]
public string note;
#endregion
#region 字段
/// <summary>
/// 可编辑
/// </summary>
[HideInInspector] public bool canEditor;
/// <summary>
/// 数据已经变化
/// </summary>
[HideInInspector] public bool isChanged;
/// <summary>
/// 阻挡球物体
/// </summary>
[HideInInspector] public GameObject sphere;
/// <summary>
/// 原始共享材质
/// </summary>
[HideInInspector] public Material originalSharedMaterial;
/// <summary>
/// 编辑时的临时材质
/// </summary>
[HideInInspector] public Material editingMaterial;
/// <summary>
/// 地格中心位置
/// </summary>
[HideInInspector] public Vector3 originalPos;
#endregion
#region 构造函数
public BlockSpereEditorInfo(int cellIndex, GameObject sphere)
{
var map = currWindow._curMap;
this.cellIndex = cellIndex;
blockSphereInfo = new MapData.BlockSphereInfo()
{
cellIndex = cellIndex,
};
originalPos = map.TGSCellIndex2WorldPosition(cellIndex);
if (sphere != null)
{
this.sphere = sphere;
SetSphereColor(Color.white);
}
isChanged = false;
}
public BlockSpereEditorInfo(MapData.BlockSphereInfo blockInfo, GameObject sphere)
{
var map = currWindow._curMap;
blockSphereInfo = new MapData.BlockSphereInfo
{
cellIndex = blockInfo.cellIndex,
scale = blockInfo.scale,
note = blockInfo.note,
offestPos = blockInfo.offestPos,
};
cellIndex = blockInfo.cellIndex;
originalPos = map.TGSCellIndex2WorldPosition(blockInfo.cellIndex);
offest = new Vector2(blockInfo.offestPos.x - originalPos.x, blockInfo.offestPos.z - originalPos.z);
scale = blockInfo.scale / Calibre;
if (sphere != null)
{
this.sphere = sphere;
}
UpdateChangedData();
}
#endregion
#region 按钮
[Button("恢复默认")]
[HorizontalGroup("1")]
[ShowIf("isChanged")]
public void Revert()
{
if (!isChanged) return;
isChanged = false;
if (sphere != null)
{
scale = 1f;
offest = new Vector2(0f, 0f);
blockSphereInfo.offestPos = originalPos;
blockSphereInfo.scale = Calibre;
sphere.transform.localScale = Vector3.one * Calibre;
sphere.transform.position = originalPos;
// 如果正在监听这个球体,更新监听的位置和缩放
if (currWindow.currentEditingSphere == this)
{
currWindow.lastPosition = originalPos;
currWindow.lastScale = Vector3.one * Calibre;
}
if (!canEditor)
{
SetSphereColor(Color.white);
}
}
}
[Button("编辑")]
[HorizontalGroup("1")]
[HideIf("canEditor")]
private void EditorBlockAttack()
{
canEditor = true;
if (sphere != null)
{
var renderer = sphere.GetComponent<Renderer>();
// 保存原始材质(可能已经是绿色的编辑过材质)
originalSharedMaterial = renderer.sharedMaterial;
// 创建编辑用的红色材质
editingMaterial = new Material(originalSharedMaterial);
editingMaterial.color = EditingSphereColor;
renderer.sharedMaterial = editingMaterial;
if (currWindow.currentEditingSphere != null)
{
currWindow.currentEditingSphere.ExitEditorBlockAttack();
}
Selection.activeGameObject = sphere;
currWindow.StartMonitoringSphere(this);
}
}
[Button("保存")]
[HorizontalGroup("1")]
[ShowIf("canEditor")]
public void ExitEditorBlockAttack()
{
canEditor = false;
if (sphere != null)
{
var renderer = sphere.GetComponent<Renderer>();
// 清理临时材质并恢复原始材质
if (editingMaterial != null)
{
DestroyImmediate(editingMaterial);
editingMaterial = null;
}
// 恢复原始材质
if (originalSharedMaterial != null)
{
renderer.sharedMaterial = originalSharedMaterial;
}
blockSphereInfo.scale = Calibre * scale;
blockSphereInfo.offestPos = sphere.transform.position;
DebugUtil.Log(
$"保存 {blockSphereInfo.cellIndex} 上的阻挡攻击球体真实直径为: {blockSphereInfo.scale}, 具体位置为: {blockSphereInfo.offestPos}, " +
$"原始的位置是:{originalPos}, 原始直径是:{Calibre}");
UpdateChangedData();
}
// 停止监听Transform变化
currWindow.StopMonitoringSphere();
}
#endregion
#region 值变化
private void OnScaleValueChanged()
{
var realScale = Calibre * scale;
if (sphere != null)
{
sphere.transform.localScale = Vector3.one * realScale;
}
UpdateChangedData();
}
private void OnOffestValueChanged()
{
var realOffestX = offest.x + originalPos.x;
var realOffestZ = offest.y + originalPos.z;
var newPos = new Vector3(realOffestX, 0f, realOffestZ);
if (sphere != null)
{
sphere.transform.position = newPos;
}
UpdateChangedData();
}
private void OnNoteValueChanged()
{
blockSphereInfo.note = note;
}
public void UpdateChangedData()
{
isChanged = offest != Vector2.zero ||
!Mathf.Approximately(1f, scale);
// 如果状态发生变化且不在编辑状态,更新球体颜色
if (!canEditor && sphere != null)
{
SetSphereColor(isChanged ? ChangedSphereColor : Color.white);
}
}
/// <summary>
/// 设置球体颜色
/// </summary>
private void SetSphereColor(Color color)
{
if (sphere == null) return;
var renderer = sphere.GetComponent<Renderer>();
if (renderer != null)
{
if (!canEditor)
{
if (renderer.sharedMaterial != null)
{
var material = new Material(renderer.sharedMaterial);
material.color = color;
renderer.sharedMaterial = material;
}
}
}
}
#endregion
}
[BoxGroup("攻击阻挡球")]
[HorizontalGroup("攻击阻挡球/Block")]
[LabelText("编辑攻击阻挡球"), Indent]
[OnValueChanged("OnCanEditorBlockSphereChanged")]
[LabelWidth(100)]
[SerializeField]
private bool canEditorBlockSphere;
private void OnCanEditorBlockSphereChanged()
{
if (canEditorBlockSphere)
{
showBuildings = false;
HideObjectsExcept();
blockSphereEditorList.Clear();
UpdateBlockSphereInfo();
foreach (var cellIndex in banAttackCells)
{
if (blockSphereInfo.TryGetValue(cellIndex, out var sphereInfo))
{
var sphere = CreateSphere(cellIndex, sphereInfo.offestPos, Vector3.one * sphereInfo.scale);
var blockEditorInfo = new BlockSpereEditorInfo(sphereInfo, sphere);
blockSphereEditorList.Add(blockEditorInfo);
}
else
{
var pos = _curMap.TGSCellIndex2WorldPosition(cellIndex);
var sphere = CreateSphere(cellIndex, pos, Vector3.one * Calibre);
var blockEditorInfo = new BlockSpereEditorInfo(cellIndex, sphere);
blockSphereEditorList.Add(blockEditorInfo);
}
}
editingBlockSphereList.Clear();
foreach (var editorInfo in blockSphereEditorList)
{
if (editorInfo.isChanged)
{
editingBlockSphereList.Add(editorInfo);
}
}
}
else
{
// 清理所有临时材质
foreach (var editorInfo in blockSphereEditorList)
{
if (editorInfo.editingMaterial != null)
{
if (editorInfo.sphere != null)
{
var renderer = editorInfo.sphere.GetComponent<Renderer>();
if (renderer != null)
{
renderer.sharedMaterial = editorInfo.originalSharedMaterial;
}
}
DestroyImmediate(editorInfo.editingMaterial);
}
}
showBuildings = true;
ShowHiddenObjects();
DestroyBlockSphere();
}
}
/// <summary>
/// 更新阻挡球信息
/// </summary>
private void UpdateBlockSphereInfo()
{
blockSphereInfo.Clear();
GetAllBanAttackCell();
foreach (var blockInfo in currentLevelData.BlockSphereInfos)
{
blockSphereInfo[blockInfo.cellIndex] = blockInfo;
}
}
/// <summary>
/// 获取所有禁止攻击地格
/// </summary>
private void GetAllBanAttackCell()
{
banAttackCells.Clear();
GetAllBanAttackBuildings();
}
/// <summary>
/// 获取所有禁止攻击建筑地格
/// </summary>
private void GetAllBanAttackBuildings()
{
AddBanAttackBuildings(currentEnemyNpcInfo);
AddBanAttackBuildings(currentMidNpcInfo);
AddBanAttackBuildings(currentSelfNpcInfo);
}
/// <summary>
/// 获取禁止攻击建筑地格
/// </summary>
private void AddBanAttackBuildings(List<NpcEditorInfo> npcList)
{
foreach (var npcEditorInfo in npcList)
{
if (!MonsterCfg.DataMap.TryGetValue(npcEditorInfo.monsterId, out var dataMonster)) continue;
if (dataMonster.NpcType != EMonsterType.FightBuild) continue;
if (!FightBuildCfg.DataMap.TryGetValue(dataMonster.MonsterClassID, out var dataFight)) continue;
if (!dataFight.IsReplaceBlockAttack || !dataFight.BlockAttack) continue;
var cellIndex = _curMap.BlockPosition2TGSCellIndex(npcEditorInfo.npcInfo.position);
if (!banAttackCells.Contains(cellIndex))
banAttackCells.Add(cellIndex);
DebugUtil.Log($"能阻挡攻击的建筑{dataMonster.MonsterClassID}所在地格是{cellIndex}");
}
}
[LabelText("展示建筑")]
[HorizontalGroup("攻击阻挡球/BlockBuildings"), Indent]
[ShowIf("canEditorBlockSphere")]
[OnValueChanged("OnShowBuildingsChanged")]
public bool showBuildings;
private void OnShowBuildingsChanged()
{
if (showBuildings)
{
ShowHiddenObjects();
}
else
{
HideObjectsExcept();
}
}
[LabelText("添加地格")] [HorizontalGroup("攻击阻挡球/新数据"), Indent] [ShowIf("canEditorBlockSphere")]
public int addCellIndex;
[Button("添加编辑")]
[HorizontalGroup("攻击阻挡球/新数据/Add"), Indent]
[ShowIf("canEditorBlockSphere")]
public void AddBlockSphere()
{
if (!banAttackCells.Contains(addCellIndex))
{
EditorUtility.DisplayDialog("错误", $"地格 {addCellIndex} 不是攻击阻挡地格!", "确定");
return;
}
foreach (var blockSpereEditor in editingBlockSphereList)
{
if (blockSpereEditor.cellIndex == addCellIndex)
{
return;
}
}
foreach (var blockSpereEditor in blockSphereEditorList)
{
if (blockSpereEditor.cellIndex == addCellIndex)
{
editingBlockSphereList.Add(blockSpereEditor);
break;
}
}
}
[LabelText("阻挡球列表")]
[HorizontalGroup("攻击阻挡球/EditList"), Indent]
[ShowIf("canEditorBlockSphere")]
[ListDrawerSettings(HideAddButton = true, CustomRemoveIndexFunction = "CustomRemoveBlockSphereIndex")]
public List<BlockSpereEditorInfo> editingBlockSphereList = new();
[HideInInspector] public List<BlockSpereEditorInfo> blockSphereEditorList = new();
private void CustomRemoveBlockSphereIndex(int index)
{
if (index < editingBlockSphereList.Count)
{
editingBlockSphereList[index].Revert();
DebugUtil.Log($"删除并还原{editingBlockSphereList[index].cellIndex}的阻挡球编辑数据");
editingBlockSphereList.RemoveAt(index);
}
}
/// <summary>
/// 创建球体
/// </summary>
private GameObject CreateSphere(int cellIndex, Vector3 pos, Vector3 scale)
{
var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.position = pos;
sphere.transform.localScale = scale;
if (blockSphereRoot == null)
blockSphereRoot = new GameObject("blockSphereRoot");
sphere.transform.SetParent(blockSphereRoot.transform);
sphere.name = "sphere" + cellIndex;
return sphere;
}
private void DestroyBlockSphere()
{
DestroyImmediate(blockSphereRoot);
}
/// <summary>
/// 保存数据
/// </summary>
private void SaveBlockSphereInfo()
{
currentLevelData.BlockSphereInfos.Clear();
foreach (var blockSpereEditor in editingBlockSphereList)
{
currentLevelData.BlockSphereInfos.Add(blockSpereEditor.blockSphereInfo);
}
}
/// <summary>
/// 加载数据
/// </summary>
private void LoadBlockSphereInfo()
{
blockSphereInfo.Clear();
foreach (var blockInfo in currentLevelData.BlockSphereInfos)
{
blockSphereInfo[blockInfo.cellIndex] = blockInfo;
}
}
#region Sphere物体编辑器监听
/// <summary>
/// 当前正在编辑的球体信息
/// </summary>
[HideInInspector] public BlockSpereEditorInfo currentEditingSphere;
/// <summary>
/// 上次记录的位置
/// </summary>
[HideInInspector] public Vector3 lastPosition;
/// <summary>
/// 上次记录的缩放
/// </summary>
[HideInInspector] public Vector3 lastScale;
/// <summary>
/// 开始监听球体Transform变化
/// </summary>
private void StartMonitoringSphere(BlockSpereEditorInfo editorInfo)
{
currentEditingSphere = editorInfo;
if (editorInfo.sphere != null)
{
lastPosition = editorInfo.sphere.transform.position;
lastScale = editorInfo.sphere.transform.localScale;
// 注册编辑器更新回调
EditorApplication.update += OnEditorUpdate;
}
}
/// <summary>
/// 停止监听球体Transform变化
/// </summary>
private void StopMonitoringSphere()
{
currentEditingSphere = null;
EditorApplication.update -= OnEditorUpdate;
}
/// <summary>
/// 编辑器更新回调
/// </summary>
private void OnEditorUpdate()
{
if (currentEditingSphere?.sphere == null || !currentEditingSphere.canEditor)
{
StopMonitoringSphere();
return;
}
var sphere = currentEditingSphere.sphere;
var transform = sphere.transform;
// 检查位置变化
if (Vector3.Distance(transform.position, lastPosition) > 0.001f)
{
// 锁定Y轴
var newPos = transform.position;
newPos.y = 0f;
transform.position = newPos;
// 更新偏移值
var offsetX = newPos.x - currentEditingSphere.originalPos.x;
var offsetZ = newPos.z - currentEditingSphere.originalPos.z;
currentEditingSphere.offest = new Vector2(offsetX, offsetZ);
lastPosition = newPos;
// 标记为已修改
currentEditingSphere.UpdateChangedData();
}
// 检查缩放变化
if (Vector3.Distance(transform.localScale, lastScale) > 0.001f)
{
var newScale = transform.localScale.x;
transform.localScale = Vector3.one * newScale;
currentEditingSphere.scale = newScale / Calibre;
lastScale = transform.localScale;
currentEditingSphere.UpdateChangedData();
}
}
#endregion
#region 物体隐藏/显示管理
/// <summary>
/// 需要隐藏的物体节点名字
/// </summary>
private readonly List<string> hideObjsRoot = new()
{
"Art", "BackGround", "Buildings", "enemyObjRoot", "midObjRoot", "selfObjRoot"
};
/// <summary>
/// 不需要隐藏的物体节点名字
/// </summary>
private readonly List<string> noHideObjsRoot = new()
{
"Ground", "Ground2", "Lights", "Fog", "Global Volume", "Effects"
};
/// <summary>
/// 隐藏指定节点下的物体(排除白名单中的物体)
/// </summary>
public void HideObjectsExcept()
{
if (hideObjsRoot == null || hideObjsRoot.Count == 0)
{
return;
}
// 清空之前的隐藏物体列表
hideObjs.Clear();
foreach (string nodeName in hideObjsRoot)
{
GameObject node = GameObject.Find(nodeName);
if (node == null)
{
continue;
}
HideChildrenRecursively(node.transform, noHideObjsRoot);
}
}
/// <summary>
/// 递归隐藏子物体
/// </summary>
/// <param name="parent">父级Transform</param>
/// <param name="excludeNames">排除的物体名列表</param>
private void HideChildrenRecursively(Transform parent, List<string> excludeNames)
{
for (int i = 0; i < parent.childCount; i++)
{
Transform child = parent.GetChild(i);
GameObject childObj = child.gameObject;
// 检查是否在排除列表中(完全匹配)
bool shouldExclude = false;
if (excludeNames != null)
{
foreach (string excludeName in excludeNames)
{
if (childObj.name.Equals(excludeName, System.StringComparison.OrdinalIgnoreCase))
{
shouldExclude = true;
break;
}
}
}
if (shouldExclude)
{
continue;
}
else
{
// 不在排除列表中,隐藏此物体
if (childObj.activeSelf)
{
childObj.SetActive(false);
hideObjs.Add(childObj);
}
// 继续递归处理子物体
HideChildrenRecursively(child, excludeNames);
}
}
}
/// <summary>
/// 显示之前隐藏的所有物体
/// </summary>
private void ShowHiddenObjects()
{
if (hideObjs == null || hideObjs.Count == 0)
{
return;
}
for (int i = hideObjs.Count - 1; i >= 0; i--)
{
GameObject obj = hideObjs[i];
// 检查物体是否仍然存在(可能已被删除)
if (obj != null)
{
obj.SetActive(true);
}
hideObjs.RemoveAt(i);
}
}
#endregion
#endregion
#region 保存
[Button("保存当前关卡"), Indent]
private void SaveCurrentLevel()
{
CheckCorrectLevel();
// 地区
currentLevelData.preparingRegionId = curRegionData.preparingRegionId;
currentLevelData.fallbackRegionId = curRegionData.fallbackRegionId;
currentLevelData.enemyFallbackRegionId = curRegionData.enemyFallbackRegionId;
currentLevelData.preparingRegionToward = curRegionData.preparingRegionToward;
currentLevelData.pvpDefendRegionId = curRegionData.pvpDefendRegionId;
currentLevelData.pvpDefendToward = curRegionData.pvpDefendToward;
#region 敌方NPC
foreach (var npcEditor in currentEnemyNpcInfo)
{
npcEditor.npcInfo.index = npcEditor.id;
}
currentLevelData.npcInfos = currentEnemyNpcInfo.Select(editorNpcInfo => editorNpcInfo.npcInfo).ToList();
#endregion
#region 己方NPC
foreach (var npcFightEditorInfo in currentSelfNpcInfo)
{
npcFightEditorInfo.npcInfo.index = npcFightEditorInfo.id;
}
currentLevelData.selfNpcInfos =
currentSelfNpcInfo.Select(editorFightNpcInfo => editorFightNpcInfo.npcInfo).ToList();
#endregion
#region 中立NPC
foreach (var npcEditor in currentMidNpcInfo)
{
npcEditor.npcInfo.index = npcEditor.id;
}
currentLevelData.midNpcInfos = currentMidNpcInfo.Select(editorNpcInfo => editorNpcInfo.npcInfo).ToList();
#endregion
// 占领数据
currentLevelData.CapturePoints.Clear();
foreach (var captureInfo in captureEditorInfo)
{
var capture = new LevelData.CaptureInfo
{
CapturePoint = _curMap.BlockPosition2TGSCellIndex(captureInfo.capturePoint),
CaptureProgress = captureInfo.captureProgress,
};
currentLevelData.CapturePoints.Add(capture);
}
// 扛旗数据
currentLevelData.flagStartPoints.Clear();
currentLevelData.flagEndPoints.Clear();
foreach (var flag in flagEditorInfo.flagStartPoints)
{
var cellIndex = _curMap.BlockPosition2TGSCellIndex(flag);
currentLevelData.flagStartPoints.Add(cellIndex);
}
foreach (var flag in flagEditorInfo.flagEndPoints)
{
var cellIndex = _curMap.BlockPosition2TGSCellIndex(flag);
currentLevelData.flagEndPoints.Add(cellIndex);
}
// 撤离数据
currentLevelData.fallbackSinglePoints.Clear();
currentLevelData.fallbackReusablePoints.Clear();
foreach (var fallback in fallbackEditorInfo.fallbackSinglePoints)
{
var cellIndex = _curMap.BlockPosition2TGSCellIndex(fallback);
currentLevelData.fallbackSinglePoints.Add(cellIndex);
}
foreach (var fallback in fallbackEditorInfo.fallbackReusablePoints)
{
var cellIndex = _curMap.BlockPosition2TGSCellIndex(fallback);
currentLevelData.fallbackReusablePoints.Add(cellIndex);
}
// 刷新点位
currentLevelData.refreshPoints.Clear();
currentLevelData.refreshPoints.AddRange(refreshPoints);
// 指定路径
currentLevelData.patrolRoadData = roadEditorInfos.Select(editorInfo => new PatrolData
{
id = editorInfo.roadID,
patrolInfos = editorInfo.roadPoints,
}).ToList();
// 指定区域
currentLevelData.patrolRegionData = areaEditorInfos.Select(editorInfo => new PatrolData
{
id = editorInfo.areaID,
patrolInfos = editorInfo.areaPoints,
}).ToList();
// 关卡进度
currentLevelData.progressGuideDatas = progressGuideDatas;
// 立标数据
currentLevelData.setFlagGuideDatas = setFlagEditorInfos.Select(x => x.flagGuideData).ToList();
// 信息板数据
currentLevelData.infoPanelGuideDatas = infoPanelGuideDatas;
currentLevelData.listAIBroadcastData = listAIBroadcastData;
// 处理条件(击杀对象列表)
ProcessCondition();
// 阻挡球
SaveBlockSphereInfo();
_levelEditor.SaveLevelData();
_mapManager.SaveMapData();
}
private void ProcessCondition()
{
foreach (var condition in currentLevelData.setFlagGuideDatas)
{
ProcessCondition(condition.triggerCondition);
ProcessCondition(condition.finishCondition);
}
foreach (var condition in currentLevelData.progressGuideDatas)
{
ProcessCondition(condition.triggerCondition);
ProcessCondition(condition.finishCondition);
}
foreach (var condition in currentLevelData.infoPanelGuideDatas)
{
ProcessCondition(condition.triggerCondition);
ProcessCondition(condition.finishCondition);
}
foreach (var condition in currentLevelData.listAIBroadcastData)
{
ProcessCondition(condition.triggerCondition);
}
ProcessConditionForList(currentLevelData.starTarget);
ProcessConditionForList(currentLevelData.scoreTarget);
}
private void ProcessConditionForList(IEnumerable<ConditionModifier> conditionModifiers)
{
foreach (var conditionModifier in conditionModifiers)
{
ProcessCondition(conditionModifier);
}
}
private void ProcessCondition(ConditionModifier conditionModifier)
{
// 击杀条件特殊处理
if (conditionModifier.conditionType == ConditionType.CombatKillEnemy)
{
conditionModifier.conditionArgs.Clear();
foreach (var enemy in conditionModifier.killEnemyIDList)
{
conditionModifier.conditionArgs.Add(new Param("", enemy, ""));
}
}
else if (conditionModifier.conditionType == ConditionType.CombatInTargetGridByJob)
{
conditionModifier.conditionArgs.Clear();
conditionModifier.conditionArgs.Add(new Param("职业", conditionModifier.job,
typeof(cfg.CharacterCfg.Ejob).FullName));
conditionModifier.conditionArgs.Add(new Param("是否路过", conditionModifier.isPass, ""));
foreach (var unitID in conditionModifier.unitIDList)
{
conditionModifier.conditionArgs.Add(new Param("地格位置", unitID, ""));
}
}
else if (conditionModifier.conditionType == ConditionType.CombatInTargetGridByCharacterId)
{
conditionModifier.conditionArgs.Clear();
conditionModifier.conditionArgs.Add(new Param("角色ID", conditionModifier.roleID, ""));
conditionModifier.conditionArgs.Add(new Param("是否路过", conditionModifier.isPass, ""));
foreach (var unitID in conditionModifier.unitIDList)
{
conditionModifier.conditionArgs.Add(new Param("地格位置", unitID, ""));
}
}
// 任意角色在目标位置特殊处理
else if (conditionModifier.conditionType == ConditionType.CombatInTargetGrid)
{
conditionModifier.conditionArgs.Clear();
conditionModifier.conditionArgs.Add(new Param("是否路过", conditionModifier.isPass, ""));
foreach (var unitID in conditionModifier.unitIDList)
{
conditionModifier.conditionArgs.Add(new Param("地格位置", unitID, ""));
}
}
// 撤退数量特殊处理
else if (conditionModifier.conditionType == ConditionType.CombatFallBack &&
conditionModifier.conditionArgs.Count > 0)
{
currentLevelData.fallBackCount = (int)conditionModifier.conditionArgs[0].Arg;
}
}
#endregion
#region 关卡ID相关
/// <summary>
/// 关卡ID 通过关卡数据 json文件名 获得
/// </summary>
[LabelText("手动指定关卡ID"), Indent, LabelWidth(100), ShowIf("needManualInputLevelID")]
[OnValueChanged("GetLevelMonsterClass")]
[InfoBox("无法正确获得关卡ID请手动指定")]
[SerializeField]
public string levelID;
/// <summary>
/// 需要手动输入关卡ID
/// </summary>
[HideInInspector] public bool needManualInputLevelID;
/*
/// <summary>
/// 获取关卡怪物列表
/// </summary>
public void GetLevelMonsterClass()
{
if (!int.TryParse(levelID, out int levelIndex) ||
!LevelCfg.DataMap.TryGetValue(levelIndex, out _))
{
needManualInputLevelID = true;
DebugUtil.LogError("无对应的关卡ID请手动输入关卡ID");
curEnemyMonsterID = 0;
enemyNpcObject = null;
return;
}
enemyMonsterIDs = new List<int>();
selfMonsterIDs = new List<int>();
midMonsterIDs = new List<int>();
foreach (var monster in MonsterCfg.DataMap)
{
string strID = monster.Key.ToString();
string tempID = strID[..6];
// 关卡ID相同
if (!tempID.Equals(levelID))
continue;
switch (monster.Value.NpcType)
{
case EMonsterType.EnemyCharacter:
{
enemyMonsterIDs.Add(monster.Key);
}
break;
case EMonsterType.SelfCharacter:
{
selfMonsterIDs.Add(monster.Key);
}
break;
case EMonsterType.Vehicle:
case EMonsterType.FightBuild:
{
midMonsterIDs.Add(monster.Key);
enemyMonsterIDs.Add(monster.Key);
selfMonsterIDs.Add(monster.Key);
}
break;
default:
DebugUtil.LogError($"未处理类型:{monster.Value.NpcType}");
break;
}
}
if (enemyMonsterIDs.Count <= 0 && midMonsterIDs.Count <= 0)
DebugUtil.LogError($"请检查Monster表没有{levelID}关卡的怪物数据");
}
*/
#endregion
#region 加载
private void LoadLevel(LevelData levelData)
{
DestroyNpcPrefab();
_enemyNpcObjectWaveDic.Clear();
_midNpcObjectWaveDic.Clear();
_showNpcInfos.Clear();
_selfNpcObjectDic.Clear();
currentLevelData = levelData;
//加载地区信息
curRegionData = new RegionData
{
preparingRegionId = currentLevelData.preparingRegionId,
fallbackRegionId = currentLevelData.fallbackRegionId,
enemyFallbackRegionId = currentLevelData.enemyFallbackRegionId,
preparingRegionToward = currentLevelData.preparingRegionToward,
pvpDefendRegionId = currentLevelData.pvpDefendRegionId,
pvpDefendToward = currentLevelData.pvpDefendToward
};
_mapManager = FindObjectOfType<MapManager>();
_curMap = _mapManager.Map;
_curMap.TerrainGridSystem.OnMouseClickOnGrid += OnCellClick;
// 加载占领数据
captureEditorInfo = new List<CaptureEditorInfo>();
foreach (var captureInfo in currentLevelData.CapturePoints)
{
var captureEditor = new CaptureEditorInfo()
{
capturePoint = _curMap.TGSCellIndex2BlockPosition(captureInfo.CapturePoint),
captureProgress = captureInfo.CaptureProgress,
};
captureEditorInfo.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);
}
// 加载撤离数据
fallbackEditorInfo = new FallbackEditorInfo();
foreach (var fallback in currentLevelData.fallbackSinglePoints)
{
var pos = _curMap.TGSCellIndex2BlockPosition(fallback);
fallbackEditorInfo.fallbackSinglePoints.Add(pos);
}
foreach (var fallback in currentLevelData.fallbackReusablePoints)
{
var pos = _curMap.TGSCellIndex2BlockPosition(fallback);
fallbackEditorInfo.fallbackReusablePoints.Add(pos);
}
// 加载刷新点位
refreshPoints = new List<int>();
refreshPoints.AddRange(currentLevelData.refreshPoints);
// 加载指定路线
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;
// 加载关卡引导立标数据
setFlagEditorInfos = currentLevelData.setFlagGuideDatas.Select(x => new SetFlagGuideEditorInfo(x)).ToList();
// 加载信息板数据
infoPanelGuideDatas = currentLevelData.infoPanelGuideDatas;
listAIBroadcastData = currentLevelData.listAIBroadcastData;
// 加载敌方NPC到场景中
LoadEnemyNpcInfo();
// 加载中立NPC到场景中
LoadMidNpcInfo();
// 加载己方NPC到场景中
LoadSelfNpcInfo();
// 加载阻挡球
LoadBlockSphereInfo();
}
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,
ETroopsId.Enemy);
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>
{ { npcEditor, obj } };
_enemyNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
}
}
index++;
}
}
private void LoadMidNpcInfo()
{
var monsterConfigDic = MonsterCfg.DataMap;
currentMidNpcInfo = new();
var index = 1;
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,
ETroopsId.Neutral);
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,
monsterClassData.AIType, ETroopsId.Self);
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 bool ResponseClickOnCell =>
isEditingEnemyNpc || isEditingSelfNpc || isEditingMidNpc || isEditingNpcPos || isEditingFlagStart ||
isEditingFlagEnd || isEditingCapture ||
isEditingFallbackReusable || isEditingFallbackSingle || isEditingRefreshID || isEditingRoadInfo ||
isEditingAreaInfo;
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 (isEditingFallbackSingle)
{
EditingFallBack(clickPosition, true, opTrue);
}
else if (isEditingFallbackReusable)
{
EditingFallBack(clickPosition, false, opTrue);
}
else if (isEditingCapture)
{
EditingCapture(clickPosition, 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 (isEditingRefreshID)
{
EditRefreshID(cellindex, opTrue);
}
else if (isEditingRoadInfo && isEditingRoad)
{
EditRoad(cellindex, opTrue);
}
else if (isEditingAreaInfo && isEditingArea)
{
EditArea(cellindex, opTrue);
}
}
private void EditingFallBack(Vector2Int clickPosition, bool isEditingSingle, bool isAdd)
{
if (isEditingSingle)
{
if (isAdd && !fallbackEditorInfo.fallbackSinglePoints.Contains(clickPosition))
{
DebugUtil.Log("单次撤离点添加: {0}", clickPosition);
fallbackEditorInfo.fallbackSinglePoints.Add(clickPosition);
}
else if (!isAdd && fallbackEditorInfo.fallbackSinglePoints.Contains(clickPosition))
{
DebugUtil.Log("单次撤离点移除: {0}", clickPosition);
fallbackEditorInfo.fallbackSinglePoints.Remove(clickPosition);
}
}
else
{
if (isAdd && !fallbackEditorInfo.fallbackReusablePoints.Contains(clickPosition))
{
DebugUtil.Log("复用撤离点添加: {0}", clickPosition);
fallbackEditorInfo.fallbackReusablePoints.Add(clickPosition);
}
else if (!isAdd && fallbackEditorInfo.fallbackReusablePoints.Contains(clickPosition))
{
DebugUtil.Log("复用撤离点移除: {0}", clickPosition);
fallbackEditorInfo.fallbackReusablePoints.Remove(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(Vector2Int clickPosition, bool isAdd)
{
var isExist = false;
CaptureEditorInfo existCapture = null;
foreach (var capture in captureEditorInfo)
{
if (capture.capturePoint == clickPosition)
{
isExist = true;
existCapture = capture;
}
}
if (isAdd)
{
if (!isExist)
{
DebugUtil.Log("占领点添加: {0}", clickPosition);
captureEditorInfo.Add(new CaptureEditorInfo()
{
capturePoint = clickPosition
});
}
}
else
{
if (existCapture != null)
{
DebugUtil.Log("占领点移除: {0}", clickPosition);
captureEditorInfo.Remove(existCapture);
}
}
}
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
{
RemoveEnemyNpc(clickPosition);
}
}
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;
var monsterConfigDic = MonsterCfg.DataMap;
if (monsterConfigDic.TryGetValue(curEnemyMonsterID, out var monster))
{
level = monster.MonsterLevel;
aiType = monster.AIType;
}
else
{
DebugUtil.LogError("获取怪物[{0}]配置数据错误", curEnemyMonsterID);
}
var npcEditor = new NpcEditorInfo(npcInfo, currentEnemyNpcInfo.Count + 1, level, aiType, ETroopsId.Enemy);
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);
npcObjectDic.Add(npcEditor, obj);
}
else
{
var obj = Instantiate(enemyNpcObject, pos, enemyNpcObject.transform.rotation);
npcObjectDic = new Dictionary<NpcEditorInfo, GameObject>
{ { npcEditor, obj } };
_enemyNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
}
DebugUtil.Log(
$"添加了第[ {npcInfo.waveIndex} ]波的[ {npcInfo.index} ]怪物到 [ {npcInfo.position} ]位置上, 其预制体为: {enemyNpcObject.name}");
}
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();
}
#endregion
#region 已放NPC编辑方法
private void EditSelfNpc(Vector2Int clickPosition, bool isAdd)
{
if (isAdd)
{
AddSelfNpc(clickPosition);
}
else
{
RemoveSelfNpc(clickPosition);
}
}
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;
var monsterConfigDic = MonsterCfg.DataMap;
if (monsterConfigDic.TryGetValue(curSelfNpcID, out var monster))
{
level = monster.MonsterLevel;
aiType = monster.AIType;
}
else
{
DebugUtil.LogError("获取己方Npc[{0}]配置数据错误", curSelfNpcID);
}
currentSelfNpcInfo.Add(new NpcEditorInfo(npcInfo, SelfID + currentSelfNpcInfo.Count + 1, level, aiType,
ETroopsId.Self));
var pos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
_selfNpcObjectDic.Add(npcInfo.position, Instantiate(selfNpcObject, pos, selfNpcObject.transform.rotation));
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
RemoveMidNpc(clickPosition);
}
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;
var monsterConfigDic = MonsterCfg.DataMap;
if (monsterConfigDic.TryGetValue(curMidMonsterID, out var monster))
{
level = monster.MonsterLevel;
aiType = monster.AIType;
}
else
{
DebugUtil.LogError("获取怪物[{0}]配置数据错误", curMidMonsterID);
}
var npcEditor = new NpcEditorInfo(npcInfo, currentMidNpcInfo.Count + 1, level, aiType, ETroopsId.Neutral);
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);
npcObjectDic.Add(npcEditor, obj);
}
else
{
var obj = Instantiate(midNpcObject, pos, midNpcObject.transform.rotation);
npcObjectDic = new Dictionary<NpcEditorInfo, GameObject>
{ { npcEditor, obj } };
_midNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
}
DebugUtil.Log(
$"添加了第[ {npcInfo.waveIndex} ]波的[ {npcInfo.index} ]怪物到 [ {npcInfo.position} ]位置上, 其预制体为: {midNpcObject.name}");
}
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(
$"从[ {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);
}
currentMidNpcInfo.Remove(npc);
}
ReorderMidNpcID();
}
#endregion
private void EditRefreshID(int clickPosition, bool isAdd)
{
if (isAdd && !refreshPoints.Contains(clickPosition))
{
refreshPoints.Add(clickPosition);
_curMap.TerrainGridSystem.CellToggleRegionSurface(clickPosition, true, RegionColor);
}
else if (!isAdd && refreshPoints.Contains(clickPosition))
{
refreshPoints.Remove(clickPosition);
_curMap.TerrainGridSystem.CellHideRegionSurface(clickPosition);
}
}
/// <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 敌方Npc某些值改变时响应事件
private void ReorderEnemyNpcID()
{
for (var i = 0; i < currentEnemyNpcInfo.Count; i++)
{
currentEnemyNpcInfo[i].id = i + 1;
currentEnemyNpcInfo[i].npcInfo.index = currentEnemyNpcInfo[i].id;
}
}
private void ReorderMidNpcID()
{
for (var i = 0; i < currentMidNpcInfo.Count; i++)
{
currentMidNpcInfo[i].id = i + 1;
currentMidNpcInfo[i].npcInfo.index = currentMidNpcInfo[i].id;
}
}
private void ReorderSelfNpcID()
{
for (var i = 0; i < currentSelfNpcInfo.Count; i++)
{
currentSelfNpcInfo[i].id = SelfID + i + 1;
}
}
private void ChangeEnemyNpcID(NpcEditorInfo npcEditorInfo, int newMonsterID)
{
if (_enemyNpcObjectWaveDic.TryGetValue(npcEditorInfo.npcInfo.waveIndex, out var npcObjectDic))
{
if (npcObjectDic.TryGetValue(npcEditorInfo, out var npcObj))
{
var newObj = LoadEnemyNpcPrefab(newMonsterID);
if (newObj != null)
{
var obj = Instantiate(newObj, npcObj.transform.position, npcObj.transform.rotation);
if (enemyObjRoot == null)
enemyObjRoot = new GameObject("enemyObjRoot");
if (obj != null)
obj.transform.SetParent(enemyObjRoot.transform);
npcEditorInfo.npcInfo.id = newMonsterID;
npcEditorInfo.level = MonsterCfg.DataMap[newMonsterID].MonsterLevel;
npcObjectDic[npcEditorInfo] = obj;
DestroyImmediate(npcObj);
DebugUtil.Log(
$"从[ {npcEditorInfo.npcInfo.position} ]的怪物ID改为:[ {npcEditorInfo.npcInfo.id} ]");
}
}
}
}
private void ChangeMidNpcID(NpcEditorInfo npcEditorInfo, int newMonsterID)
{
if (_midNpcObjectWaveDic.TryGetValue(npcEditorInfo.npcInfo.waveIndex, out var npcObjectDic))
{
if (npcObjectDic.TryGetValue(npcEditorInfo, out var npcObj))
{
var newObj = LoadMidNpcPrefab(newMonsterID);
if (newObj != null)
{
var obj = Instantiate(newObj, npcObj.transform.position, npcObj.transform.rotation);
if (midObjRoot == null)
midObjRoot = new GameObject("midObjRoot");
if (obj != null)
obj.transform.SetParent(midObjRoot.transform);
npcEditorInfo.npcInfo.id = newMonsterID;
npcEditorInfo.level = MonsterCfg.DataMap[newMonsterID].MonsterLevel;
npcObjectDic[npcEditorInfo] = obj;
DestroyImmediate(npcObj);
DebugUtil.Log(
$"从[ {npcEditorInfo.npcInfo.position} ]的怪物ID改为:[ {npcEditorInfo.npcInfo.id} ]");
}
}
}
}
private void ChangeSelfNpcID(NPCInfo npcInfoInfo, int newMonsterID)
{
if (_selfNpcObjectDic.TryGetValue(npcInfoInfo.position, out var npcObj))
{
var newObj = LoadSelfNpcPrefab(newMonsterID);
if (newObj != null)
{
var obj = Instantiate(newObj, npcObj.transform.position, npcObj.transform.rotation);
if (selfObjRoot == null)
selfObjRoot = new GameObject("selfObjRoot");
if (obj != null)
obj.transform.SetParent(selfObjRoot.transform);
npcInfoInfo.id = newMonsterID;
_selfNpcObjectDic[npcInfoInfo.position] = obj;
DestroyImmediate(npcObj);
DebugUtil.Log("位置上[ {0} ]的fight怪物ID改为: [ {1} ] ", npcInfoInfo.position, npcInfoInfo.id);
}
}
}
#endregion
#region 检查关卡
private const string PreparingRegionErrorMessage = "关卡出战区设置错误,该地图没有:<{0}>号地区。\n";
private const string FallbackRegionErrorMessage = "关卡撤退区设置错误,该地图没有:<{0}>号地区。\n";
private const string EnemyFallbackRegionErrorMessage = "关卡敌方撤退区设置错误,该地图没有:<{0}>号地区。\n";
private const string FixedMessage = "已将错误区域设置为默认<0>号地区。";
private const string CaptureErrorMessage = "占领条件参数大于配置的占领区域。";
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 (!_curMap.MapData.IsRegionExist(curRegionData.fallbackRegionId))
{
errorMessage += string.Format(FallbackRegionErrorMessage,
curRegionData.fallbackRegionId);
curRegionData.fallbackRegionId = 0;
}
if (!_curMap.MapData.IsRegionExist(curRegionData.enemyFallbackRegionId))
{
errorMessage += string.Format(EnemyFallbackRegionErrorMessage,
curRegionData.enemyFallbackRegionId);
curRegionData.enemyFallbackRegionId = 0;
}
// 检查占领条件相关
/*
if (!CheckLevelCondition())
{
CustomPopUpWindow.PopUp(CaptureErrorMessage);
}
*/
if (!string.IsNullOrEmpty(errorMessage))
{
CustomPopUpWindow.PopUp(errorMessage + FixedMessage);
}
}
/*private bool CheckLevelCondition()
{
if (currentLevelData.stages.Any(stage => stage.successCondition.conditionType == ConditionType.CombatLeastOccupy
&& stage.successCondition.conditionArgs[0].Arg >
captureEditorInfo.Count))
{
return false;
}
if (currentLevelData.starTarget.Any(modifier => modifier.conditionType == ConditionType.CombatLeastOccupy
&& modifier.conditionArgs[0].Arg > captureEditorInfo.Count))
{
return false;
}
if (currentLevelData.scoreTarget.Any(modifier => modifier.conditionType == ConditionType.CombatLeastOccupy
&& modifier.conditionArgs[0].Arg > captureEditorInfo.Count))
{
return false;
}
return true;
}*/
#endregion
#region 内部方法
/// <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);
/*foreach (var npcData in _enemyNpcObjectWaveDic.Values)
{
foreach (var npc in npcData.Values)
{
DestroyImmediate(npc);
}
}
foreach (var npcData in _midNpcObjectWaveDic.Values)
{
foreach (var npc in npcData.Values)
{
DestroyImmediate(npc);
}
}
foreach (var npc in _selfNpcObjectDic)
{
DestroyImmediate(npc.Value);
}*/
}
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.starTarget.Any(modifier => modifier.conditionType == conditionType))
{
return true;
}
if (currentLevelData.scoreTarget.Any(modifier => modifier.conditionType == conditionType))
{
return true;
}
return false;
}
#endregion
#region MainLife
private void Awake()
{
SceneView.duringSceneGui += OnSceneGUI;
}
protected override void OnEnable()
{
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>();
base.OnEnable();
}
protected override void OnDisable()
{
StopMonitoringSphere();
ShowHiddenObjects();
SaveBlockSphereInfo();
DestroyBlockSphere();
currWindow = null;
DestroyNpcPrefab();
_enemyNpcObjectWaveDic = null;
_midNpcObjectWaveDic = null;
_selfNpcObjectDic = null;
base.OnDisable();
}
protected override void OnDestroy()
{
StopMonitoringSphere();
ShowHiddenObjects();
SaveBlockSphereInfo();
DestroyBlockSphere();
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 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 content = $"{waveID},{npc.id}";
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 content = $"{waveID},{npc.id}";
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
}