414 lines
13 KiB
C#
414 lines
13 KiB
C#
using cfg.LevelCfg;
|
||
using Framework;
|
||
using Gameplay;
|
||
using Gameplay.Level;
|
||
using Sirenix.OdinInspector;
|
||
using Sirenix.OdinInspector.Editor;
|
||
using Sirenix.Utilities;
|
||
using Sirenix.Utilities.Editor;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using TGS;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using UnityEngine.Serialization;
|
||
using Constants = Framework.Constants;
|
||
using ObjectFieldAlignment = Sirenix.OdinInspector.ObjectFieldAlignment;
|
||
|
||
|
||
public class LevelEditorWindow : OdinEditorWindow
|
||
{
|
||
private static readonly Color regionColor = MapUtils.GetRGBAColor("FF960072");
|
||
|
||
[Serializable]
|
||
public class LevelPresentation
|
||
{
|
||
[ReadOnly] public string levelId = "temp";
|
||
|
||
|
||
[Button("编辑"), VerticalGroup("")]
|
||
public void Open()
|
||
{
|
||
if (thisWindow.currentLevelData != null && ValueValidator.IsIdValid(thisWindow.currentLevelData.id))
|
||
{
|
||
CustomPopUpWindow.PopUp($"是否保存关卡数据:{thisWindow.currentLevelData.id}",
|
||
() =>
|
||
{
|
||
thisWindow.SaveCurrentLevel();
|
||
thisWindow.LoadLevel(levelId);
|
||
currentLevelPresentation = this;
|
||
},
|
||
() =>
|
||
{
|
||
thisWindow.LoadLevel(levelId);
|
||
currentLevelPresentation = this;
|
||
}
|
||
);
|
||
}
|
||
else
|
||
{
|
||
thisWindow.LoadLevel(levelId);
|
||
currentLevelPresentation = this;
|
||
}
|
||
}
|
||
}
|
||
|
||
[Serializable]
|
||
public class RegionData
|
||
{
|
||
[LabelText("出战区")]
|
||
[OnValueChanged("OnRegionChanged")]
|
||
public int preparingRegionId;
|
||
|
||
[LabelText("出战区朝向")]
|
||
public LevelData.CharacterToward preparingRegionToward;
|
||
|
||
[LabelText("撤退区")]
|
||
public int fallbackRegionId;
|
||
|
||
[LabelText("敌方撤退区")]
|
||
public int enemyFallbackRegionId;
|
||
|
||
private void OnRegionChanged()
|
||
{
|
||
var regionslist = thisWindow._curMap.MapData.GetAllRegions();
|
||
for(int i = 0; i < regionslist.Count; i++)
|
||
{
|
||
thisWindow._curMap.SetRegionColor(i, false, regionColor);
|
||
}
|
||
thisWindow._curMap.SetRegionColor(preparingRegionId, true, regionColor);
|
||
|
||
}
|
||
}
|
||
|
||
private static LevelPresentation currentLevelPresentation;
|
||
|
||
private Dictionary<Vector2Int, GameObject> _npcDic;
|
||
|
||
private Map _curMap;
|
||
|
||
private static LevelEditorWindow thisWindow;
|
||
|
||
[TitleGroup("关卡编辑器")]
|
||
[HorizontalGroup("关卡编辑器/Split", Width = 0.4f)]
|
||
[TableList(ShowIndexLabels = true, AlwaysExpanded = true, DrawScrollView = true, ScrollViewHeight = 500),
|
||
LabelText("所有关卡")]
|
||
[OnValueChanged("OnLevelListChanged")]
|
||
public List<LevelPresentation> LevelList = null;
|
||
|
||
|
||
[VerticalGroup("关卡编辑器/Split/Right")]
|
||
[LabelText("关卡名称"), Indent]
|
||
public string levelName;
|
||
|
||
[VerticalGroup("关卡编辑器/Split/Right")]
|
||
[TextArea, LabelText("关卡描述"), Indent]
|
||
public string levelDesc;
|
||
|
||
|
||
[VerticalGroup("关卡编辑器/Split/Right")]
|
||
[LabelText("当前关卡数据"),Indent]
|
||
[OnValueChanged("OnCurrentLevelDataChanged")]
|
||
public LevelData currentLevelData = null;
|
||
|
||
[VerticalGroup("关卡编辑器/Split/Right")]
|
||
[LabelText("当前地区数据"),Indent]
|
||
[OnValueChanged("OnCurrentLevelDataChanged")]
|
||
public RegionData curRegionData = null;
|
||
|
||
[VerticalGroup("关卡编辑器/Split/Right")]
|
||
[LabelText("添加NPC")]
|
||
[LabelWidth(100)]
|
||
[SerializeField]
|
||
public bool canAddNpc;
|
||
|
||
[VerticalGroup("关卡编辑器/Split/Right")]
|
||
[PreviewField(150,ObjectFieldAlignment.Center)]
|
||
[AssetList(Path = (Constants.LEVEL_NPC_PATH_WITHOUT_ASSET))]
|
||
[ShowIf("canAddNpc")]
|
||
[LabelText("选择NPC")]
|
||
public GameObject npcObject;
|
||
|
||
|
||
[VerticalGroup("关卡编辑器/Split/Right")] [LabelText("当前NPC数据")]
|
||
public List<LevelData.NPCInfo> CurrentNpcInfo = null;
|
||
|
||
|
||
[VerticalGroup("关卡编辑器/Split/Right")]
|
||
[Indent]
|
||
[EnableIf("@this.mapPrefab != null")]
|
||
[Button("绑定地图")]
|
||
private async void BindMap()
|
||
{
|
||
currentLevelData.mapName = mapPrefab.name;
|
||
EditorMap.Instance.Current = await MapEditorUtils.LoadMap(currentLevelData.mapName);
|
||
_curMap = EditorMap.Instance.Current;
|
||
}
|
||
|
||
[VerticalGroup("关卡编辑器/Split/Right")]
|
||
[Indent]
|
||
[AssetList(Path = (Constants.MAP_ASSET_PATH_WITHOUT_ASSET))]
|
||
[PreviewField(200, ObjectFieldAlignment.Center)]
|
||
public GameObject mapPrefab = null;
|
||
|
||
|
||
[VerticalGroup("关卡编辑器/Split/Right")]
|
||
[Indent]
|
||
[Button("保存当前关卡")]
|
||
private void SaveCurrentLevel()
|
||
{
|
||
CheckCorrectLevel();
|
||
var configFilePath = string.Format(Constants.LEVEL_CONFIG_FORMAT_PATH, currentLevelData.id);
|
||
currentLevelData.preparingRegionId = curRegionData.preparingRegionId;
|
||
currentLevelData.fallbackRegionId = curRegionData.fallbackRegionId;
|
||
currentLevelData.enemyFallbackRegionId = curRegionData.enemyFallbackRegionId;
|
||
currentLevelData.preparingRegionToward = curRegionData.preparingRegionToward;
|
||
|
||
//NPC列表
|
||
currentLevelData.npcInfos = CurrentNpcInfo;
|
||
|
||
JsonHelper.SaveJson(configFilePath, currentLevelData);
|
||
UpdateLevelList(currentLevelData);
|
||
CustomPopUpWindow.PopUp($"保存关卡数据成功:{configFilePath}");
|
||
}
|
||
|
||
|
||
[MenuItem("Tools/LevelEditor")]
|
||
private static void OpenWindow()
|
||
{
|
||
MapEditorSettings.Init();
|
||
thisWindow = GetWindow<LevelEditorWindow>();
|
||
thisWindow.position = GUIHelper.GetEditorWindowRect().AlignCenter(800, 600);
|
||
thisWindow.currentLevelData = null;
|
||
thisWindow.LoadLevelList();
|
||
}
|
||
|
||
public const string preparingRegionErrorMessage = "关卡出战区设置错误,<{0}>地图没有:<{1}>号地区。\n";
|
||
public const string fallbackRegionErrorMessage = "关卡撤退区设置错误,<{0}>地图没有:<{1}>号地区。\n";
|
||
public const string enemyFallbackRegionErrorMessage = "关卡敌方撤退区设置错误,<{0}>地图没有:<{1}>号地区。\n";
|
||
public const string fixedMessage = "已将错误区域设置为<0>号地区。";
|
||
|
||
private void CheckCorrectLevel()
|
||
{
|
||
if (_curMap == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var regionList = _curMap.MapData.GetAllRegions();
|
||
|
||
var errorMessage = string.Empty;
|
||
|
||
if (!regionList.ContainsKey(currentLevelData.preparingRegionId))
|
||
{
|
||
errorMessage += string.Format(preparingRegionErrorMessage, currentLevelData.mapName,
|
||
currentLevelData.preparingRegionId);
|
||
currentLevelData.preparingRegionId = 0;
|
||
}
|
||
|
||
if (!regionList.ContainsKey(currentLevelData.fallbackRegionId))
|
||
{
|
||
errorMessage += string.Format(fallbackRegionErrorMessage, currentLevelData.mapName,
|
||
currentLevelData.fallbackRegionId);
|
||
currentLevelData.fallbackRegionId = 0;
|
||
}
|
||
|
||
if (!regionList.ContainsKey(currentLevelData.enemyFallbackRegionId))
|
||
{
|
||
errorMessage += string.Format(enemyFallbackRegionErrorMessage, currentLevelData.mapName,
|
||
currentLevelData.enemyFallbackRegionId);
|
||
currentLevelData.enemyFallbackRegionId = 0;
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(errorMessage))
|
||
{
|
||
CustomPopUpWindow.PopUp(errorMessage + fixedMessage);
|
||
}
|
||
}
|
||
|
||
protected override void OnEnable()
|
||
{
|
||
TerrainTypeConfig.Load();
|
||
_npcDic = new Dictionary<Vector2Int, GameObject>();
|
||
base.OnEnable();
|
||
}
|
||
|
||
protected override void OnDisable()
|
||
{
|
||
thisWindow = null;
|
||
MapEditorUtils.UnLoadMap(EditorMap.Instance);
|
||
DestroyImmediate(GameObject.Find("Main Camera"));
|
||
DestroyNpcPrefab();
|
||
_npcDic = null;
|
||
base.OnDisable();
|
||
}
|
||
|
||
|
||
private void LoadLevelList()
|
||
{
|
||
banSaveLevelList = true;
|
||
var configFilePath = string.Format(Constants.LEVEL_LIST_PATH);
|
||
LevelList = JsonHelper.LoadJson<List<LevelPresentation>>(configFilePath);
|
||
if (LevelList == null)
|
||
{
|
||
LevelList = new List<LevelPresentation>(4);
|
||
}
|
||
|
||
banSaveLevelList = false;
|
||
}
|
||
|
||
private bool banSaveLevelList = false;
|
||
|
||
private void OnLevelListChanged()
|
||
{
|
||
if (!banSaveLevelList)
|
||
{
|
||
banSaveLevelList = true;
|
||
foreach (var levelPresentation in LevelList)
|
||
{
|
||
if (!ValueValidator.IsIdValid(levelPresentation.levelId))
|
||
{
|
||
Debug.Log($"{GetType()}.New Level declared : {levelPresentation.levelId}");
|
||
}
|
||
}
|
||
|
||
SaveLevelList();
|
||
Debug.Log($"{GetType()}.Save Level List");
|
||
banSaveLevelList = false;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
private void OnCurrentLevelDataChanged()
|
||
{
|
||
Debug.Log($"{GetType()}.OnCurrentLevelDataChanged");
|
||
}
|
||
|
||
private void SaveLevelList()
|
||
{
|
||
var configFilePath = string.Format(Constants.LEVEL_LIST_PATH);
|
||
JsonHelper.SaveJson(configFilePath, LevelList);
|
||
}
|
||
|
||
private const string descKey = "LevelDesc";
|
||
private const string nameKey = "LevelName";
|
||
|
||
private async void LoadLevel(string id)
|
||
{
|
||
DestroyNpcPrefab();
|
||
_npcDic.Clear();
|
||
|
||
var configFilePath = string.Format(Constants.LEVEL_CONFIG_FORMAT_PATH, id);
|
||
currentLevelData = JsonHelper.LoadJson<LevelData>(configFilePath);
|
||
|
||
//加载地区信息
|
||
curRegionData = new RegionData();
|
||
curRegionData.preparingRegionId = currentLevelData.preparingRegionId;
|
||
curRegionData.fallbackRegionId = currentLevelData.fallbackRegionId;
|
||
curRegionData.enemyFallbackRegionId = currentLevelData.enemyFallbackRegionId;
|
||
curRegionData.preparingRegionToward = currentLevelData.preparingRegionToward;
|
||
|
||
if (!string.IsNullOrEmpty(currentLevelData.mapName))
|
||
{
|
||
EditorMap.Instance.Current = await MapEditorUtils.LoadMap(currentLevelData.mapName);
|
||
|
||
_curMap = EditorMap.Instance.Current;
|
||
|
||
// 显示在关卡编辑器中的地图资产
|
||
mapPrefab = await MapUtils.LoadMapAsset(currentLevelData.mapName);
|
||
|
||
_curMap.TerrainGridSystem.OnMouseClickOnGrid += OnCellClick;
|
||
}
|
||
else
|
||
{
|
||
MapEditorUtils.UnLoadMap(EditorMap.Instance);
|
||
mapPrefab = null;
|
||
}
|
||
|
||
//加载NPC到场景中
|
||
CurrentNpcInfo = new List<LevelData.NPCInfo>();
|
||
foreach (var npcInfo in currentLevelData.npcInfos)
|
||
{
|
||
CurrentNpcInfo.Add(npcInfo);
|
||
|
||
string npcId = string.Format("P_E{0}", npcInfo.id.ToString().Substring(1));
|
||
var npc = AssetDatabase.LoadAssetAtPath<GameObject>(string.Format(Constants.LEVEL_NPC_PATH, npcId));
|
||
var position = _curMap.BlockPosition2WorldPosition(npcInfo.position);
|
||
_npcDic.Add(npcInfo.position, Instantiate(npc, position, npc.transform.rotation));
|
||
}
|
||
}
|
||
|
||
private void OnCellClick(TerrainGridSystem tgs, int cellindex, int buttonindex)
|
||
{
|
||
if (!canAddNpc) return;
|
||
|
||
|
||
var clickPosition = _curMap.TGSCellIndex2BlockPosition(cellindex);
|
||
|
||
int leftButton = 0;
|
||
int rightButton = 1;
|
||
|
||
if (canAddNpc && buttonindex == leftButton)
|
||
{
|
||
if (npcObject == null)
|
||
{
|
||
DebugUtil.LogError("请选择正确的NPC!!");
|
||
return;
|
||
}
|
||
|
||
if (_npcDic.ContainsKey(clickPosition))
|
||
{
|
||
DebugUtil.LogError("该位置已有NPC存在,请重新选!!!");
|
||
return;
|
||
}
|
||
|
||
LevelData.NPCInfo npcInfo = new LevelData.NPCInfo();
|
||
string npcId = npcObject.name.Replace("P_E", "1");
|
||
|
||
npcInfo.id = int.Parse(npcId);
|
||
npcInfo.position = clickPosition;
|
||
|
||
//默认等级为1
|
||
npcInfo.level = 1;
|
||
CurrentNpcInfo.Add(npcInfo);
|
||
var position = _curMap.BlockPosition2WorldPosition(npcInfo.position);
|
||
_npcDic.Add(npcInfo.position, Instantiate(npcObject, position, npcObject.transform.rotation));
|
||
DebugUtil.Log("添加了[{0}]NPC到[ {1} ]位置上", npcObject.name, npcInfo.position);
|
||
}
|
||
else if(canAddNpc && buttonindex == rightButton)
|
||
{
|
||
if (_npcDic.TryGetValue(clickPosition,out var npcObj))
|
||
{
|
||
var npc = CurrentNpcInfo.FirstOrDefault(npc => npc.position == clickPosition);
|
||
if (npc != null)
|
||
{
|
||
CurrentNpcInfo.Remove(npc);
|
||
DebugUtil.Log("从[ {0} ]位置上移除了[{1}]NPC", clickPosition, npcObj.name);
|
||
DestroyImmediate(npcObj);
|
||
_npcDic.Remove(clickPosition);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
private void DestroyNpcPrefab()
|
||
{
|
||
foreach (var npc in _npcDic)
|
||
{
|
||
DestroyImmediate(npc.Value);
|
||
}
|
||
}
|
||
|
||
|
||
private void UpdateLevelList(LevelData levelData)
|
||
{
|
||
if (currentLevelPresentation != null)
|
||
{
|
||
currentLevelPresentation.levelId = levelData.id;
|
||
SaveLevelList();
|
||
}
|
||
}
|
||
} |