文章目录
- 先看本次实现的最终效果
- 前言
- 敌人生命值
- 扣血测试,
- 敌人死亡
- 控制敌人动画
- 敌人死亡动画
- 敌人转向问题
- 源码
- 完结
先看本次实现的最终效果
前言
本期紧接着上一篇,本期主要内容是实现敌人血条、动画和行为逻辑。
敌人生命值
绘制血条UI
新建
public class EnemyHealth : MonoBehaviour
{
[SerializeField, Header("血条预制体")] private GameObject healthBarPrefab;
[SerializeField, Header("血条位置")] private Transform barPosition;
[SerializeField, Header("初始生命值")] private float initialHealth = 10f;
[SerializeField, Header("最大生命值")] private float maxHealth = 10f;
private void Start()
{
CreateHealthBar();
}
// 创建血条
private void CreateHealthBar()
{
// 实例化血条预制体并设置位置为敌人的位置
GameObject newBar = Instantiate(healthBarPrefab, barPosition.position, Quaternion.identity);
// 设置血条的父物体为当前敌人,使血条随着敌人移动
newBar.transform.SetParent(transform);
}
}
配置
效果
扣血测试,
新增
public class EnemyHealthContainer : MonoBehaviour {
[SerializeField]private Image fillAmountImage;
public Image FillAmountImage => fillAmountImage;
}
修改EnemyHealth
public class EnemyHealth : MonoBehaviour
{
[SerializeField, Header("血条预制体")] private GameObject healthBarPrefab;
[SerializeField, Header("血条位置")] private Transform barPosition;
[SerializeField, Header("初始生命值")] private float initialHealth = 10f;
[SerializeField, Header("最大生命值")] private float maxHealth = 10f;
public float CurrentHealth { get; set; }
private Image _healthBar;
private void Start()
{
CreateHealthBar();
CurrentHealth = initialHealth;
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
DealDamage(5f);
}
_healthBar.fillAmount = Mathf.Lerp(_healthBar.fillAmount, CurrentHealth / maxHealth, Time.deltaTime * 10f);
}
// 创建血条
private void CreateHealthBar()
{
// 实例化血条预制体并设置位置为敌人的位置
GameObject newBar = Instantiate(healthBarPrefab, barPosition.position, Quaternion.identity);
// 设置血条的父物体为当前敌人,使血条随着敌人移动
newBar.transform.SetParent(transform);
EnemyHealthContainer container = newBar.GetComponent<EnemyHealthContainer>();
_healthBar = container.FillAmountImage;
}
public void DealDamage(float damage)
{
CurrentHealth -= damage;
}
}
效果
敌人死亡
敌人死亡送回池内,并重置健康值,修改EnemyHealth
public static Action OnEnemyKilled;//定义敌人被杀死委托事件
public void ResetHealth(){
CurrentHealth = initialHealth;
_healthBar.fillAmount = 1f;
}
public void DealDamage(float damage)
{
CurrentHealth -= damage;
if (CurrentHealth <= 0)
{
CurrentHealth = 0;
Die();
}
}
private void Die()
{
ResetHealth();
OnEnemyKilled?.Invoke();
ObjectPooler.ReturnToPool(gameObject);
}
修改Spawner,调用敌人死亡委托
private void RecordEnemy()
{
_enemiesRamaining--;
if (_enemiesRamaining <= 0) StartCoroutine(NextWave());
}
private void OnEnable()
{
Enemy.OnEndReached += RecordEnemy;
EnemyHealth.OnEnemyKilled += RecordEnemy;
}
private void OnDisable()
{
Enemy.OnEndReached -= RecordEnemy;
EnemyHealth.OnEnemyKilled -= RecordEnemy;
}
敌人扣除部分生命值,到达终点也要重置生命值
修改Enemy
private EnemyHealth _enemyHealth;
_enemyHealth = GetComponent<EnemyHealth>();
//回收敌人
private void ReturnEnemyToPool()
{
OnEndReached?.Invoke();
_enemyHealth.ResetHealth();
ObjectPooler.ReturnToPool(gameObject);
}
效果
控制敌人动画
添加一个敌人动画控制器
因为敌人的逻辑都一样,除了动画不一样,所以其他敌人我们可以创建动画覆盖控制器实现,修改对应的动画即可
修改EnemyHealth,定义敌人受伤委托
public static Action<Enemy> OnEnemyHit;//定义敌人受伤委托事件
private Enemy _enemy;
_enemy = GetComponent<Enemy>();
public void DealDamage(float damage)
{
CurrentHealth -= damage;
if (CurrentHealth <= 0)
{
//。。。
}
else
{
OnEnemyHit?.Invoke(_enemy);
}
}
新增EnemyAnimations类用于控制敌人的动画播放。PlayHurt方法是一个协程,用于播放受伤动画并暂停敌人的移动一段时间后恢复移动。EnemyHit方法是处理敌人被击中事件的方法,通过判断传入的敌人和当前敌人是否匹配来决定是否播放受伤动画。在OnEnable方法中注册了对EnemyHealth.OnEnemyHit事件的监听,在OnDisable方法中取消了对该事件的监听。
public class EnemyAnimations : MonoBehaviour
{
private Animator _animator; // 敌人动画控制组件
private Enemy _enemy; // 敌人组件
private void Start()
{
_animator = GetComponent<Animator>(); // 获取敌人动画控制组件
_enemy = GetComponent<Enemy>(); // 获取敌人组件
}
// 播放受伤动画
private void PlayHurtAnimation()
{
_animator.SetTrigger("Hurt"); // 设置触发器播放受伤动画
}
// 播放死亡动画
private void PlayDieAnimation()
{
_animator.SetTrigger("Die"); // 设置触发器播放死亡动画
}
// 播放受伤动画的协程
private IEnumerator PlayHurt()
{
_enemy.StopMovement(); // 停止敌人移动
PlayHurtAnimation(); // 播放受伤动画
yield return new WaitForSeconds(GetCurrentAnimationLength() + 0.3f); // 等待当前动画长度+0.3秒
_enemy.ResumeMovement(); // 恢复敌人移动
}
// 敌人被击中的事件处理方法
private void EnemyHit(Enemy enemy)
{
if (_enemy == enemy)
{
StartCoroutine(PlayHurt()); // 开始播放受伤动画的协程
}
}
private void OnEnable()
{
EnemyHealth.OnEnemyHit += EnemyHit; // 注册敌人被击中的事件监听
}
private void OnDisable()
{
EnemyHealth.OnEnemyHit -= EnemyHit; // 取消敌人被击中的事件监听
}
}
修改Enemy
public float MoveSpeed;//保存移速
public void StopMovement()
{
moveSpeed = 0f;
}
public void ResumeMovement()
{
moveSpeed = MoveSpeed;
}
效果
敌人死亡动画
订阅敌人死亡事件
修改EnemyHealth
public static Action<Enemy> OnEnemyKilled;//定义敌人被杀死委托事件
private void Die()
{
// ResetHealth();
OnEnemyKilled?.Invoke(_enemy);
// ObjectPooler.ReturnToPool(gameObject);
}
修改EnemyAnimations
private EnemyHealth _enemyHealth; // 敌人组件
_enemyHealth = GetComponent<EnemyHealth>();
private IEnumerator PlayDead()
{
_enemy.StopMovement();
PlayDieAnimation();
yield return new WaitForSeconds(GetCurrentAnimationLength());
_enemy.ResumeMovement();
_enemyHealth.ResetHealth();
ObjectPooler.ReturnToPool(_enemy.gameObject);
}
private void EnemyDead(Enemy enemy)
{
if (_enemy == enemy)
{
StartCoroutine(PlayDead());
}
}
private void OnEnable()
{
EnemyHealth.OnEnemyHit += EnemyHit; // 注册敌人被击中的事件监听
EnemyHealth.OnEnemyKilled += EnemyDead;
}
private void OnDisable()
{
EnemyHealth.OnEnemyHit -= EnemyHit; // 取消敌人被击中的事件监听
EnemyHealth.OnEnemyKilled -= EnemyDead;
}
修改死亡动画为不循环
效果
敌人转向问题
修改Enemy
private Vector3 _lastPointPosition;
private SpriteRenderer _spriteRenderer;
private void Start()
{
//...
_spriteRenderer = GetComponent<SpriteRenderer>();
_lastPointPosition = transform.position;
}
private void Update()
{
//...
Rotate();
}
//控制转向
private void Rotate()
{
if (CurrentPointPosition.x > _lastPointPosition.x)
{
_spriteRenderer.flipX = false;
}
else
{
_spriteRenderer.flipX = true;
}
}
效果
源码
见本项目最后一节
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,以便我第一时间收到反馈,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~