文章目录
- 先看本次实现的最终效果
- 前言
- 敌人生命值
- 扣血测试,
- 敌人死亡
- 控制敌人动画
- 敌人死亡动画
- 敌人转向问题
- 源码
- 完结
先看本次实现的最终效果
前言
本期紧接着上一篇,本期主要内容是实现敌人血条、动画和行为逻辑。
敌人生命值
绘制血条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。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~