#创作灵感#
记录一下最近的练习项目
废话少说,我们数据驱动有很多办法,最简单的就是通过unity的scriptObject加上一层逻辑层来驱动,但是为了方便策划,我们会使用一些表工具,比如luban。这个自己的学习项目呢(没写完),我是通过luban配置数据变成json导入unity,然后通过一个configManager来处理导入的,后来发现每次都要为一个表写一个很麻烦,后面就考虑通过editor工具自动化配置(待完善)
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using SimpleJSON;
using CDTU.Utils;
using CDTU.LogHelper;
using Hotfix.Config;
[DefaultExecutionOrder(-200)]
public sealed class ConfigManager : SingletonDD<ConfigManager>, IConfigManager
{ private TablesComponent _tables;
public bool IsInitialized { get; private set; } = false;
// 缓存所有表的JSON内容
private Dictionary<string, JSONNode> _jsonCache = new Dictionary<string, JSONNode>();
// 配置表名称列表 - 根据实际配置表扩展
private static readonly List<string> ConfigTableNames = new List<string>
{
"tbcharacterinfo",
"tbcharacterlevel",
"tbweapon",
//TODO- 在这里添加其他配置表名称
};
// 初始化任务,避免async void
private Task _initializationTask;
protected override void Awake()
{
base.Awake();
_ = ConfigManager.Instance;
// 启动初始化但不等待,避免async void
_initializationTask = Initialize();
}
/// <summary>
/// 等待初始化完成
/// </summary>
public async Task WaitForInitialization()
{
if (_initializationTask != null)
{
await _initializationTask;
}
}
/// <summary>
/// 初始化所有配置表
/// </summary>
public async Task Initialize()
{
if (IsInitialized) return;
LogHelper.Log("开始初始化配置管理器...");
// 加载所有配置表的JSON数据
var loadTasks = ConfigTableNames.Select(LoadConfigJson);
await Task.WhenAll(loadTasks); // 构造 TablesComponent,传入JSON加载器
_tables = new TablesComponent(tableName =>
{
_jsonCache.TryGetValue(tableName, out var node);
return node;
});
IsInitialized = true;
LogHelper.Log($"配置管理器初始化完成,共加载 {_jsonCache.Count} 个配置表"); }
/// <summary>
/// 加载单个配置表的JSON数据
/// </summary>
/// <param name="tableName">配置表名称</param>
/// <returns>JSON节点</returns>
private async Task<JSONNode> LoadConfigJson(string tableName)
{
if (_jsonCache.TryGetValue(tableName, out var cachedNode))
return cachedNode;
// 构建Addressables资源路径
string assetPath = $"Assets/Bundles/Config/{tableName}.json";
LogHelper.LogConfig($"开始加载配置表: {tableName}");
// 异步加载TextAsset
var handle = Addressables.LoadAssetAsync<TextAsset>(assetPath);
await handle.Task;
if (handle.Status == AsyncOperationStatus.Succeeded && handle.Result != null)
{
var jsonText = handle.Result.text;
var jsonNode = JSON.Parse(jsonText);
if (jsonNode != null)
{
_jsonCache[tableName] = jsonNode;
LogHelper.LogConfig($"配置表加载成功: {tableName}");
}
else
{
LogHelper.LogError($"配置表JSON解析失败: {tableName}");
}
// 释放Addressables资源
Addressables.Release(handle);
return jsonNode;
}
LogHelper.LogError($"配置表加载失败: {assetPath}, Status: {handle.Status}");
if (handle.IsValid())
{
Addressables.Release(handle);
} return null;
}
/// <summary>
/// 获取TablesComponent - 提供直接访问
/// </summary>
/// <returns>TablesComponent实例</returns>
public TablesComponent GetTables()
{
return IsInitialized ? _tables : null;
}
/// <summary>
/// 重新加载配置(简化版,主要用于编辑器测试)
/// </summary>
public async Task ReloadConfig()
{
IsInitialized = false;
_jsonCache.Clear();
_initializationTask = Initialize();
await _initializationTask;
}
}
这是Editor工具
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEditor;
using Hotfix.Config;
namespace ConfigManagerSystem
{
#if UNITY_EDITOR
/// <summary>
/// 配置注册器代码生成器
/// 扫描Luban生成的TablesComponent,自动生成强类型的配置访问器
/// </summary>
public static class ConfigRegistryGenerator
{
private class ConfigMethodInfo
{
public string TableName { get; set; }
public string TableType { get; set; }
public string DataType { get; set; }
public string DataTypeFullName { get; set; }
public string KeyType { get; set; } = "string"; // 默认为string
}
private const string GENERATED_FILE_PATH = "Assets/Scripts/ConfigSystem/Generated/ConfigRegistry.cs";
private const string NAMESPACE = "ConfigManagerSystem.Generated";
[MenuItem("Tools/Config/生成配置注册器")]
public static void GenerateConfigRegistry()
{
try
{
Debug.Log("开始生成配置注册器...");
var configMethods = ExtractConfigMethods();
Debug.Log($"提取到 {configMethods.Count} 个配置方法:");
foreach (var method in configMethods)
{
Debug.Log($" - 表: {method.TableName}, 类型: {method.DataType}, 键类型: {method.KeyType}");
}
var generatedCode = GenerateRegistryCode(configMethods);
// 确保目录存在
var directory = Path.GetDirectoryName(GENERATED_FILE_PATH);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
File.WriteAllText(GENERATED_FILE_PATH, generatedCode);
AssetDatabase.Refresh();
Debug.Log($"配置注册器生成成功: {GENERATED_FILE_PATH}");
Debug.Log($"共生成 {configMethods.Count} 个配置访问器");
Debug.Log($"生成的代码长度: {generatedCode.Length} 字符");
}
catch (Exception e)
{
Debug.LogError($"生成配置注册器失败: {e.Message}");
Debug.LogError($"堆栈跟踪: {e.StackTrace}");
}
}
private static List<ConfigMethodInfo> ExtractConfigMethods()
{
var methods = new List<ConfigMethodInfo>();
var tablesType = typeof(TablesComponent);
Debug.Log($"正在扫描 {tablesType.FullName} 类型的属性...");
foreach (var property in tablesType.GetProperties())
{
Debug.Log($"扫描属性: {property.Name}, 类型: {property.PropertyType.Name}");
// 检查是否是配置表属性
if (property.PropertyType.Name.StartsWith("TB"))
{
Debug.Log($"发现配置表属性: {property.Name} (类型: {property.PropertyType.Name})");
// 查找所有GetOrDefault方法(支持string和int键)
var getMethods = property.PropertyType.GetMethods()
.Where(m => m.Name == "GetOrDefault" && m.GetParameters().Length == 1);
foreach (var getMethod in getMethods)
{
var paramType = getMethod.GetParameters()[0].ParameterType;
string keyTypeName;
// 确定键的类型
if (paramType == typeof(string))
keyTypeName = "string";
else if (paramType == typeof(int))
keyTypeName = "int";
else
{
Debug.Log($" 跳过不支持的键类型: {paramType.Name}");
continue; // 跳过不支持的键类型
}
Debug.Log($" 找到GetOrDefault方法: 键类型={keyTypeName}, 返回类型={getMethod.ReturnType.Name}");
methods.Add(new ConfigMethodInfo
{
TableName = property.Name,
TableType = property.PropertyType.Name,
DataType = getMethod.ReturnType.Name,
DataTypeFullName = getMethod.ReturnType.FullName,
KeyType = keyTypeName
});
}
}
}
Debug.Log($"总共提取到 {methods.Count} 个配置方法");
return methods;
}
private static string GenerateRegistryCode(List<ConfigMethodInfo> methods)
{
// 按键类型分组,避免方法重复
var groupedMethods = methods.GroupBy(m => new { m.DataType, m.KeyType }).ToList();
var getMethods = string.Join("\n\n ", groupedMethods.Select(g =>
{
var method = g.First();
var keyTypeSuffix = method.KeyType == "int" ? "ById" : "";
return $@"/// <summary>
/// 获取{method.DataType}配置
/// </summary>
public static {method.DataType} Get{method.DataType}{keyTypeSuffix}({method.KeyType} id)
{{
var tables = ConfigManager.Instance?.GetTables();
return tables?.{method.TableName}?.GetOrDefault(id);
}}";
}));
var getAllMethods = string.Join("\n\n ", methods.GroupBy(m => m.DataType).Select(g =>
{
var method = g.First();
return $@"/// <summary>
/// 获取所有{method.DataType}配置
/// </summary>
public static List<{method.DataType}> GetAll{method.DataType}()
{{
var tables = ConfigManager.Instance?.GetTables();
return tables?.{method.TableName}?.DataList ?? new List<{method.DataType}>();
}}";
})); var hasMethods = string.Join("\n\n ", groupedMethods.Select(g =>
{
var method = g.First();
var keyTypeSuffix = method.KeyType == "int" ? "ById" : "";
return $@"/// <summary>
/// 检查{method.DataType}配置是否存在
/// </summary>
public static bool Has{method.DataType}{keyTypeSuffix}({method.KeyType} id)
{{
return Get{method.DataType}{keyTypeSuffix}(id) != null;
}}";
})); var fastAccessMethods = string.Join("\n\n ", methods.GroupBy(m => m.DataType).Select(g =>
{
var method = g.First();
return $@"/// <summary>
/// 获取{method.DataType}配置表(零分配访问)
/// </summary>
public static {method.TableType} Get{method.DataType}Table()
{{
return ConfigManager.Instance?.GetTables()?.{method.TableName};
}}";
})); // 添加缓存相关的方法
var cachedMethods = string.Join("\n\n ", groupedMethods.Select(g =>
{
var method = g.First();
var keyTypeSuffix = method.KeyType == "int" ? "ById" : "";
var cacheKey = method.DataType.ToLower() + (method.KeyType == "int" ? "ById" : "");
return $@"private static readonly Dictionary<{method.KeyType}, {method.DataType}> _{cacheKey}Cache = new Dictionary<{method.KeyType}, {method.DataType}>();
/// <summary>
/// 获取{method.DataType}配置(带缓存)
/// </summary>
public static {method.DataType} Get{method.DataType}Cached{keyTypeSuffix}({method.KeyType} id)
{{
if (_{cacheKey}Cache.TryGetValue(id, out var cached))
return cached;
var config = Get{method.DataType}{keyTypeSuffix}(id);
if (config != null)
_{cacheKey}Cache[id] = config;
return config;
}}
/// <summary>
/// 清除{method.DataType}缓存
/// </summary>
public static void Clear{method.DataType}Cache{keyTypeSuffix}()
{{
_{cacheKey}Cache.Clear();
}}";
}));
return $@"//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by ConfigRegistryGenerator.
// Changes to this file will be lost if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System.Collections.Generic;
using Hotfix.Config;
namespace {NAMESPACE}
{{
/// <summary>
/// 自动生成的配置访问器
/// 提供强类型、高性能的配置访问方法
/// 避免运行时反射,提升性能
/// </summary>
public static class ConfigRegistry
{{
#region 单个配置获取方法
{getMethods}
#endregion
#region 所有配置获取方法
{getAllMethods}
#endregion
#region 配置存在性检查方法
{hasMethods}
#endregion
#region 快速表访问方法(零分配)
{fastAccessMethods}
#endregion
#region 缓存访问方法
{cachedMethods}
#endregion
#region 工具方法
/// <summary>
/// 检查配置管理器是否已初始化
/// </summary>
public static bool IsConfigManagerReady => ConfigManager.Instance?.IsInitialized == true;
/// <summary>
/// 等待配置管理器初始化完成
/// </summary>
public static async System.Threading.Tasks.Task WaitForInitialization()
{{
if (ConfigManager.Instance != null)
{{
await ConfigManager.Instance.WaitForInitialization();
}}
}}
/// <summary>
/// 清除所有缓存
/// </summary>
public static void ClearAllCaches()
{{
{string.Join("\n ", groupedMethods.Select(g =>
{
var method = g.First();
var keyTypeSuffix = method.KeyType == "int" ? "ById" : "";
return $"Clear{method.DataType}Cache{keyTypeSuffix}();";
}))}
}}
/// <summary>
/// 获取配置统计信息
/// </summary>
public static string GetConfigStats()
{{
var tables = ConfigManager.Instance?.GetTables();
if (tables == null) return ""配置未初始化"";
return $@""配置统计:
{string.Join("\n", methods.GroupBy(m => m.DataType).Select(g =>
{
var method = g.First();
return $"- {method.DataType}: {{tables.{method.TableName}?.DataList?.Count ?? 0}} 项";
}))}"";
}}
#endregion
}}
}}";
}
}
#endif
}
通过这两个工具(当然还是有问题的比如还是要在这个配置表名称里面加自己json表名称)
这就是数据驱动以及载入了。。。。。
接下来是组件模式,人物的组件模式我准备分为两种,一种人物本身的组件模式,这个不用说都很简单,只需要给characterdata一个共享数据的地方
using System.Collections.Generic;
using UnityEngine;
using ConfigManagerSystem.Generated;
using CDTU.LogHelper;
using System;
namespace Characters.Data
{
/// <summary>
/// 修正值类型枚举
/// </summary>
public enum ModifierType
{
MaxHealth,
Attack,
MoveSpeed,
AttackSpeed,
Power,
Unknown
}
/// <summary>
/// 角色配置数据类 - 统一管理角色的所有配置数值
/// </summary>
[Serializable]
public class CharacterConfigData : ICharacterConfigProvider
{
// 基础信息
public readonly string CharacterId;
// 缓存的最终数值(避免重复计算)
private float _cachedMaxHealth;
private float _cachedAttack;
private float _cachedMoveSpeed;
private float _cachedPower;
private float _cachedAttackSpeed;
private bool _cachedDestroyOnDeath;
private bool _statsDirty = true;
// 公开访问属性
public float MaxHealth
{
get
{
RefreshStatsIfDirty();
return _cachedMaxHealth;
}
}
public float Attack
{
get
{
RefreshStatsIfDirty();
return _cachedAttack;
}
}
public float MoveSpeed
{
get
{
RefreshStatsIfDirty();
return _cachedMoveSpeed;
}
}
public float Power
{
get
{
RefreshStatsIfDirty();
return _cachedPower;
}
}
public float AttackSpeed
{
get
{
RefreshStatsIfDirty();
return _cachedAttackSpeed;
}
}
public bool DestroyOnDeath
{
get
{
RefreshStatsIfDirty();
return _cachedDestroyOnDeath;
}
}
// 修正值系统(装备、BUFF等)
private readonly Dictionary<string, float> _modifiers = new();
// 事件系统
public Action<CharacterConfigData> OnStatsChanged;
/// <summary>
/// 构造函数
/// </summary>
public CharacterConfigData(string characterId)
{
CharacterId = characterId;
_statsDirty = true;
}
// /// <summary>
// /// 添加修正值(装备、BUFF等)
// /// </summary>
// public void AddModifier(string key, float value)
// {
// _modifiers[key] = value;
// _statsDirty = true;
// }
/// <summary>
/// 添加类型化修正值(推荐使用)
/// </summary>
public void AddModifier(ModifierType type, string key, float value)
{
// 使用类型前缀确保准确分类
var typedKey = $"{type}_{key}";
_modifiers[typedKey] = value;
_statsDirty = true;
}
/// <summary>
/// 移除修正值
/// </summary>
public void RemoveModifier(string key)
{
if (_modifiers.Remove(key))
{
_statsDirty = true;
}
}
/// <summary>
/// 移除类型化修正值
/// </summary>
public void RemoveModifier(ModifierType type, string key)
{
var typedKey = $"{type}_{key}";
RemoveModifier(typedKey);
}
/// <summary>
/// 清除所有修正值
/// </summary>
public void ClearModifiers()
{
_modifiers.Clear();
_statsDirty = true;
}
/// <summary>
/// 强制刷新数值
/// </summary>
public void ForceRefresh()
{
_statsDirty = true;
RefreshStatsIfDirty();
}
/// <summary>
/// 按需刷新数值
/// </summary>
private void RefreshStatsIfDirty()
{
if (_statsDirty)
{
RefreshAllStats();
}
}
/// <summary>
/// 重新计算所有数值
/// </summary>
private void RefreshAllStats()
{
// 获取基础配置
var baseConfig = ConfigRegistry.GetCharacterInfo(CharacterId);
if (baseConfig == null)
{
LogHelper.LogError($"找不到角色配置: {CharacterId}");
return;
}
// 暂时使用基础配置数值,不考虑等级影响
_cachedMaxHealth = baseConfig.MaxHealth;
_cachedAttack = baseConfig.Atk;
_cachedMoveSpeed = baseConfig.MoveSpeed;
_cachedPower = baseConfig.Power;
// 暂时使用固定攻击速度
_cachedAttackSpeed = 1.0f;
_cachedDestroyOnDeath = baseConfig.DestroyOnDeath;
// 应用修正值
ApplyModifiers();
_statsDirty = false;
// 触发变化事件
OnStatsChanged?.Invoke(this);
}
/// <summary>
/// 应用所有修正值
/// </summary>
private void ApplyModifiers()
{
foreach (var modifier in _modifiers)
{
var key = modifier.Key;
var value = modifier.Value;
var modifierType = ClassifyModifier(key);
switch (modifierType)
{
case ModifierType.MaxHealth:
_cachedMaxHealth += value;
break;
case ModifierType.Attack:
_cachedAttack += value;
break;
case ModifierType.MoveSpeed:
_cachedMoveSpeed += value;
break;
case ModifierType.AttackSpeed:
_cachedAttackSpeed += value;
break;
case ModifierType.Power:
_cachedPower += value;
break;
case ModifierType.Unknown:
LogHelper.LogWarning($"未识别的修正值类型: {key},值: {value}");
break;
}
}
// 确保数值不为负
_cachedMaxHealth = Mathf.Max(1, _cachedMaxHealth);
_cachedAttack = Mathf.Max(0, _cachedAttack);
_cachedMoveSpeed = Mathf.Max(0.1f, _cachedMoveSpeed);//todo-也许需要调整
_cachedPower = Mathf.Max(0, _cachedPower);
_cachedAttackSpeed = Mathf.Max(0.1f, _cachedAttackSpeed);
}
/// <summary>
/// 分类修正值类型
/// </summary>
private ModifierType ClassifyModifier(string key)
{
// 检查类型化前缀
if (key.StartsWith("MaxHealth_") || key.StartsWith("Health_"))
return ModifierType.MaxHealth;
if (key.StartsWith("Attack_"))
return ModifierType.Attack;
if (key.StartsWith("MoveSpeed_"))
return ModifierType.MoveSpeed;
if (key.StartsWith("AttackSpeed_"))
return ModifierType.AttackSpeed;
if (key.StartsWith("Power_"))
return ModifierType.Power;
// 向后兼容的字符串匹配(按优先级顺序)
var lowerKey = key.ToLowerInvariant();
if (lowerKey.Contains("maxhealth") || lowerKey.Contains("health"))
return ModifierType.MaxHealth;
if (lowerKey.Contains("attackspeed"))
return ModifierType.AttackSpeed;
if (lowerKey.Contains("movespeed"))
return ModifierType.MoveSpeed;
if (lowerKey.Contains("attack"))
return ModifierType.Attack;
if (lowerKey.Contains("power"))
return ModifierType.Power;
return ModifierType.Unknown;
}
public float GetStats(ModifierType type)
{
RefreshStatsIfDirty();
return type switch
{
ModifierType.MaxHealth => _cachedMaxHealth,
ModifierType.Attack => _cachedAttack,
ModifierType.MoveSpeed => _cachedMoveSpeed,
// ModifierType.AttackSpeed => _cachedAttackSpeed,
ModifierType.Power => _cachedPower,
_ => 0f // 未知类型返回0
};
}
/// <summary>
/// 获取配置摘要信息
/// </summary>
public string GetConfigSummary()
{
RefreshStatsIfDirty();
return $"角色: {CharacterId}\n" +
$"生命值: {_cachedMaxHealth:F1}, 攻击力: {_cachedAttack:F1}\n" +
$"移动速度: {_cachedMoveSpeed:F1}\n" +
$"能量: {_cachedPower:F1}, 死亡销毁: {_cachedDestroyOnDeath}";
}
/// <summary>
/// 获取修正值详情(调试用)
/// </summary>
public string GetModifiersDetail()
{
if (_modifiers.Count == 0)
return "无修正值";
var details = new System.Text.StringBuilder();
details.AppendLine("当前修正值 (按类型分组):");
// 按类型分组显示
var typeGroups = new Dictionary<ModifierType, List<KeyValuePair<string, float>>>();
foreach (var modifier in _modifiers)
{
var modifierType = ClassifyModifier(modifier.Key);
if (!typeGroups.ContainsKey(modifierType))
typeGroups[modifierType] = new List<KeyValuePair<string, float>>();
typeGroups[modifierType].Add(modifier);
}
foreach (var group in typeGroups)
{
details.AppendLine($" {group.Key}:");
foreach (var modifier in group.Value)
{
details.AppendLine($" {modifier.Key}: {modifier.Value:F1}");
}
}
return details.ToString().TrimEnd();
}
/// <summary>
/// 获取基础值详情(调试用)
/// </summary>
public string GetBaseValuesDetail()
{
var baseConfig = ConfigRegistry.GetCharacterInfo(CharacterId);
if (baseConfig == null)
return "基础配置未找到";
// 暂时注释掉等级系统相关代码
// float healthMultiplier = 1.0f + (_currentLevel - 1) * 0.1f;
// float attackBonus = (_currentLevel - 1) * 2f;
// float speedBonus = (_currentLevel - 1) * 0.1f;
return $"基础配置:\n" +
$" 基础血量: {baseConfig.MaxHealth}\n" +
$" 基础攻击: {baseConfig.Atk}\n" +
$" 基础速度: {baseConfig.MoveSpeed}\n" +
$" 基础能量: {baseConfig.Power}";
// $" 基础攻速: 1.0";
}
}
}
当然还是未完善,,大家都在这里处理数据修改数据,保证了人物数据的统一,当然玩家控制的人物是只通过这里修改,敌人的数据我会想着通过半ECS通过一个system统一处理组件,优化性能。这里就只讲解一下玩家控制人物处理的逻辑,开始一个协调者Character组件先初始化数据并载入数据,通过init方法和接口IComponent来初始化各个组件,这样一个人物就初始化好了。
using UnityEngine;
using CDTU.LogHelper;
using Characters.Utils;
using Characters.Data;
using System.Threading.Tasks;
using Characters.Managers;
public class Character : MonoBehaviour
{
[SerializeField] protected string characterId;
// [SerializeField] protected int characterLevel = 1;
// 组件管理器
protected ComponentManager _componentManager;
// 核心组件
private CharacterHealthComponent _healthComponent;
private CharacterPowerComponent _powerComponent;
// 统一配置数据
private CharacterConfigData _characterConfig;
// 属性访问
public string CharacterId => characterId;
// public int CharacterLevel => characterLevel;
public CharacterConfigData CharacterConfig => _characterConfig;
// 便捷的数值访问
public float MaxHealth => _characterConfig?.MaxHealth ?? 0f;
public float Attack => _characterConfig?.Attack ?? 0f;
public float MoveSpeed => _characterConfig?.MoveSpeed ?? 0f;
public float Power => _characterConfig?.Power ?? 0f;
// 升级事件
public System.Action<int> OnCharacterLevelUp;
/// <summary>
/// 检查角色是否已初始化完成
/// </summary>
public bool IsInitialized => _isInitialized;
// 初始化状态
private Task _initializationTask;
private bool _isInitialized = false;
protected virtual void Awake()
{
if (string.IsNullOrEmpty(characterId))
{
LogHelper.LogError("角色ID未设置!");
return;
}
// 启动异步初始化
_initializationTask = InitializeAsync();
}
private async Task InitializeAsync()
{
// 1. 等待配置管理器初始化
await ConfigManager.Instance.WaitForInitialization();
// 2. 获取统一配置数据
// _characterConfig = CharacterConfigManager.GetConfig(characterId, characterLevel);
_characterConfig = CharacterConfigManager.GetConfig(characterId);
if (_characterConfig == null)
{
// LogHelper.LogError($"无法创建角色配置,ID: {characterId}, Level: {characterLevel}");
LogHelper.LogError($"无法创建角色配置,ID: {characterId}");
return;
}
// 3. 订阅配置变化事件
_characterConfig.OnStatsChanged += OnConfigStatsChanged;
// 4. 初始化组件管理器
_componentManager = new ComponentManager(this.gameObject, this);
if (!_componentManager.Initialize())
{
LogHelper.LogError("组件管理器初始化失败!");
return;
} // 5. 获取核心组件并传递配置
_healthComponent = _componentManager.GetOrAddComponent<CharacterHealthComponent>();
_powerComponent = _componentManager.GetOrAddComponent<CharacterPowerComponent>();
// 6. 订阅组件事件
if (_healthComponent != null)
{
_healthComponent.OnHealthChanged += OnCharacterHealthChanged;
_healthComponent.OnDied += OnCharacterDied;
}
if (_powerComponent != null)
{
_powerComponent.OnPowerChanged += OnPowerChanged;
}
// 7. 标记初始化完成
_isInitialized = true;
LogHelper.Log($"角色 {characterId}");
// LogHelper.Log($"角色 {characterId} (Level {characterLevel}) 初始化完成");
}
/// <summary>
/// 配置数值变化时的回调
/// </summary>
private void OnConfigStatsChanged(CharacterConfigData config)
{
LogHelper.Log($"角色 {characterId} 配置更新: {config.GetConfigSummary()}");
// 通知组件配置已更新
NotifyComponentsConfigUpdated();
} /// <summary>
/// 通知所有组件配置已更新
/// </summary>
private void NotifyComponentsConfigUpdated()
{
// 配置数据系统会自动通知所有组件,这里主要用于其他特殊处理
LogHelper.Log($"角色 {characterId} 配置已更新,所有组件将自动同步");
}
// /// <summary>
// /// 角色升级
// /// </summary>
// public virtual void LevelUp()
// {
// // characterLevel++;
// // _characterConfig?.LevelUp();
// LogHelper.Log($"角色 {characterId} 升级到 {characterLevel} 级");
// OnCharacterLevelUp?.Invoke(characterLevel);
// }
// /// <summary>
// /// 设置角色等级
// /// </summary>
// public virtual void SetLevel(int newLevel)
// {
// if (characterLevel == newLevel) return;
// characterLevel = newLevel;
// _characterConfig?.SetLevel(newLevel);
// OnCharacterLevelUp?.Invoke(characterLevel);
// }
protected virtual async void Start()
{
// 等待初始化完成
if (_initializationTask != null)
{
await _initializationTask;
}
// 确保初始化成功后才开始组件
if (_isInitialized && _componentManager != null)
{
_componentManager.StartAll();
}
}
protected virtual void Update()
{
// 只有在初始化完成后才更新组件
if (_isInitialized && _componentManager != null)
{
_componentManager.UpdateAll(Time.deltaTime);
}
}
/// <summary>
/// 等待角色初始化完成
/// </summary>
public async Task WaitForInitialization()
{
if (_initializationTask != null)
{
await _initializationTask;
}
}
#region 事件处理
protected virtual void OnCharacterHealthChanged(object sender, CharacterHealthComponent.HealthChangedEventArgs e)
{
LogHelper.Log($"角色受到 {e.ChangeAmount} 点伤害!当前生命值: {e.CurrentHealth}/{_componentManager.GetComponent<CharacterHealthComponent>().GetMaxHealth()}");
}
protected virtual void OnCharacterDied()
{
LogHelper.Log($"{CharacterId}已死亡!");
}
protected virtual void OnPowerChanged(object sender, CharacterPowerComponent.OnPowerChangedEventArgs e)
{
LogHelper.Log($"角色能量值变化: {e.Power}/{_powerComponent.GetMaxPower()}");
}
#endregion
#region 公共方法供调用
public void ApplyDamage(float damage)
{
_componentManager.GetComponent<CharacterHealthComponent>().TakeDamage(damage);
}
public void ApplyHealing(float amount)
{
_componentManager.GetComponent<CharacterHealthComponent>().Heal(amount);
}
#endregion
protected virtual void OnDestroy()
{
// 取消配置事件订阅
if (_characterConfig != null)
{
_characterConfig.OnStatsChanged -= OnConfigStatsChanged;
}
// 取消事件订阅
if (_healthComponent != null)
{
_healthComponent.OnHealthChanged -= OnCharacterHealthChanged;
_healthComponent.OnDied -= OnCharacterDied;
}
if (_powerComponent != null)
{
_powerComponent.OnPowerChanged -= OnPowerChanged;
}
// 清理所有组件
_componentManager?.CleanupAll();
}
}
using Characters.Data;
public interface IComponent
{
/// <summary>
/// 组件的拥有者
/// </summary>
/// <param name="config"></param>
void OnInit(CharacterConfigData config);
/// <summary>
/// 所有组件初始化后调用,用于依赖其他组件的设置
/// </summary>
void OnStart();
/// <summary>
/// 组件被禁用或移除时调用
/// </summary>
void OnDisable();
/// <summary>
/// 设置组件的拥有者
/// 该方法通常在组件被添加到角色时调用
/// </summary>
/// <param name="owner">组件的拥有者</param>
void SetOwner(Character owner);
}
// 可更新组件
public interface IUpdatableComponent : IComponent
{
void OnTick(float deltaTime);
}