using UnityEngine; using BaseGames.Core; using BaseGames.Core.Pool; namespace BaseGames.Combat { /// /// 抛射物基类。子类通过重写 设定初速度。 /// 依赖 子组件进行碰撞伤害检测。 /// [RequireComponent(typeof(Rigidbody2D), typeof(HitBox))] public abstract class Projectile : MonoBehaviour { [HideInInspector] public DamageInfo DamageInfo; [HideInInspector] public Vector2 Direction; protected ProjectileConfigSO _config; protected Rigidbody2D _rb; protected HitBox _hitBox; protected float _aliveTimer; // Lifetime 在 Initialize 时缓存,避免 Update 每帧访问 SO 成员并做 null check private float _lifetime = float.MaxValue; // 命中预算:剩余可命中次数(<=0 的 _maxHits 表示无限穿透) private int _maxHits = 1; private int _hitsRemaining; // 弹反帧标记:弹反命中不计入命中预算(避免反射后立即被回收) private bool _justReflected; // 预制体上配置的初始伤害目标层:对象池复用时还原,避免上一发弹反翻转后的掩码污染下一发 private LayerMask _initialTargetLayers; private PooledObject _pooledObject; protected virtual void Awake() { _rb = GetComponent(); _hitBox = GetComponent(); _pooledObject = GetComponent(); _initialTargetLayers = _hitBox.TargetLayers; // 订阅命中确认:按命中预算决定何时回收(穿透 / 命中即消失) _hitBox.OnHitConfirmed += HandleHitConfirmed; } /// 从对象池取出后的初始化入口。 public virtual void Initialize(ProjectileConfigSO config, DamageInfo damageInfo, Vector2 direction, int ownerLayer = 0) { _config = config; _lifetime = config.Lifetime; DamageInfo = damageInfo; Direction = direction.normalized; _aliveTimer = 0f; _maxHits = config.MaxHits; _hitsRemaining = config.MaxHits; _justReflected = false; // 还原预制体配置的伤害目标层(清除上一次弹反翻转的影响) _hitBox.TargetLayers = _initialTargetLayers; SetFactionLayer(ownerLayer); _hitBox.Activate(config.DamageSource); OnInitialized(); } /// 根据发射方所在 Layer 切换到对应的 PlayerProjectile / EnemyProjectile 层。 private void SetFactionLayer(int ownerLayer) { int playerLayer = LayerMask.NameToLayer("Player"); int playerProjLayer = LayerMask.NameToLayer("PlayerProjectile"); int enemyProjLayer = LayerMask.NameToLayer("EnemyProjectile"); if (playerProjLayer < 0 || enemyProjLayer < 0) return; // Layer 尚未创建,保留现有层 gameObject.layer = (ownerLayer == playerLayer) ? playerProjLayer : enemyProjLayer; } /// /// 阵营感知弹反入口:根据弹反者阵营决定反射行为。 /// 玩家弹反 → 切 PlayerProjectile 层并切换伤害目标层(走 ); /// 敌人弹反 → 阵营层与伤害目标层均保持不变(仍是敌方投射物、仍打玩家侧),仅反转方向并重置命中记录。 /// 由 HurtBox.ReceiveDamage() 在弹反成功后调用,传入弹反者 Transform。 /// public virtual void ReflectBy(Transform parrier) { if (parrier != null && parrier.root.CompareTag("Player")) { ReflectAsPlayerProjectile(); return; } // ── 敌人弹反:不换阵营、不换目标层,仅反转飞行并重置判定 ── Direction = -Direction; _rb.velocity = -_rb.velocity; _hitBox.Deactivate(); _hitBox.Activate(_config?.DamageSource); _hitsRemaining = _maxHits; _justReflected = true; } /// /// 玩家弹反:将投射物阵营从 EnemyProjectile 切换为 PlayerProjectile。 /// 反转飞行方向,并重置 HitBox 命中记录使其能够命中新目标(敌人)。 /// public virtual void ReflectAsPlayerProjectile() { int playerProjLayer = LayerMask.NameToLayer("PlayerProjectile"); if (playerProjLayer < 0) return; gameObject.layer = playerProjLayer; Direction = -Direction; _rb.velocity = -_rb.velocity; // 重置 HitBox 命中记录,确保反射后可命中新目标 _hitBox.Deactivate(); _hitBox.Activate(_config?.DamageSource); // 伤害目标随阵营切换(优先取配置的显式目标层) ApplyReflectedTargetLayers(); // 反射后重置命中预算,并跳过本次(弹反)命中的扣减,避免反射后立即被回收 _hitsRemaining = _maxHits; _justReflected = true; } /// /// 弹反换阵营后应用伤害目标层: /// 优先使用 的显式配置; /// 未配置(Nothing)时自动翻转——仅交换 PlayerHurtBox→EnemyHurtBox 位, /// 保留可破坏物等其他目标层。 /// protected void ApplyReflectedTargetLayers() { if (_config != null && _config.ReflectedTargetLayers.value != 0) { _hitBox.TargetLayers = _config.ReflectedTargetLayers; return; } int playerHurt = LayerMask.NameToLayer("PlayerHurtBox"); int enemyHurt = LayerMask.NameToLayer("EnemyHurtBox"); if (playerHurt < 0 || enemyHurt < 0) return; // Layer 尚未创建,保留现有掩码 int mask = _hitBox.TargetLayers.value; mask &= ~(1 << playerHurt); mask |= 1 << enemyHurt; _hitBox.TargetLayers = mask; } /// HitBox 命中确认回调:按命中预算决定是否回收(穿透 / 命中即消失)。 private void HandleHitConfirmed(DamageInfo _) { if (_maxHits <= 0) return; // 无限穿透:只靠寿命回收 if (_justReflected) { _justReflected = false; return; } // 弹反命中不计入预算 if (--_hitsRemaining <= 0) ReturnToPool(); } protected virtual void OnInitialized() { } protected virtual void Update() { _aliveTimer += Time.deltaTime; if (_aliveTimer >= _lifetime) ReturnToPool(); } /// 停用并归还对象池。 protected void ReturnToPool() { _hitBox.Deactivate(); gameObject.SetActive(false); if (_pooledObject != null && _config != null) ServiceLocator.GetOrDefault()?.Despawn(_config.PoolKey, _pooledObject); } protected virtual void OnDisable() { _aliveTimer = 0f; _lifetime = float.MaxValue; // 归还对象池后重置,防止未初始化时自毁 } } }