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

news2024/11/28 0:38:04

文章目录

  • 先看本次实现的最终效果
  • 前言
  • 把敌人和炮塔全部配置成预制体
  • 炮塔商店
  • 打开商店
  • 放置炮塔
  • 升级炮塔
  • 出售
  • 显示炮塔攻击范围
  • 显示玩家金额和血量
  • 关闭升级面板和商店功能
  • 源码
  • 完结

先看本次实现的最终效果

在这里插入图片描述

前言

本期紧接着上一篇,本期主要内容是实现商店、购买、出售、升级等功能。

把敌人和炮塔全部配置成预制体

在这里插入图片描述

炮塔商店

新增TurretSettings

[CreateAssetMenu(fileName = "炮塔商店设置", menuName = "数据/炮塔商店设置")]
public class TurretSettings : ScriptableObject
{
    [SerializeField]
    public GameObject turretPrefab; // 炮塔预制体
    [SerializeField]
    public int turretShopCost; // 炮塔商店价格
    [SerializeField]
    public Sprite turretShopSprite; // 炮塔商店显示的图标
}

配置不同炮塔参数
在这里插入图片描述

绘制商店UI
在这里插入图片描述

新增TurretCard,渲染商品数据,如果前面有绘制价格文本,还可以渲染价格文本,这里我就偷懒不弄了

public class TurretCard : MonoBehaviour
{
    [SerializeField]
    private Image turretImage; // 炮塔图片
    [SerializeField]
    // private TextMeshProUGUI turretCost; // 炮塔价格文本

    public void SetupTurretButton(TurretSettings turretSettings)
    {
        turretImage.sprite = turretSettings.turretShopSprite; // 设置炮塔图片
        // turretCost.text = turretSettings.turretShopCost.ToString(); // 设置炮塔价格文本
    }
}

挂载脚本,并配置

在这里插入图片描述

新增TurretShopManager,商店管理器

public class TurretShopManager : MonoBehaviour
{
    [SerializeField]
    private GameObject turretCardPrefab; // 炮塔卡片预制体
    [SerializeField]
    private Transform turretPanelContainer; // 放置炮塔卡片的容器

    [Header("炮塔商店配置")]
    [SerializeField]
    private TurretSettings[] turrets; // 可用炮塔设置数组

    private void Start()
    {
        // 在开始时生成炮塔卡片
        for (int i = 0; i < turrets.Length; i++)
        {
            CreateTurretCard(turrets[i]);
        }
    }

    private void CreateTurretCard(TurretSettings turretSettings)
    {
        // 创建炮塔卡片实例
        GameObject newInstance = Instantiate(turretCardPrefab, turretPanelContainer);
        // 设置炮塔卡片实例的比例为默认值
        newInstance.transform.localScale = Vector3.one;
        // 获取炮塔卡片组件,并设置炮塔信息
        TurretCard cardButton = newInstance.GetComponent<TurretCard>();
        cardButton.SetupTurretButton(turretSettings);
    }
}

挂载脚本把那个配置参数
在这里插入图片描述

效果
在这里插入图片描述

打开商店

新增Node ,放置节点

public class Node : MonoBehaviour
{
    public static Action<Node> OnNodeSelected; // 当节点被选中时的事件

    public Turret Turret { get; set; } // 节点上的炮塔

    public void SetTurret(Turret turret)
    {
        Turret = turret; // 设置节点上的炮塔
    }

    public bool IsEmpty()
    {
        return Turret == null; // 判断节点是否为空(没有炮塔)
    }

    public void SelectTurret()
    {
        OnNodeSelected?.Invoke(this); // 触发节点被选中事件
    }
}

挂载并配置点击事件
在这里插入图片描述

新增UI管理器

public class UIManager : MonoBehaviour
{
    [SerializeField, Header("炮塔商店面板")]
    private GameObject turretShopPanel;

    private Node _currentNodeSelected; // 当前选中的节点

    private void NodeSelected(Node nodeSelected)
    {
        _currentNodeSelected = nodeSelected; // 更新当前选中的节点

        if (_currentNodeSelected.IsEmpty())
        {
            turretShopPanel.SetActive(true); // 显示炮塔商店面板
        }
    }

    private void OnEnable()
    {
        Node.OnNodeSelected += NodeSelected; // 注册节点被选中事件的回调方法
    }

    private void OnDisable()
    {
        Node.OnNodeSelected -= NodeSelected; // 取消注册节点被选中事件的回调方法
    }
}

挂载并配置参数
在这里插入图片描述
效果,点击打开商店
在这里插入图片描述

放置炮塔

修改TurretCard

public static Action<TurretSettings> OnPlaceTurret;// 放置炮塔时的事件
public TurretSettings TurretLoaded { get; set; }// 载入的炮塔设置信息

TurretLoaded = turretSettings;

// 放置炮塔的方法
public void PlaceTurret()
{
	// 检查玩家金币是否足够购买炮塔
    if (CurrencySystem.Instance.TotalCoins >= TurretLoaded.turretShopCost)
    {
    	// 扣除对应金币
        CurrencySystem.Instance.RemoveCoins(TurretLoaded.turretShopCost);
        UIManager.Instance.CloseTurretShopPanel();
        // 触发放置炮塔事件,传递炮塔设置信息
        OnPlaceTurret?.Invoke(TurretLoaded);
    }
}

修改UIManager

public void CloseTurretShopPanel()
{
    turretShopPanel.SetActive(false);
}

修改TurretShopManager

private Node _currentNodeSelected;// 当前选中的节点

// 当节点被选中时的回调方法
private void NodeSelected(Node nodeSelected)
{
    _currentNodeSelected = nodeSelected;
}

// 放置炮塔的方法,接受传入的炮塔设置信息
private void PlaceTurret(TurretSettings turretLoaded)
{
    if (_currentNodeSelected != null)
    {
        GameObject turretInstance = Instantiate(turretLoaded.turretPrefab);
        turretInstance.transform.localPosition = _currentNodeSelected.transform.position;
        turretInstance.transform.parent = _currentNodeSelected.transform;
        Turret turretPlaced = turretInstance.GetComponent<Turret>();
        // 在当前节点上设置放置的炮塔
        _currentNodeSelected.SetTurret(turretPlaced);
    }
}

private void OnEnable()
{
    Node.OnNodeSelected += NodeSelected;
    TurretCard.OnPlaceTurret += PlaceTurret;
}

private void OnDisable()
{
    Node.OnNodeSelected -= NodeSelected;
    TurretCard.OnPlaceTurret -= PlaceTurret;
}

配置点击事件
在这里插入图片描述
效果
在这里插入图片描述

升级炮塔

绘制升级UI面板
在这里插入图片描述

修改UIManager,开启升级面板

[SerializeField] private GameObject nodeUIPanel;
[SerializeField] private TextMeshProUGUI upgradeText;
[SerializeField] private TextMeshProUGUI sellText;

private void ShowNodeUI()
{
    nodeUIPanel.SetActive(true);
}

private void NodeSelected(Node nodeSelected)
{
    _currentNodeSelected = nodeSelected; // 更新当前选中的节点

    if (_currentNodeSelected.IsEmpty())
    {
        //。。。
    }
    else
    {
        ShowNodeUI();
    }
}

配置
在这里插入图片描述

修改Turret

public TurretUpgrade TurretUpgrade {get;set;}

TurretUpgrade = GetComponent<TurretUpgrade>();

修改UIManager

private void ShowNodeUI()
{
    nodeUIPanel.SetActive(true);
    UpdateUpgradeText();
}

//升级
public void UpgradeTurret()
{
    _currentNodeSelected.Turret.TurretUpgrade.UpgradeTurret();
    UpdateUpgradeText();
}

private void UpdateUpgradeText()
{
    upgradeText.text = _currentNodeSelected.Turret.TurretUpgrade.UpgradeCost.ToString();
}

配置点击事件
在这里插入图片描述
显示等级
修改TurretUpgrade

public int Level { get; set; }

Level = 1;

private void UpdateUpgrade()
{
    CurrencySystem.Instance.RemoveCoins(UpgradeCost);//扣除金额
    UpgradeCost += upgradeCostIncremental;//加下次升级金额
    Level++;
}

修改UIManager

[SerializeField]private TextMeshProUGUI turretLevelText;

private void ShowNodeUI()
{
    //。。。
    UpdateTurretLevel();
}

//升级
public void UpgradeTurret()
{
    //。。。
    UpdateTurretLevel();
}

private void UpdateTurretLevel(){
    turretLevelText.text = $"等级 {_currentNodeSelected.Turret.TurretUpgrade.Level}";
}

配置等级文本参数
在这里插入图片描述
效果
在这里插入图片描述

出售

修改TurretUpgrade

[Header("出售折扣")]
[Range(0, 1)]
[SerializeField] private float sellPert;
public float SellPerc { get; set; }

SellPerc = sellPert;

//计算出售价格
public int GetSellValue()
{
    int sellValue = Mathf.RoundToInt(UpgradeCost * SellPerc);
    return sellValue;
}

修改UIManager

private void ShowNodeUI()
{
    //。。。
    UpdateSellValue();
}

//升级
public void UpgradeTurret()
{
    //。。。
    UpdateSellValue();
}

private void UpdateSellValue()
{
    int sellAmount = _currentNodeSelected.Turret.TurretUpgrade.GetSellValue();
    sellText.text = sellAmount.ToString();
}

//出售
public void SellTurret()
{
    _currentNodeSelected.SellTurret();
    _currentNodeSelected = null;
    nodeUIPanel.SetActive(false);
}

修改Node

public static Action OnTurretSold;

public void SellTurret()
{
    if (!IsEmpty())
    {
        CurrencySystem.Instance.AddCoins(Turret.TurretUpgrade.GetSellValue());
        Destroy(Turret.gameObject);
        Turret = null;
        OnTurretSold?.Invoke();
    }
}

修改TurretShopManager

private void TurretSold()
{
    _currentNodeSelected = null;
}

private void OnEnable()
{
    Node.OnNodeSelected += NodeSelected;
    TurretCard.OnPlaceTurret += PlaceTurret;
    Node.OnTurretSold += TurretSold;
}

private void OnDisable()
{
    Node.OnNodeSelected -= NodeSelected;
    TurretCard.OnPlaceTurret -= PlaceTurret;
    Node.OnTurretSold -= TurretSold;
}

配置出售点击事件
在这里插入图片描述
配置各个炮塔的出售折扣,比如这里我配置0.5,则只能卖一半的钱
在这里插入图片描述
效果
在这里插入图片描述

显示炮塔攻击范围

修改Node

[SerializeField] private GameObject attackRangeSprite;//攻击范围显示
private float _rangesize;
private Vector3 _rangeOriginalsize;

private void Start()
{
    _rangesize = attackRangeSprite.GetComponent<SpriteRenderer>().bounds.size.y;
    _rangeOriginalsize = attackRangeSprite.transform.localScale;
}

private void ShowTurretInfo()
{
    attackRangeSprite.SetActive(true);
    attackRangeSprite.transform.localScale = _rangeOriginalsize * Turret.attackRange / (_rangesize / 2);
}

public void SelectTurret()
{
    OnNodeSelected?.Invoke(this); // 触发节点被选中事件
    if (!IsEmpty())
    {
        ShowTurretInfo();
    }
}

public void SellTurret()
{
    if (!IsEmpty())
    {
        //。。。
        attackRangeSprite.SetActive(false);
    }
}

配置参数
在这里插入图片描述
效果
在这里插入图片描述

显示玩家金额和血量

修改UIManager

[SerializeField] private TextMeshProUGUI totalCoinsText;
[SerializeField] private TextMeshProUGUI lifesText;
private void Update()
{
    totalCoinsText.text = "金币:" + CurrencySystem.Instance.TotalCoins.ToString();
    lifesText.text = "生命:" + LevelManager.Instance.TotalLives.ToString();
}

配置
在这里插入图片描述
效果
在这里插入图片描述

关闭升级面板和商店功能

修改Node

public void CloseAttackRangeSprite(){
	attackRangeSprite.SetActive(false);
}

修改UIManager

public void CloseNodeUIPanel(){
	currentNodeSelected.CloseAttackRangeSprite();
	nodeUIPanel.SetActive(false);
}

配置
在这里插入图片描述

效果
在这里插入图片描述

关闭商店
在这里插入图片描述

在这里插入图片描述

源码

见本项目最后一节

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

js的File对象,Blob和file相互转换

示例 <!DOCTYPE html> <html><head><meta charset"utf-8" /><meta name"viewport" content"widthdevice-width, initial-scale1" /><title>js的File对象&#xff0c;Blob和file相互转换</title><…

生成对抗网络Generative Adversarial Network,GAN

Basic Idea of GAN Generation&#xff08;生成器&#xff09;  Generation是一个neural network&#xff0c;它的输入是一个vector&#xff0c;它的输出是一个更高维的vector&#xff0c;以图片生成为例&#xff0c;输出就是一张图片&#xff0c;其中每个维度的值代表生…

中级程序员——uniapp和小程序面试题

&#x1f604;博主&#xff1a;小猫娃来啦 &#x1f604;文章核心&#xff1a;uniapp和小程序面试题 文章目录 用uniapp有遇到一些兼容性问题吗&#xff1f;uniapp最大的优点是什么&#xff1f;uniapp如何实现多端兼容&#xff1f;uniapp是如何做跨端适配的&#xff1f;常用的u…

lxml基本使用

lxml是python的一个解析库&#xff0c;支持HTML和XML的解析&#xff0c;支持XPath解析方式&#xff0c;而且解析效率非常高 XPath&#xff0c;全称XML Path Language&#xff0c;即XML路径语言&#xff0c;它是一门在XML文档中查找信息的语言&#xff0c;它最初是用来搜寻XML文…

打造智算中心新标杆,普洛斯数据中心荣获“2023年数据中心科技成果奖”一等奖

11月15&#xff0d;16日&#xff0c;由CDCC主办的2023第十一届数据中心标准大会在北京举行&#xff0c;大会同期举行“2023年数据中心科技成果奖”颁奖典礼。会上&#xff0c;普洛斯数据中心及美团联合申报的“智能算力中心弹性高效节能技术”荣获“2023年数据中心科技成果奖一…

数据服务化在京东平台API接口接入的实践

数据服务化在京东的实践 导读 本次分享的主题为数据服务化在京东的实践&#xff0c;主要包含三个模块&#xff1a;数据服务化的缘起、成长、如何将系统做得更好。 01 缘起&#xff1a;数据服务化从 0 到 1 1. 缘起 京东数据智能部负责维护数据资产和对外提供数据服务&#…

类BERT模型蒸馏原理

如果你曾经训练过 BERT 或 RoBERTa 等大型 NLP 模型&#xff0c;就就会知道这个过程非常漫长。 由于此类模型规模庞大&#xff0c;训练可能会持续数天。 当需要在小型设备上运行它们时&#xff0c;可能会发现你正在为当今不断提高的性能付出巨大的内存和时间成本。 幸运的是&a…

单片机语音芯片在工业控制中的应用优势

单片机语音芯片&#xff0c;这一智能化的代表产品&#xff0c;不仅在家庭和消费电子领域发挥着重要的作用&#xff0c;更为工业控制领域注入了新的活力。将单片机语音芯片与语音交互技术相结合&#xff0c;为工业设备的控制和监测提供了前所未有的解决方案。 首先&#xff0c;…

【Hello Go】Go语言运算符

Go语言运算符 算术运算符关系运算符逻辑运算符位运算符赋值运算符其他运算符运算符优先级 算术运算符 如果之前没有其他语言基础的小伙伴可以参考下我之前写的C语言运算符讲解 这里主要讲解下Go和C运算符的不同点 – 运算符 Go语言中只有后置 和后置– var a int 5a--fmt.P…

宏集干货 | 手把手教你通过CODESYS V3进行PLC编程(三)

来源&#xff1a;宏集科技 工业物联网 宏集干货 | 手把手教你通过CODESYS V3进行PLC编程&#xff08;三&#xff09; 教程背景 通过之前的教程&#xff0c;我们已经为大家演示了宏集MC-Prime控制器的连接、试运行和CODESYS的安装&#xff0c;并创建了一个计数器项目。在本期教…

小型内衣洗衣机什么牌子好?性价比高的迷你洗衣机推荐

现在洗内衣内裤也是一件较麻烦的事情了&#xff0c;在清洗过程中还要用热水杀菌&#xff0c;还要确保洗衣液是否有冲洗干净&#xff0c;还要防止细菌的滋生等等&#xff0c;所以入手一款小型的烘洗全套的内衣洗衣机是非常有必要的&#xff0c;专门的内衣洗衣机可以最大程度减少…

用护眼灯到底好不好?适合小学生用的五款护眼台灯推荐

如果不想家里的孩子年纪小小的就戴着眼镜&#xff0c;从小就容易近视&#xff0c;那么护眼灯的选择就非常重要了&#xff0c;但是市场上那么多品类&#xff0c;价格也参差不齐&#xff0c;到底怎么选呢&#xff1f;大家一定要看完本期内容。为大家推荐五款护眼台灯。 一、书客护…

几种典型的深度学习算法:(CNN、RNN、GANS、RL)

以下是几种典型的深度学习算法&#xff1a; 1、卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;&#xff1a;主要用于图像和视频等视觉任务。通过使用卷积层、池化层和全连接层等不同类型的神经网络层&#xff0c;CNN 可以学习图像中的特征并…

Apache DolphinScheduler在通信行业的多集群统一建设与管理实践

背景介绍 为什么我们考虑构建统一的调度平台&#xff1f; 主要原因是&#xff1a;我们公司的大数据中心目前拥有七个大数据集群&#xff0c;这些集群分布在不同的机房&#xff0c;例如内蒙、南京、苏州和广州。而且&#xff0c;这些机房之间的网络并不互通。如果每个集群都独立…

校园跑腿小程序源码系统+多校园版+取快递+食堂超市跑腿+外卖 带完整的搭建教程

大家好啊&#xff0c;又到了罗峰给大家分享源码的时间啦。今天要给大家分享的是一款校园跑腿小程序源码系统。这款系统功能十分强大&#xff0c;开发了多校园版。众所周知&#xff0c;校园跑腿的发展是从外卖配送一点点演变过来的&#xff0c;而校园跑腿的基础性服务项目就是帮…

智慧城市安全监控的新利器

在传统的城市管理中&#xff0c;井盖的监控一直是一个难题&#xff0c;而井盖异动传感器的出现为这一问题提供了有效的解决方案。它具有体积小、重量轻、安装方便等特点&#xff0c;可以灵活地应用于各种类型的井盖&#xff0c;实现对城市基础设施的全方位监控。 智能井盖监测终…

如何创建react项目

可以直接通过react脚手架进行创建 首先确保自己是否安装了create-react-app npm install -g create-react-app 如果安装不成功有可能是有缓存&#xff0c;可以先清除缓存再重新安装 npm cache clean --force 安装成功后就可以使用脚手架创建自己的项目,my-react就是我的项…

求臻医学:当MRD遇到肺癌 见招拆招,斩草除根

当MRD遇到肺癌→见招拆招&#xff0c;斩草除根 浅谈MRD在肺癌中的应用 新辅助治疗后&#xff0c;可以通过MRD来辅助评估预后&#xff0c;一般MRD阴性与新辅助治疗后的pCR (病理完全缓解)相关&#xff0c;达到MRD阴性或pCR的惠者预后更好. MRD在肺癌中可潜在解决的问题点1.术…

成都瀚网科技有限公司抖音带货是否靠谱

成都瀚网科技有限公司&#xff0c;作为一家专业的科技公司&#xff0c;近年来积极参与了抖音带货的浪潮&#xff0c;凭借其出色的产品和服务&#xff0c;赢得了广大用户的信赖。 在当今的数字化时代&#xff0c;抖音带货已经成为了一种非常流行的购物方式。消费者可以通过抖音平…

Linux中的进程等待(超详细)

Linux中的进程等待 1. 进程等待必要性2. 进程等待的方法2.1 wait方法2.2 waitpid方法 3. 获取子进程status4. 具体代码实现 1. 进程等待必要性 我们知道&#xff0c;子进程退出&#xff0c;父进程如果不管不顾&#xff0c;就可能造成‘僵尸进程’的问题&#xff0c;进而造成内…