chore: initial commit

This commit is contained in:
2026-05-08 11:04:00 +08:00
commit f55d2a57c3
6278 changed files with 866081 additions and 0 deletions

View File

@@ -0,0 +1,164 @@
using UnityEngine;
using Animancer;
using BaseGames.Combat;
namespace BaseGames.Enemies
{
/// <summary>
/// 敌人基类(架构 07_EnemyModule §1
/// 实现 IDamageable为 Behavior Designer 任务提供统一虚方法接口。
/// Phase 1 实现完整骨架BD 接口、受击、死亡流程。
/// ⚠️ _nav 字段类型为 IPathAgent在 BaseGames.Enemies.Navigation 中实现具体类)。
/// </summary>
public class EnemyBase : MonoBehaviour, IDamageable
{
[Header("配置 SO")]
[SerializeField] protected EnemyStatsSO _statsSO;
[SerializeField] protected EnemyAnimationConfigSO _animConfig;
[Header("子组件Prefab Inspector 绑定)")]
[SerializeField] protected EnemyStats _stats;
[SerializeField] protected EnemyMovement _movement;
[SerializeField] protected EnemyCombat _combat;
[SerializeField] protected AnimancerComponent _animancer;
[SerializeField] protected EnemyFeedback _feedback;
[SerializeField] protected HurtBox _hurtBox;
[Header("事件频道")]
[SerializeField] private BaseGames.Core.Events.TransformEventChannelSO _onEnemyDied;
// ── 导航代理IPathAgent由 EnemyNavAgent 实现)───────────────────
// Phase 1通过接口引用避免对 Navigation 程序集的直接依赖。
// 由子类 / Inspector 注入,或者运行时 GetComponent<IPathAgent>() 获取。
protected IPathAgent _nav;
// ── 状态 ──────────────────────────────────────────────────────────
private EnemyStateType _currentState;
public EnemyStateType CurrentState => _currentState;
// ── IDamageable ───────────────────────────────────────────────────
public bool IsInvincible => _currentState == EnemyStateType.Dead;
public int Defense => _stats != null ? _stats.Defense : 0;
public void TakeDamage(DamageInfo info)
{
if (_currentState == EnemyStateType.Dead) return;
_stats?.TakeDamage(info.FinalDamage);
_feedback?.OnHit(info);
if (_stats != null && _stats.CurrentHP <= 0)
{
Die();
return;
}
// Phase 2根据霸体结果选 Stagger / Hurt
ForceState(EnemyStateType.Hurt);
}
// ── BD 行为树接口(虚方法)────────────────────────────────────────
public virtual void MoveTo(Vector2 target)
=> _nav?.RequestMoveTo(target);
public virtual void MoveInDirection(float dir)
=> _movement?.MoveHorizontal(dir);
public virtual void StopMovement()
{
_nav?.StopNavigation();
_movement?.StopHorizontal();
}
public virtual void BeginAttack(AttackType type)
{
_combat?.StartAttack(type);
_stats?.ResetAttackCooldown();
}
public virtual bool CanAttack()
=> _stats != null && _stats.AttackCooldownTimer <= 0f;
public virtual bool IsPlayerInRange(float range)
=> _stats != null && _stats.DistanceToPlayer <= range;
public virtual void FacePlayer()
{
if (_playerTransform != null)
_movement?.FaceTarget(_playerTransform.position);
}
public virtual void Knockback(DamageInfo info)
{
if (info.Flags.HasFlag(DamageFlags.NoKnockback)) return;
_movement?.ApplyKnockback(info.KnockbackDirection, info.KnockbackForce);
}
// ── 状态控制 ──────────────────────────────────────────────────────
public void ForceState(EnemyStateType newState)
{
_currentState = newState;
// Phase 2根据状态播放对应动画 / 触发硬直计时
}
// ── Unity 生命周期 ────────────────────────────────────────────────
protected virtual void Awake()
{
_nav = GetComponent<IPathAgent>() ?? new NullPathAgent();
if (_stats != null && _statsSO != null)
_stats.Initialize(_statsSO);
// Phase 1简单查找玩家Phase 2 改为事件频道订阅
var playerGO = GameObject.FindWithTag("Player");
if (playerGO != null) _playerTransform = playerGO.transform;
}
protected virtual void Update()
{
_stats?.TickAttackCooldown(Time.deltaTime);
if (_playerTransform != null && _stats != null)
_stats.DistanceToPlayer = Vector2.Distance(transform.position, _playerTransform.position);
}
protected virtual void Start()
{
// 播放 Idle 动画(若 Animancer 和配置都就绪)
if (_animancer != null && _animConfig != null && _animConfig.Idle != null)
_animancer.Play(_animConfig.Idle);
}
// ── 内部 ──────────────────────────────────────────────────────────
private Transform _playerTransform;
protected virtual void Die()
{
if (_currentState == EnemyStateType.Dead) return;
ForceState(EnemyStateType.Dead);
// 禁用所有碰撞体
foreach (var col in GetComponentsInChildren<Collider2D>())
col.enabled = false;
// 播放死亡动画
if (_animancer != null && _animConfig != null && _animConfig.Dead != null)
{
var state = _animancer.Play(_animConfig.Dead);
state.Events(this).OnEnd = () => Destroy(gameObject);
}
else
{
Destroy(gameObject, 1.5f);
}
_feedback?.OnDeath();
_onEnemyDied?.Raise(transform);
}
}
// ── 枚举(架构 07 §1────────────────────────────────────────────────
public enum EnemyStateType { Controlled, Hurt, Stagger, Dead }
public enum AttackType { Melee, Ranged, Special }
}