Merge branch 'temp827'
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Data;
|
||||
|
||||
namespace Entity
|
||||
@ -8,9 +9,10 @@ namespace Entity
|
||||
public float moveSpeed = 1;
|
||||
public int attack = 1;
|
||||
public int defense = 0;
|
||||
public int attackSpeed = 2;
|
||||
public int attackRange = 3;
|
||||
public float attackSpeed = 2;
|
||||
public float attackRange = 3;
|
||||
public int attackTargetCount = 1;
|
||||
|
||||
public Attributes(AttributesDef def)
|
||||
{
|
||||
health = def.health;
|
||||
@ -21,7 +23,108 @@ namespace Entity
|
||||
attackRange = def.attackRange;
|
||||
attackTargetCount = def.attackTargetCount;
|
||||
}
|
||||
|
||||
public Attributes(Attributes other)
|
||||
{
|
||||
health = other.health;
|
||||
moveSpeed = other.moveSpeed;
|
||||
attack = other.attack;
|
||||
defense = other.defense;
|
||||
attackSpeed = other.attackSpeed;
|
||||
attackRange = other.attackRange;
|
||||
attackTargetCount = other.attackTargetCount;
|
||||
}
|
||||
|
||||
public Attributes()
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据给定的属性偏移,生成一个新的 Attributes 实例。
|
||||
/// 原有的 Attributes 实例保持不变。
|
||||
/// </summary>
|
||||
/// <param name="offset">要应用的属性偏移定义。</param>
|
||||
/// <returns>一个新的 Attributes 实例,包含了应用偏移后的值。</returns>
|
||||
public Attributes GetModifiedAttributes(AttributesOffsetDef offset)
|
||||
{
|
||||
// 1. 创建当前 Attributes 实例的一个副本
|
||||
var newAttributes = new Attributes(this);
|
||||
|
||||
if (offset == null)
|
||||
{
|
||||
return newAttributes; // 如果没有偏移,直接返回副本
|
||||
}
|
||||
|
||||
// 2. 在副本上应用绝对值偏移
|
||||
newAttributes.health += (int)offset.healthOffset;
|
||||
newAttributes.moveSpeed += offset.moveSpeedOffset;
|
||||
newAttributes.attack += (int)offset.attackOffset;
|
||||
newAttributes.defense += (int)offset.defenseOffset;
|
||||
|
||||
// 修正: attackSpeed 和 attackRange 是 float,不应强制转换为 int
|
||||
newAttributes.attackSpeed += offset.attackSpeedOffset;
|
||||
newAttributes.attackRange += offset.attackRangeOffset;
|
||||
|
||||
newAttributes.attackTargetCount += (int)offset.attackTargetCountOffset;
|
||||
|
||||
// 3. 在副本上应用百分比偏移 (基于应用绝对值偏移后的结果)
|
||||
newAttributes.health = (int)(newAttributes.health * (1 + offset.healthPercentOffset));
|
||||
newAttributes.moveSpeed *= (1 + offset.moveSpeedPercentOffset);
|
||||
newAttributes.attack = (int)(newAttributes.attack * (1 + offset.attackPercentOffset));
|
||||
newAttributes.defense = (int)(newAttributes.defense * (1 + offset.defensePercentOffset));
|
||||
|
||||
// 修正: attackSpeed 和 attackRange 是 float,不应强制转换为 int
|
||||
newAttributes.attackSpeed *= (1 + offset.attackSpeedPercentOffset);
|
||||
newAttributes.attackRange *= (1 + offset.attackRangePercentOffset);
|
||||
|
||||
newAttributes.attackTargetCount =
|
||||
(int)(newAttributes.attackTargetCount * (1 + offset.attackTargetCountPercentOffset));
|
||||
|
||||
// 4. 确保属性不低于最小值
|
||||
newAttributes.health = Math.Max(0, newAttributes.health);
|
||||
newAttributes.moveSpeed = Math.Max(0f, newAttributes.moveSpeed);
|
||||
newAttributes.attack = Math.Max(0, newAttributes.attack);
|
||||
newAttributes.defense = Math.Max(0, newAttributes.defense);
|
||||
|
||||
// 修正: Math.Max 期望相同类型,0f 对于 float 类型更准确
|
||||
newAttributes.attackSpeed = Math.Max(0f, newAttributes.attackSpeed);
|
||||
newAttributes.attackRange = Math.Max(0f, newAttributes.attackRange);
|
||||
|
||||
newAttributes.attackTargetCount = Math.Max(1, newAttributes.attackTargetCount);
|
||||
|
||||
// 5. 返回修改后的新 Attributes 实例
|
||||
return newAttributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 合并两个 Attributes 实例,生成一个新的 Attributes 实例,
|
||||
/// 其中每个属性值都取自传入两个实例中对应属性的最小值。
|
||||
/// 这对于应用属性上限或限制非常有用。
|
||||
/// </summary>
|
||||
/// <param name="a">第一个 Attributes 实例。</param>
|
||||
/// <param name="b">第二个 Attributes 实例。</param>
|
||||
/// <returns>一个新的 Attributes 实例,其属性是输入实例中对应属性的最小值。</returns>
|
||||
public static Attributes Min(Attributes a, Attributes b)
|
||||
{
|
||||
// 处理 null 情况
|
||||
if (a == null && b == null) return new Attributes(); // 两者都为null,返回默认空属性
|
||||
if (a == null) return new Attributes(b); // a为null,返回b的副本
|
||||
if (b == null) return new Attributes(a); // b为null,返回a的副本
|
||||
|
||||
// 创建一个新的 Attributes 实例来存储结果
|
||||
var result = new Attributes
|
||||
{
|
||||
health = Math.Min(a.health, b.health),
|
||||
moveSpeed = Math.Min(a.moveSpeed, b.moveSpeed),
|
||||
attack = Math.Min(a.attack, b.attack),
|
||||
defense = Math.Min(a.defense, b.defense),
|
||||
attackSpeed = Math.Min(a.attackSpeed, b.attackSpeed),
|
||||
attackRange = Math.Min(a.attackRange, b.attackRange),
|
||||
attackTargetCount = Math.Min(a.attackTargetCount, b.attackTargetCount)
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ using UnityEngine;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
public class Character : Entity
|
||||
public class Character : LivingEntity
|
||||
{
|
||||
private int _currentSelected; // 私有字段用于存储实际值
|
||||
|
||||
@ -21,6 +21,7 @@ namespace Entity
|
||||
var maxIndex = Inventory != null && Inventory.Capacity > 0 ? Inventory.Capacity - 1 : 0;
|
||||
var clampedValue = Mathf.Clamp(value, 0, maxIndex);
|
||||
_currentSelected = clampedValue;
|
||||
InitWeaponAnimator();
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,29 +65,6 @@ namespace Entity
|
||||
return remainingQuantity;
|
||||
}
|
||||
|
||||
// public override void TryAttack()
|
||||
// {
|
||||
// if (IsAttacking)
|
||||
// return;
|
||||
// if (!DefineManager.Instance.defines.TryGetValue(nameof(BulletDef), out var def))
|
||||
// return;
|
||||
// // 修正:First() 可能会在一个空的 Values 集合上抛出异常。
|
||||
// // 更好的做法是使用 TryGetValue 或 FirstOrDefault 并检查结果。
|
||||
// // 这里假设至少有一个 BulletDef 存在,如果不是,需要更复杂的错误处理。
|
||||
// var bulletDefEntry = def.Values.FirstOrDefault();
|
||||
// if (bulletDefEntry == null)
|
||||
// {
|
||||
// Debug.LogError("No BulletDef found in DefineManager. Cannot attack.");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// var bulletDef = (BulletDef)bulletDefEntry;
|
||||
//
|
||||
// Vector3 dir = MousePosition.GetWorldPosition();
|
||||
// EntityManage.Instance.GenerateBulletEntity(Program.Instance.FocusedDimensionId, bulletDef, Position,
|
||||
// dir - Position, this);
|
||||
// }
|
||||
|
||||
public override WeaponResource GetCurrentWeapon()
|
||||
{
|
||||
var currentSelectItem = Inventory.GetSlot(CurrentSelected);
|
||||
|
@ -32,6 +32,12 @@ namespace Entity
|
||||
|
||||
public EntityPrefab entityPrefab;
|
||||
public EntityDef entityDef;
|
||||
/// <summary>
|
||||
/// 手上拿着显示的贴图
|
||||
/// </summary>
|
||||
public GameObject weaponAnimator;
|
||||
|
||||
public ITick[] weaponAnimatorNodeList;
|
||||
|
||||
/// <summary>
|
||||
/// 人工智能行为树,定义实体的行为逻辑。
|
||||
@ -46,7 +52,13 @@ namespace Entity
|
||||
/// <summary>
|
||||
/// 实体的属性定义,包括生命值、攻击力、防御力等。
|
||||
/// </summary>
|
||||
public Attributes attributes = new();
|
||||
public virtual Attributes attributes { get; protected set; }
|
||||
|
||||
private Attributes _baseAttributes;
|
||||
public virtual Attributes baseAttributes
|
||||
{
|
||||
get { return _baseAttributes ??= new Attributes(entityDef.attributes); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实体当前的移动方向。
|
||||
@ -164,7 +176,6 @@ namespace Entity
|
||||
private Coroutine _attackCoroutine;
|
||||
|
||||
|
||||
|
||||
[SerializeField] private float _hitBarUIShowTime = 5;
|
||||
private float _hitBarUIShowTimer = 0;
|
||||
private int _walkingTimer = 0;
|
||||
@ -183,13 +194,34 @@ namespace Entity
|
||||
this.entityDef = entityDef;
|
||||
|
||||
HideHealthBar();
|
||||
InitWeaponAnimator();
|
||||
}
|
||||
|
||||
protected virtual void InitWeaponAnimator()
|
||||
{
|
||||
if (!weaponAnimator)
|
||||
return;
|
||||
for (var i = 0; i < weaponAnimator.transform.childCount; i++)
|
||||
{
|
||||
Destroy(weaponAnimator.transform.GetChild(i).gameObject);
|
||||
weaponAnimatorNodeList = null;
|
||||
}
|
||||
var weapon = GetCurrentWeapon();
|
||||
if (weapon == null)
|
||||
{
|
||||
weaponAnimator.SetActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var weaponAnimation = weapon.InstantiateAttackAnimation(weaponAnimator.transform);
|
||||
weaponAnimatorNodeList = weaponAnimation.animationComponents;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化实体的身体部分,包括不同朝向下的绘图节点。
|
||||
/// </summary>
|
||||
/// <param name="drawingOrder">绘制顺序定义。</param>
|
||||
public virtual void InitBody(DrawingOrderDef drawingOrder)
|
||||
protected virtual void InitBody(DrawingOrderDef drawingOrder)
|
||||
{
|
||||
// 预缓存枚举值(避免每次循环重复调用 Enum.GetValues)
|
||||
var states = Enum.GetValues(typeof(EntityState)).Cast<EntityState>().ToArray();
|
||||
@ -295,7 +327,7 @@ namespace Entity
|
||||
/// <param name="drawNode">绘图节点定义。</param>
|
||||
/// <param name="parent">父节点对象。</param>
|
||||
/// <returns>创建的GameObject,如果失败则返回null</returns>
|
||||
public virtual GameObject InitBodyPart(DrawNodeDef drawNode, GameObject parent)
|
||||
protected virtual GameObject InitBodyPart(DrawNodeDef drawNode, GameObject parent)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -434,7 +466,7 @@ namespace Entity
|
||||
/// <summary>
|
||||
/// 更新实体的逻辑,包括玩家控制和自动行为。
|
||||
/// </summary>
|
||||
public void Tick()
|
||||
public virtual void Tick()
|
||||
{
|
||||
if (_walkingTimer > 0)
|
||||
{
|
||||
@ -470,6 +502,13 @@ namespace Entity
|
||||
}
|
||||
}
|
||||
|
||||
if (weaponAnimatorNodeList != null)
|
||||
{
|
||||
foreach (var tick in weaponAnimatorNodeList)
|
||||
{
|
||||
tick.Tick();
|
||||
}
|
||||
}
|
||||
if (IsShowingHealthBarUI)
|
||||
{
|
||||
_hitBarUIShowTimer -= Time.deltaTime;
|
||||
@ -488,14 +527,12 @@ namespace Entity
|
||||
if (IsAttacking || IsDead) return; // 死亡时无法攻击
|
||||
|
||||
// 尝试获取当前武器
|
||||
WeaponResource currentWeapon = GetCurrentWeapon();
|
||||
var currentWeapon = GetCurrentWeapon();
|
||||
|
||||
// 如果没有武器,可以选择进行徒手攻击或者直接返回
|
||||
// 暂时设定为:如果没有武器,则不进行攻击
|
||||
if (currentWeapon == null)
|
||||
{
|
||||
// 可以在这里添加一个默认的徒手攻击逻辑,或者播放一个“不能攻击”的提示
|
||||
Debug.Log($"{name} 没有装备武器,无法攻击。");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -744,7 +781,7 @@ namespace Entity
|
||||
}
|
||||
|
||||
// STEP 4: 等待到攻击判定时间
|
||||
float elapsedTime = 0f;
|
||||
var elapsedTime = 0f;
|
||||
while (elapsedTime < weapon.AttackDetectionTime)
|
||||
{
|
||||
if (IsDead)
|
||||
@ -766,7 +803,7 @@ namespace Entity
|
||||
|
||||
ExecuteWeaponAction(weapon);
|
||||
|
||||
float remainingAnimationTime = weapon.AttackAnimationTime - elapsedTime;
|
||||
var remainingAnimationTime = weapon.AttackAnimationTime - elapsedTime;
|
||||
if (remainingAnimationTime > 0)
|
||||
{
|
||||
yield return new WaitForSeconds(remainingAnimationTime);
|
||||
@ -851,10 +888,10 @@ namespace Entity
|
||||
|
||||
// 获取子弹方向。这里使用实体当前的移动方向作为子弹发射方向
|
||||
// 更复杂的逻辑可能根据鼠标位置、目标位置等确定
|
||||
Vector3 bulletDirection = direction; // 实体当前的朝向
|
||||
var bulletDirection = direction; // 实体当前的朝向
|
||||
if (PlayerControlled && Input.GetMouseButton(0)) // 玩家控制时,如果鼠标按下,尝试朝鼠标方向发射
|
||||
{
|
||||
Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
|
||||
var mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
|
||||
mouseWorldPos.z = transform.position.z; // 保持Z轴一致
|
||||
bulletDirection = (mouseWorldPos - transform.position).normalized;
|
||||
}
|
||||
|
276
Client/Assets/Scripts/Entity/Hediff.cs
Normal file
276
Client/Assets/Scripts/Entity/Hediff.cs
Normal file
@ -0,0 +1,276 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Data;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 定义一个接口,用于提供属性偏移量。
|
||||
/// 任何实现此接口的组件都可以贡献实体的属性修正。
|
||||
/// </summary>
|
||||
public interface IAttributesOffsetProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取此提供者当前的属性偏移量定义。
|
||||
/// </summary>
|
||||
/// <returns>此提供者所带来的属性偏移量。</returns>
|
||||
AttributesOffsetDef GetAttributesOffset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个实体上运行时存在的健康状态(Hediff)。
|
||||
/// Hediff 可以是疾病、增益、减益、伤口等,它们会影响实体的属性、行为或状态。
|
||||
/// </summary>
|
||||
public class Hediff
|
||||
{
|
||||
/// <summary>
|
||||
/// 此 Hediff 的定义数据。
|
||||
/// </summary>
|
||||
public HediffDef def { get; private set; }
|
||||
/// <summary>
|
||||
/// 此 Hediff 附加到的活体实体。
|
||||
/// </summary>
|
||||
public LivingEntity parent { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 此 Hediff 存在的当前年龄(以秒为单位)。
|
||||
/// </summary>
|
||||
public float Age { get; private set; } = 0f;
|
||||
/// <summary>
|
||||
/// 当前激活的 Hediff 阶段的索引。
|
||||
/// </summary>
|
||||
private int _currentStageIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
/// 附加到此 Hediff 上的所有组件列表。
|
||||
/// </summary>
|
||||
public List<HediffComp> Comps { get; private set; } = new List<HediffComp>();
|
||||
|
||||
/// <summary>
|
||||
/// 标志,指示此 Hediff 是否应该被父实体移除。
|
||||
/// </summary>
|
||||
public bool ShouldRemove { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 内部缓存,用于存储计算出的总属性偏移量。
|
||||
/// </summary>
|
||||
private AttributesOffsetDef _cachedTotalAttributesOffset;
|
||||
/// <summary>
|
||||
/// 标志,指示 Hediff 的属性偏移量是否需要重新计算。
|
||||
/// </summary>
|
||||
private bool _attribsDirty = true;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数,创建一个新的运行时 Hediff 实例。
|
||||
/// </summary>
|
||||
/// <param name="definition">此 Hediff 的定义。</param>
|
||||
/// <exception cref="ArgumentNullException">如果传入的定义为空。</exception>
|
||||
public Hediff(HediffDef definition)
|
||||
{
|
||||
if (definition == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(definition), "Hediff 定义不能为空。");
|
||||
}
|
||||
|
||||
this.def = definition;
|
||||
this._attribsDirty = true; // 构造时标记需要计算属性
|
||||
|
||||
// 确保阶段列表按开始时间排序,以便正确判断当前阶段。
|
||||
if (this.def.stages == null) this.def.stages = new List<HediffStageDef>();
|
||||
this.def.stages = this.def.stages.OrderBy(s => s.start).ToList();
|
||||
|
||||
// 实例化所有定义的组件。
|
||||
if (def.comps != null)
|
||||
{
|
||||
foreach (var compDef in def.comps)
|
||||
{
|
||||
if (compDef.compClass != null && typeof(HediffComp).IsAssignableFrom(compDef.compClass))
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用 Activator.CreateInstance 动态创建组件实例,并传入构造函数参数。
|
||||
// HediffComp 的构造函数需要接受 Hediff parentHediff 和 HediffCompDef def。
|
||||
var comp = (HediffComp)Activator.CreateInstance(compDef.compClass, this, compDef);
|
||||
Comps.Add(comp);
|
||||
comp.Initialize(); // 初始化组件
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"实例化健康状态组件 '{compDef.compClass?.Name ?? "空"}' 失败,所属健康状态 '{def.defName ?? def.GetType().Name}':{ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"警告:健康状态组件定义 '{compDef.compClass?.Name ?? "空"}' 无效或未继承自 HediffComp,所属健康状态 '{def.defName ?? def.GetType().Name}'。");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化时确定第一个阶段,这会触发 SetDirty()。
|
||||
UpdateStageIndex();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取此 Hediff 当前激活的阶段定义。
|
||||
/// 如果没有激活的阶段或阶段列表为空,则返回 null。
|
||||
/// </summary>
|
||||
public HediffStageDef CurrentStage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentStageIndex >= 0 && _currentStageIndex < def.stages.Count)
|
||||
{
|
||||
return def.stages[_currentStageIndex];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取此 Hediff 当前累计的总属性偏移量。
|
||||
/// 只有当属性被标记为“脏”时才重新计算,否则返回缓存值。
|
||||
/// </summary>
|
||||
public AttributesOffsetDef CurrentTotalAttributesOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
// 只有当属性需要更新时才重新计算。
|
||||
if (_attribsDirty || _cachedTotalAttributesOffset == null)
|
||||
{
|
||||
_cachedTotalAttributesOffset = CalculateTotalAttributesOffset();
|
||||
_attribsDirty = false; // 计算完成后重置标志。
|
||||
}
|
||||
return _cachedTotalAttributesOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 内部方法,用于实际计算 Hediff 的所有总属性偏移量。
|
||||
/// 这包括当前阶段的偏移量和所有组件提供的偏移量。
|
||||
/// </summary>
|
||||
/// <returns>此 Hediff 当前的总属性偏移量。</returns>
|
||||
private AttributesOffsetDef CalculateTotalAttributesOffset()
|
||||
{
|
||||
var totalOffset = new AttributesOffsetDef();
|
||||
if (CurrentStage != null)
|
||||
{
|
||||
totalOffset += CurrentStage.attributesOffset;
|
||||
}
|
||||
|
||||
// 累加来自组件的属性偏移。
|
||||
foreach (var comp in Comps)
|
||||
{
|
||||
if (comp is IAttributesOffsetProvider attributesOffsetProvider)
|
||||
{
|
||||
totalOffset += attributesOffsetProvider.GetAttributesOffset();
|
||||
}
|
||||
}
|
||||
return totalOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记 Hediff 的属性偏移量需要重新计算。
|
||||
/// 当 Hediff 的内部状态改变并可能影响属性时调用。
|
||||
/// 同时会通知其父 LivingEntity,使其也更新属性缓存。
|
||||
/// </summary>
|
||||
internal void SetDirty()
|
||||
{
|
||||
_attribsDirty = true;
|
||||
// 如果此 Hediff 附加到了一个 LivingEntity 上,也通知 LivingEntity 属性可能改变。
|
||||
if (parent)
|
||||
{
|
||||
parent.SetAttribsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新 Hediff 的年龄,检查阶段变化并更新所有组件。
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">自上次更新以来的时间(秒)。</param>
|
||||
public void Tick(float deltaTime)
|
||||
{
|
||||
if (ShouldRemove)
|
||||
return; // 已经标记为移除,无需继续更新。
|
||||
|
||||
Age += deltaTime; // 增加 Hediff 的年龄。
|
||||
|
||||
UpdateStageIndex(); // 检查是否有阶段变化(此方法内部会调用 SetDirty)。
|
||||
|
||||
// 更新所有组件。
|
||||
foreach (var comp in Comps)
|
||||
{
|
||||
comp.Tick(deltaTime);
|
||||
}
|
||||
|
||||
// 检查 Hediff 是否到期(如果 def.time > 0 表示有时限,否则为永久)。
|
||||
if (def.time > 0 && Age >= def.time)
|
||||
{
|
||||
ShouldRemove = true;
|
||||
SetDirty(); // Hediff 将被移除,其贡献的属性将不再有效,标记为脏。
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据当前年龄更新阶段索引。
|
||||
/// 如果阶段发生变化,会调用 SetDirty()。
|
||||
/// </summary>
|
||||
private void UpdateStageIndex()
|
||||
{
|
||||
var originalStageIndex = _currentStageIndex; // 获取当前阶段索引。
|
||||
var newStageIndex = _currentStageIndex;
|
||||
|
||||
// 从后往前遍历阶段,找到当前年龄所属的最高阶段。
|
||||
for (var i = def.stages.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (def.stages[i].start <= Age)
|
||||
{
|
||||
newStageIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果阶段发生变化,则标记为脏。
|
||||
if (newStageIndex != originalStageIndex)
|
||||
{
|
||||
_currentStageIndex = newStageIndex;
|
||||
// 阶段发生变化,属性偏移可能改变,标记为脏。
|
||||
SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当此 Hediff 被添加到实体时调用。
|
||||
/// 进行初始化设置,如存储父实体引用,并标记属性为脏以强制重新计算。
|
||||
/// </summary>
|
||||
/// <param name="entity">此 Hediff 附加到的 LivingEntity 实例。</param>
|
||||
internal void OnAdded(LivingEntity entity)
|
||||
{
|
||||
this.parent = entity; // 存储父实体引用。
|
||||
SetDirty(); // 刚添加也需要标记为脏以确保属性在下次计算时被纳入。
|
||||
|
||||
foreach (var comp in Comps)
|
||||
{
|
||||
comp.OnAdded();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当此 Hediff 从实体移除时调用。
|
||||
/// 进行清理操作,如清除父实体引用,并标记属性为脏以强制重新计算。
|
||||
/// </summary>
|
||||
/// <param name="entity">此 Hediff 从中移除的 LivingEntity 实例。</param>
|
||||
internal void OnRemoved(LivingEntity entity)
|
||||
{
|
||||
foreach (var comp in Comps)
|
||||
{
|
||||
comp.OnRemoved();
|
||||
}
|
||||
this.parent = null; // 清除对父实体的引用。
|
||||
SetDirty(); // 移除后也需要标记为脏,确保属性计算考虑去除此Hediff。
|
||||
}
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/Entity/Hediff.cs.meta
Normal file
3
Client/Assets/Scripts/Entity/Hediff.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: afec36ba23b944cf94ca944a8f8f35f4
|
||||
timeCreated: 1756308924
|
47
Client/Assets/Scripts/Entity/HediffComp.cs
Normal file
47
Client/Assets/Scripts/Entity/HediffComp.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using Data;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
// 运行时健康状态组件的抽象基类
|
||||
public abstract class HediffComp
|
||||
{
|
||||
protected Hediff parentHediff; // 对父 Hediff 的引用
|
||||
protected HediffCompDef def; // 对组件定义的引用
|
||||
|
||||
public HediffComp(Hediff parentHediff, HediffCompDef def)
|
||||
{
|
||||
this.parentHediff = parentHediff;
|
||||
this.def = def;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 组件初始化时调用,在构造函数之后。
|
||||
/// </summary>
|
||||
public virtual void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 每帧更新时调用。
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">自上次更新以来的时间(秒)。</param>
|
||||
public virtual void Tick(float deltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当其父 Hediff 被添加到实体时调用。
|
||||
/// </summary>
|
||||
public virtual void OnAdded()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当其父 Hediff 从实体移除时调用。
|
||||
/// </summary>
|
||||
public virtual void OnRemoved()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
3
Client/Assets/Scripts/Entity/HediffComp.cs.meta
Normal file
3
Client/Assets/Scripts/Entity/HediffComp.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3f6a16d500840bea821c21ea612a5c6
|
||||
timeCreated: 1756612166
|
162
Client/Assets/Scripts/Entity/LivingEntity.cs
Normal file
162
Client/Assets/Scripts/Entity/LivingEntity.cs
Normal file
@ -0,0 +1,162 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Data;
|
||||
using Managers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
// 假设 Entity 基类如下结构
|
||||
// public abstract class Entity
|
||||
// {
|
||||
// public virtual Attributes baseAttributes { get; protected set; } = new Attributes();
|
||||
// public virtual Attributes attributes { get; protected set; } = new Attributes();
|
||||
// public virtual void Tick(float deltaTime) { /* Base entity update logic */ }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个具有生命周期、属性和可受健康状态(Hediff)影响的实体。
|
||||
/// </summary>
|
||||
public class LivingEntity : Entity
|
||||
{
|
||||
// 存储应用于此实体的所有健康状态(Hediff)列表。
|
||||
protected List<Hediff> hediffs = new List<Hediff>();
|
||||
// 标记实体属性是否需要重新计算。当Hediff发生变化时,此标记会被设置为true。
|
||||
private bool _needUpdateAttributes = true;
|
||||
|
||||
// 缓存实体的基础属性,这些属性不受动态Hediff影响,但可能受基类或Def影响。
|
||||
private Attributes _cachedBaseAttributes;
|
||||
/// <summary>
|
||||
/// 获取实体的基础属性。这些属性通常来源于实体的定义(Def),并可能受到常驻的基础健康状态(Base Hediffs)影响。
|
||||
/// </summary>
|
||||
public override Attributes baseAttributes
|
||||
{
|
||||
get
|
||||
{
|
||||
// 仅在 _cachedBaseAttributes 为 null 时计算一次
|
||||
if(_cachedBaseAttributes == null)
|
||||
{
|
||||
var defAttributes = base.baseAttributes;
|
||||
var hediffOffset = new AttributesOffsetDef();
|
||||
|
||||
// 这里假设 SaveManager.Instance.baseHediffs 指的是“所有实体共通的基础Hediff”
|
||||
// 并且这些基础 Hediff 也会影响 baseAttributes
|
||||
foreach (var hediff in SaveManager.Instance.baseHediffs)
|
||||
{
|
||||
hediffOffset += hediff.CurrentTotalAttributesOffset;
|
||||
}
|
||||
_cachedBaseAttributes = defAttributes.GetModifiedAttributes(hediffOffset);
|
||||
}
|
||||
return _cachedBaseAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存实体当前的最终属性,该属性是基础属性加上所有健康状态(Hediff)修正后的结果。
|
||||
private Attributes _cachedAttributes;
|
||||
/// <summary>
|
||||
/// 获取实体当前的最终属性,包括所有健康状态(Hediff)的修正。
|
||||
/// </summary>
|
||||
public override Attributes attributes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_needUpdateAttributes || _cachedAttributes == null)
|
||||
{
|
||||
// 1. 获取旧的属性值(在重新计算之前,也就是当前的缓存值)
|
||||
// 仅用于需要与“当前值”进行比较或钳制的情况,例如最大生命值Buff移除时
|
||||
var oldCachedAttributes = _cachedAttributes;
|
||||
if (oldCachedAttributes == null) // 如果是第一次计算,初始化一个默认值或者使用baseAttributes
|
||||
{
|
||||
oldCachedAttributes = baseAttributes;
|
||||
}
|
||||
|
||||
// 2. 计算完全修正后的“理论”最大属性值(基于 baseAttributes 和所有 Hediff 偏移)
|
||||
var totalModifiedAttributes = baseAttributes;
|
||||
|
||||
var hediffOffset = new AttributesOffsetDef();
|
||||
foreach (var hediff in hediffs)
|
||||
{
|
||||
hediffOffset += hediff.CurrentTotalAttributesOffset;
|
||||
}
|
||||
|
||||
// 应用所有 hediff 的偏移到 totalModifiedAttributes
|
||||
totalModifiedAttributes = totalModifiedAttributes.GetModifiedAttributes(hediffOffset);
|
||||
_cachedAttributes = Attributes.Min(oldCachedAttributes, totalModifiedAttributes);
|
||||
// 标记为已更新
|
||||
_needUpdateAttributes = false;
|
||||
}
|
||||
return _cachedAttributes;
|
||||
}
|
||||
protected set => _cachedAttributes = value;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 供内部使用的属性标记方法。当 Hediff 自身状态改变并影响属性时,通过此方法通知 LivingEntity。
|
||||
/// </summary>
|
||||
internal void SetAttribsDirty()
|
||||
{
|
||||
_needUpdateAttributes = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 每帧调用的更新函数,传入时间增量。
|
||||
/// </summary>
|
||||
public override void Tick()
|
||||
{
|
||||
base.Tick(); // 调用基类的Tick方法
|
||||
|
||||
// 遍历并更新所有健康状态,从后向前循环以安全地移除已完成的Hediff
|
||||
for (var i = hediffs.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var hediff = hediffs[i];
|
||||
hediff.Tick(Time.deltaTime); // 调用单个Hediff的Tick方法
|
||||
|
||||
// 检查Hediff是否已达到移除条件
|
||||
if (hediff.ShouldRemove)
|
||||
{
|
||||
RemoveHediff(hediff); // 使用RemoveHediff方法确保OnRemoved被调用并设置_needUpdateAttributes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个新的健康状态到实体上。
|
||||
/// </summary>
|
||||
/// <param name="hediff">要添加的 Hediff 实例。</param>
|
||||
public void AddHediff(Hediff hediff)
|
||||
{
|
||||
if (hediff == null)
|
||||
{
|
||||
Debug.LogWarning("尝试向活体实体添加一个空的健康状态(Hediff)。");
|
||||
return;
|
||||
}
|
||||
|
||||
hediffs.Add(hediff);
|
||||
// 通知Hediff它被添加到一个实体上,进行初始化等操作,并传入自身引用
|
||||
hediff.OnAdded(this);
|
||||
_needUpdateAttributes = true; // 添加新Hediff,需要更新属性缓存
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除一个特定的健康状态。
|
||||
/// </summary>
|
||||
/// <param name="hediff">要移除的 Hediff 实例。</param>
|
||||
public void RemoveHediff(Hediff hediff)
|
||||
{
|
||||
if (hediff == null)
|
||||
{
|
||||
Debug.LogWarning("尝试从活体实体移除一个空的健康状态(Hediff)。");
|
||||
return;
|
||||
}
|
||||
|
||||
// 尝试从列表中移除Hediff
|
||||
if (hediffs.Remove(hediff))
|
||||
{
|
||||
// 通知Hediff它被从实体上移除,进行清理等操作,并传入自身引用
|
||||
hediff.OnRemoved(this);
|
||||
_needUpdateAttributes = true; // 移除Hediff,需要更新属性缓存
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/Entity/LivingEntity.cs.meta
Normal file
3
Client/Assets/Scripts/Entity/LivingEntity.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 440140899cba41b3a023f86e27e69909
|
||||
timeCreated: 1756632414
|
@ -4,7 +4,7 @@ using Managers;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
public class Monster : Entity
|
||||
public class Monster:LivingEntity
|
||||
{
|
||||
private WeaponResource weapon;
|
||||
public override void Init(EntityDef entityDef)
|
||||
|
Reference in New Issue
Block a user