Files
zeling_v2/Assets/_Game/Scripts/Editor/Events/CreateEventChannelAssets.cs
2026-06-05 18:41:33 +08:00

250 lines
17 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEditor;
using BaseGames.Core.Events;
using BaseGames.Combat;
using BaseGames.Combat.StatusEffects;
using BaseGames.Equipment;
using BaseGames.Parry;
using BaseGames.Player;
using BaseGames.Dialogue;
using BaseGames.Progression;
using System.IO;
namespace BaseGames.Editor
{
/// <summary>
/// Editor 工具:一键在 Assets/_Game/Data/Events/ 下生成所有全局事件频道 .asset 资产。
/// 菜单BaseGames → Tools → Create Event Channel Assets
/// 已存在的资产会自动跳过(幂等)。
/// </summary>
public static class CreateEventChannelAssets
{
private const string RootPath = "Assets/_Game/Data/Events";
[MenuItem("BaseGames/Events/Create Event Channels")]
public static void CreateAll()
{
// ── Core 原始类型频道 ──────────────────────────────────────────────
CreateAsset<VoidEventChannelSO> ("Core", "EVT_Void");
CreateAsset<BoolEventChannelSO> ("Core", "EVT_Bool");
CreateAsset<IntEventChannelSO> ("Core", "EVT_Int");
CreateAsset<FloatEventChannelSO> ("Core", "EVT_Float");
CreateAsset<StringEventChannelSO> ("Core", "EVT_String");
CreateAsset<Vector2EventChannelSO> ("Core", "EVT_Vector2");
CreateAsset<TransformEventChannelSO> ("Core", "EVT_Transform");
CreateAsset<GameStateEventChannelSO> ("Core", "EVT_GameStateChanged");
CreateAsset<SceneLoadRequestEventChannelSO>("Core", "EVT_SceneLoadRequest");
CreateAsset<StringEventChannelSO> ("Core", "EVT_SceneLoaded");
CreateAsset<VoidEventChannelSO> ("Core", "EVT_FadeInRequest");
CreateAsset<VoidEventChannelSO> ("Core", "EVT_FadeOutRequest");
CreateAsset<VoidEventChannelSO> ("Core", "EVT_SceneWorldStateRestored"); // 场景加载完毕、世界状态恢复后触发;场景物体在此订阅并应用存档状态,淡入前保证画面正确
// ── 难度 ──────────────────────────────────────────────────────────
CreateAsset<DifficultyChangedEventChannel>("Difficulty", "EVT_DifficultyChanged");
// ── 战斗 ──────────────────────────────────────────────────────────
CreateAsset<DamageInfoEventChannelSO> ("Combat", "EVT_DamageDealt");
CreateAsset<HitConfirmedEventChannelSO> ("Combat", "EVT_HitConfirmed");
CreateAsset<VoidEventChannelSO> ("Combat", "EVT_NailClash");
CreateAsset<VoidEventChannelSO> ("Combat", "EVT_PlayerDied");
CreateAsset<VoidEventChannelSO> ("Combat", "EVT_DeathScreenConfirmed");
CreateAsset<StringEventChannelSO> ("Combat", "EVT_EnemyDied");
CreateAsset<ParryInfoEventChannelSO> ("Combat", "EVT_ParrySuccess");
CreateAsset<VoidEventChannelSO> ("Combat", "EVT_PlayerRespawned");
CreateAsset<VoidEventChannelSO> ("Combat", "EVT_RespawnStarted");
CreateAsset<VoidEventChannelSO> ("Combat", "EVT_RespawnCompleted");
CreateAsset<VoidEventChannelSO> ("Combat", "EVT_CheckpointRespawn");
CreateAsset<StatusEffectEventChannelSO> ("Combat", "EVT_StatusEffectApplied");
CreateAsset<StatusEffectEventChannelSO> ("Combat", "EVT_StatusEffectExpired");
CreateAsset<VoidEventChannelSO> ("Combat", "EVT_ShieldBroken");
CreateAsset<VoidEventChannelSO> ("Combat", "EVT_ShieldRestored");
// ── Boss ──────────────────────────────────────────────────────────
CreateAsset<BossSkillEventChannelSO> ("Boss", "EVT_BossSkill");
CreateAsset<BossPhaseEventChannelSO> ("Boss", "EVT_BossPhase");
CreateAsset<BossPhaseEventChannelSO> ("Boss", "EVT_BossPhaseChanged");
CreateAsset<StringEventChannelSO> ("Boss", "EVT_BossDefeated");
CreateAsset<StatusEffectEventChannelSO> ("Boss", "EVT_StatusEffect");
CreateAsset<StringEventChannelSO> ("Boss", "EVT_BossFightStarted");
CreateAsset<BoolEventChannelSO> ("Boss", "EVT_BossFightEnded");
// ── 任务 ──────────────────────────────────────────────────────────
CreateAsset<QuestStateChangedEventChannel>("Quest", "EVT_QuestStateChanged");
CreateAsset<QuestObjectiveEventChannelSO> ("Quest", "EVT_QuestObjective");
// ── UI ────────────────────────────────────────────────────────────
CreateAsset<VoidEventChannelSO> ("UI", "EVT_PauseRequested");
CreateAsset<VoidEventChannelSO> ("UI", "EVT_PauseResumed");
CreateAsset<VoidEventChannelSO> ("UI", "EVT_UICancelPressed"); // ESC / 手柄 B·Circle 全局关闭栈顶面板
CreateAsset<VoidEventChannelSO> ("UI", "EVT_FastTravelOpen");
CreateAsset<StringEventChannelSO> ("UI", "EVT_ShopOpen");
CreateAsset<VoidEventChannelSO> ("UI", "EVT_MapOpen");
CreateAsset<ColorblindModeEventChannelSO> ("UI", "EVT_ColorblindMode");
CreateAsset<BoolEventChannelSO> ("UI", "EVT_InputDeviceChanged");
CreateAsset<BoolEventChannelSO> ("UI", "EVT_SaveIndicatorVisible");
// ── UI / 背包菜单InventoryHub Tab 容器)──────
CreateAsset<VoidEventChannelSO> ("UI/Inventory", "EVT_InventoryOpen"); // 请求打开统一背包菜单
CreateAsset<IntEventChannelSO> ("UI/Inventory", "EVT_InventoryTabChanged"); // 当前激活 Tab 索引变化
CreateAsset<VoidEventChannelSO> ("UI/Inventory", "EVT_InventoryTabNext"); // L/R 肩键:切换到下一 Tab
CreateAsset<VoidEventChannelSO> ("UI/Inventory", "EVT_InventoryTabPrev"); // L/R 肩键:切换到上一 Tab
CreateAsset<StringEventChannelSO> ("UI/Inventory", "EVT_ItemAcquired"); // 道具首次获得itemId
CreateAsset<VoidEventChannelSO> ("UI/Inventory", "EVT_InventoryChanged"); // 背包内容变化(无负载)
// ── 启动流程 / Splash ─────────────────────────────────────────────
CreateAsset<VoidEventChannelSO> ("UI/Splash", "EVT_SplashStartRequest");
CreateAsset<VoidEventChannelSO> ("UI/Splash", "EVT_SplashComplete");
// ── 启动流程 / Loading 画面 ───────────────────────────────────────
CreateAsset<VoidEventChannelSO> ("UI/Loading", "EVT_LoadingStarted");
CreateAsset<VoidEventChannelSO> ("UI/Loading", "EVT_LoadingComplete");
CreateAsset<FloatEventChannelSO> ("UI/Loading", "EVT_LoadingProgressUpdated");
CreateAsset<BoolEventChannelSO> ("UI/Loading", "EVT_LoadingOverlay");
// ── 启动流程 / 主菜单 ─────────────────────────────────────────────
CreateAsset<IntEventChannelSO> ("UI/MainMenu", "EVT_SlotConfirmed");
// ── World ─────────────────────────────────────────────────────────
CreateAsset<StringEventChannelSO> ("World", "EVT_SavePointActivated");
CreateAsset<VoidEventChannelSO> ("World", "EVT_CheckpointReached");
CreateAsset<StringEventChannelSO> ("World", "EVT_DoorOpened"); // 开门/交互机关(钥匙、机关等)触发自动存档
CreateAsset<StringEventChannelSO> ("World", "EVT_ItemPickup"); // 道具/收集品获取itemId
CreateAsset<StringEventChannelSO> ("World", "EVT_CollectiblePickup"); // 关键物品拾取护符、道具等触发存档AutoSaveService / QuestManager / EventChainManager 监听
CreateAsset<StringEventChannelSO> ("World", "EVT_CollectibleSaved"); // 持久化记录收集品collectibleId
CreateAsset<StringEventChannelSO> ("World", "EVT_RoomEntered"); // 玩家进入新房间roomId
CreateAsset<StringEventChannelSO> ("World", "EVT_RegionChanged"); // 玩家首次进入新区域regionId
CreateAsset<StringEventChannelSO> ("World", "EVT_RevealRegion"); // 触发地图区域揭露regionId
CreateAsset<StringEventChannelSO> ("World", "EVT_MapUpdated"); // 房间首次探索/标注时刷新roomIdMapManager 发布MapPanel 订阅
CreateAsset<StringEventChannelSO> ("World", "EVT_ChallengeCompleted"); // 挑战房间通关challengeId
CreateAsset<StringEventChannelSO> ("World", "EVT_ChallengeFailed"); // 挑战房间失败challengeId
CreateAsset<LiquidEventChannelSO> ("World", "EVT_LiquidEntered"); // 玩家进入液体区域
CreateAsset<LiquidEventChannelSO> ("World", "EVT_LiquidExited"); // 玩家离开液体区域
CreateAsset<BaseGames.World.WorldMarkerEventChannelSO>("World", "EVT_WorldMarkerActivated"); // 导航标记点激活(地图图标显示)
CreateAsset<BaseGames.World.WorldMarkerEventChannelSO>("World", "EVT_WorldMarkerDeactivated"); // 导航标记点失活(地图图标隐藏)
// ── 对话/商店 ─────────────────────────────────────────────────────
CreateAsset<ShopPurchaseEventChannelSO> ("Dialogue", "EVT_ShopPurchase");
CreateAsset<DialogueEventChannelSO> ("Dialogue", "EVT_DialogueStartRequest");
CreateAsset<VoidEventChannelSO> ("Dialogue", "EVT_DialogueEnded");
CreateAsset<VoidEventChannelSO> ("Dialogue", "EVT_ShopClosed");
// ── 玩家能力 ──────────────────────────────────────────────────────
CreateAsset<TransformEventChannelSO> ("Player", "EVT_PlayerSpawned");
// 状态值频道开启粘性:延迟订阅的 HUD 等 UI 立即获得当前 HP/灵珠/形态等
CreateAsset<IntEventChannelSO> ("Player", "EVT_HPChanged", stickyReplay: true);
CreateAsset<IntEventChannelSO> ("Player", "EVT_MaxHPChanged", stickyReplay: true);
CreateAsset<IntEventChannelSO> ("Player", "EVT_SoulPowerChanged", stickyReplay: true);
CreateAsset<IntEventChannelSO> ("Player", "EVT_SpiritPowerChanged", stickyReplay: true);
CreateAsset<IntEventChannelSO> ("Player", "EVT_SpringChargesChanged", stickyReplay: true);
CreateAsset<IntEventChannelSO> ("Player", "EVT_LingZhuChanged", stickyReplay: true);
CreateAsset<AbilityTypeEventChannelSO> ("Player", "EVT_AbilityUnlocked");
CreateAsset<StringEventChannelSO> ("Player", "EVT_AbilityUnlockedStr");
CreateAsset<IntEventChannelSO> ("Player", "EVT_FormChanged", stickyReplay: true);
CreateAsset<VoidEventChannelSO> ("Player", "EVT_SkillSetChanged");
// ── 音频 ──────────────────────────────────────────────────────────
CreateAsset<StringEventChannelSO> ("Audio", "EVT_BGMRequest");
CreateAsset<VoidEventChannelSO> ("Audio", "EVT_BGMStop");
// ── 进度/成就 ─────────────────────────────────────────────────────
CreateAsset<ToolUsedEventChannelSO> ("Progression", "EVT_ToolUsed");
CreateAsset<AchievementEventChannelSO> ("Progression", "EVT_AchievementUnlocked");
CreateAsset<StringEventChannelSO> ("Progression", "EVT_MaxHPContainerPickedUp");
CreateAsset<VoidEventChannelSO> ("Progression", "EVT_AchievementNotchGranted");
CreateAsset<CharmEventChannelSO> ("Progression", "EVT_CharmEquipped");
CreateAsset<CharmEventChannelSO> ("Progression", "EVT_CharmUnequipped");
CreateAsset<VoidEventChannelSO> ("Progression", "EVT_EquipmentChanged");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("[CreateEventChannelAssets] 所有事件频道资产生成完毕。");
}
[MenuItem("BaseGames/Events/Reimport Event Channels")]
public static void ReimportAllEventAssets()
{
if (!AssetDatabase.IsValidFolder(RootPath))
{
Debug.LogWarning($"[CreateEventChannelAssets] 未找到目录: {RootPath}");
return;
}
string absoluteRoot = Path.Combine(Directory.GetCurrentDirectory(), RootPath);
string[] files = Directory.GetFiles(absoluteRoot, "*.asset", SearchOption.AllDirectories);
int count = 0;
foreach (string file in files)
{
string relativePath = "Assets" + file.Replace('\\', '/').Substring(Directory.GetCurrentDirectory().Length);
AssetDatabase.ImportAsset(relativePath, ImportAssetOptions.ForceUpdate);
count++;
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log($"[CreateEventChannelAssets] 已重导入 {count} 个事件资产。");
}
private static void CreateAsset<T>(string subfolder, string assetName, bool stickyReplay = false) where T : ScriptableObject
{
string folderPath = $"{RootPath}/{subfolder}";
EnsureDirectory(folderPath);
string fullPath = $"{folderPath}/{assetName}.asset";
var existing = AssetDatabase.LoadAssetAtPath<T>(fullPath);
if (existing != null)
{
if (stickyReplay) ApplyStickyReplay(existing); // 已存在也确保粘性正确(幂等)
Debug.Log($"[CreateEventChannelAssets] 已跳过(已存在): {fullPath}");
return;
}
// 存在但类型不匹配(如旧版残留),先删除再重建
if (AssetDatabase.LoadMainAssetAtPath(fullPath) != null)
{
AssetDatabase.DeleteAsset(fullPath);
Debug.Log($"[CreateEventChannelAssets] 已删除旧类型资产: {fullPath}");
}
T asset = ScriptableObject.CreateInstance<T>();
AssetDatabase.CreateAsset(asset, fullPath);
if (stickyReplay) ApplyStickyReplay(asset);
Debug.Log($"[CreateEventChannelAssets] 已创建: {fullPath}");
}
/// <summary>
/// 对"状态值"频道开启粘性回放BaseEventChannelSO._replayLastValueToNewSubscribers=true
/// 使延迟启用的 UI如 HUD 在加载阶段未启用、进入 Gameplay 后才订阅)订阅时立即收到当前值。
/// </summary>
private static void ApplyStickyReplay(UnityEngine.Object asset)
{
var so = new SerializedObject(asset);
var prop = so.FindProperty("_replayLastValueToNewSubscribers");
if (prop != null && !prop.boolValue)
{
prop.boolValue = true;
so.ApplyModifiedProperties();
EditorUtility.SetDirty(asset);
}
}
/// <summary>递归创建所有缺失的中间文件夹(使用 AssetDatabase API。</summary>
private static void EnsureDirectory(string path)
{
if (AssetDatabase.IsValidFolder(path))
return;
string[] parts = path.Split('/');
string current = parts[0];
for (int i = 1; i < parts.Length; i++)
{
string next = $"{current}/{parts[i]}";
if (!AssetDatabase.IsValidFolder(next))
AssetDatabase.CreateFolder(current, parts[i]);
current = next;
}
}
}
}