【用unity实现100个游戏之15】开发一个类保卫萝卜的Unity2D塔防游戏2(附项目源码)

文章目录

  • 先看本次实现的最终效果
  • 前言
  • 敌人生命值
  • 扣血测试,
  • 敌人死亡
  • 控制敌人动画
  • 敌人死亡动画
  • 敌人转向问题
  • 源码
  • 完结

先看本次实现的最终效果

在这里插入图片描述

前言

本期紧接着上一篇,主要内容是实现本期主要内容是实现敌人血条、动画和行为逻辑。

敌人生命值

绘制血条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。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/153176.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

15 万奖金!开放原子开源大赛OpenAnolis 赛题@你报名

8 月 29 日&#xff0c;2023 开源和信息消费大赛新闻发布会在北京召开&#xff0c;首届“开放原子开源大赛”正式启动报名。大赛由工业和信息化部、江苏省人民政府、湖南省人民政府共同主办&#xff0c;开源赛道拟由开放原子开源基金会、央视网、江苏省工业和信息化厅、无锡市人…

掌握这个技巧,你也能成为资产管理高手!

资产管理是企业管理中至关重要的一环&#xff0c;涉及到对公司财务、物资和信息等各个方面的有效监控和管理。 随着企业规模的扩大和业务复杂性的增加&#xff0c;采用先进的资产管理系统成为确保企业高效运营的必要条件之一。 客户案例 医疗机构 温州某医疗机构拥有大量的医…

超越传统:明懿金汇定义现代金融服务

量化交易的新纪元&#xff1a;明懿金汇引领创新浪潮 在数字化时代的飞速发展下&#xff0c;明懿金汇凭借其独特的跟单平台和卓越的金融服务&#xff0c;成为互联网金融行业的佼佼者。自2020年起&#xff0c;公司重点投资于互联网金融行业&#xff0c;并通过与国内知名证券软件开…

xxl-job配置须知

执行器也有自己的一个接口等 配置xxl-job YAML xxl:job:admin:addresses: http://localhost:8080/xxl-job-admin/executor:appname: media-process-serviceaddress:ip:port: 9999logpath: /data/applogs/xxl-job/jobhandlerlogretentiondays: 30accessToken: default_token--…

java面试常问

文章目录 java 基础1、JDK 和 JRE的区别2、 和equals的区别3、String、StringBuffer、StringBuilder4、String str “a”、 new String(“a”)一样吗&#xff1f;5、ArrayList 和 LinkedList的区别&#xff1f;6、HashMap的原理与实现6.1、容量与扩容6.2、扩容机制 7、HashMa…

智慧汽车—城市NOA迎爆发

在特斯拉引领的 TransformerBev 架构驱动下&#xff0c;智驾算法趋近于端到端的智驾大模型&#xff0c;使得智能驾驶开始步入城市 NOA 新时代。 消费者认知增强&#xff0c;未来市场空间广阔。伴随城市 NOA 在 23-24 年的落地和普及、L3 法规在年内的落地&#xff0c;行业 0-1…

逆向USB设备共享:利用内网穿透让远程设备访问本地USB设备

文章目录 前言1. 安装下载软件1.1 内网安装使用USB Redirector1.2 下载安装cpolar内网穿透 2. 完成USB Redirector服务端和客户端映射连接3. 设置固定的公网地址 前言 USB Redirector是一款方便易用的USB设备共享服务应用程序&#xff0c;它提供了共享和访问本地或互联网上的U…

幸福金龄会《金龄盛典》佛山赛区启动海选

近日&#xff0c;由中国老龄事业发展基金会老年维权管理委员会指导&#xff0c;金龄管理委员会&#xff08;以下简称“金龄会”&#xff09;主办的《金龄盛典》中老年春晚佛山赛区启动演出在佛山桂城万达隆重举行。 此次大赛佛山赛区由金龄会及佛山优秀传统文化促进会联合承办&…

高质量发展项目在京顺利举行

国家卫生健康委出台《关于加强卫生与健康科技成果转移转化工作的指导意见》,提出加快建设协同高效的卫生与健康科技创新体&#xff0c;激发各类创新主体活力;建立协同推进机制&#xff0c;促进科技成果转移转化与适宜技术推广应用。 为促进“医工结合”&#xff0c;提升全国卫…

传统广电媒体为何选择上云?有何优势?

随着现在互联网和科技的发展&#xff0c;现在更多的行业都搭上了科技这辆快车快速的完成了转型&#xff0c;那么在传统的广电媒资行业他们目前有哪些痛点呢&#xff1f;传统广电媒体转型发展现状是什么&#xff1f;企业如何数字化转型&#xff1f;企业上云的优势有哪些&#xf…

mysql数据模型

创建数据库 命令 create database hellox &#xff1a; &#xff08; hellox名字&#xff09; sql语句 创建 数据库 命令 create database hell&#xff1b; 也是创建但是有数据库不创建 命令 create database if not exists hell ; 切换数据库 命令 use hello&…

分享好用的浏览器内截图和滚动截图扩展

一、先打开浏览器安装好这个扩展 我已经安装过了 二、使用方式 单击就行了&#xff0c;如果页面很长&#xff0c;那么会自动滚动截屏。 截图完成后&#xff0c;如图 右上角是一排操作按钮&#xff0c;有删除、下载(pdf和png)、编辑。不过编辑过的图片下载是要开会员的。直接…

Mysql-体系结构

MySQL体系结构 1). 连接层 最上层是一些客户端和链接服务&#xff0c;包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于 TCP/IP的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程 池的概念&#xff0c;为通过认证安全接入的客…

2022年第八届美亚杯个人赛复盘

以学生的身份最后一次打美亚杯了还是要记录一下的写个wp告别哈哈。 1.[单选题] 王晓琳在这本电子书籍里最后对哪段文字加入了重点标示效果(Highlight)?(2分) A. 卿有何妙计 B. 宝玉已是三杯过去了 C. 武松那日早饭罢 D. 就除他做个强马温罢 2.[多选题] 王晓的手机里有一个 …

3+肿瘤+免疫浸润+预后,经典的发文硬套路,解决你发文的烦恼

今天给同学们分享一篇生信文章“Cuproptosis-Related genes in the prognosis of colorectal cancer and their correlation with the tumor microenvironment”&#xff0c;这篇文章发表在Front Genet期刊上&#xff0c;影响因子为3.7。 结果解读&#xff1a; 不同临床特征的…

解决IP查询结果偏差的几个方法

解决IP查询结果偏差的方法可以包括以下几个方面&#xff1a; 选择权威的IP查询工具&#xff1a;使用来自可信来源的IP查询工具&#xff0c;例如官方或专业的IP地址数据库&#xff0c;以确保查询结果的准确性和可靠性。 考虑使用代理服务器或VPN&#xff1a;如果需要更准确的IP…

VSCode 入门操作大全 + 实用插件推荐【零基础专属详细教程】

&#x1f4c0; 前言&#xff1a; &#x1f3ab; 选择一个好的开发工具很重要&#xff0c;很多刚学编程的小伙伴在 webstorm 和 vscode 上很难抉择&#xff0c;我个人更喜欢使用 vscode&#xff0c;因为其有着简洁的操作风格和丰富的人性化的各种功能&#xff0c;这篇文章带给大…

GitHub新手用法详解【适合新手入门-建议收藏!!!】

目录 什么是Github&#xff0c;为什么使用它&#xff1f; 一、GitHub账号的注册与登录 二、 gitbash安装详解 1.git bash的下载与安装 2.git常用命令 3. Git 和 GitHub 的绑定 1. 获取SSH keys 2.绑定ssh密钥 三、通过Git将代码提交到GitHub 1.克隆仓库 2.测试提交代码…

C++实现高频设计模式

面试能说出这几种常用的设计模式即可 1.策略模式 1.1 业务场景 大数据系统把文件推送过来&#xff0c;根据不同类型采取不同的解析方式。多数的小伙伴就会写出以下的代码&#xff1a; if(type"A"){//按照A格式解析 }else if(type"B"){//按照B格式解析 …

在Sprinng Boot中使用Redis充当缓存

关于我们使用EhCache可以适应很多的应用场景了&#xff0c;但是因为EhCache是进程内的缓存框架&#xff0c;在集群模式下&#xff0c;我们在我们的应用服务器或者云服务器之间的缓存都是独立的。故而在不同的服务器之间的进程会存在缓存不一致的情况&#xff0c;就算我们的EhCa…