赞
踩
本期紧接着上一篇,本期主要内容是实现敌人血条、动画和行为逻辑。
绘制血条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。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。