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

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

先看本次实现的最终效果

前言

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

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

炮塔商店

新增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);
}

配置

效果

关闭商店

源码

见本项目最后一节

目录
相关文章
|
人机交互 开发工具 vr&ar
使用Unity引擎开发Rokid主机应用的模型交互操作
本文介绍如何使用Unity引擎结合Rokid OpenXR Plugin开发空间计算应用,实现射线交互、模型操作等功能。涵盖环境配置、Demo导入、UI搭建与脚本编写,助力开发者快速构建AR交互应用。
|
机器学习/深度学习 人工智能 图形学
卓伊凡的第一款独立游戏-详细介绍游戏开发引擎unity-以及详细介绍windows和mac的安装步骤【01】
卓伊凡的第一款独立游戏-详细介绍游戏开发引擎unity-以及详细介绍windows和mac的安装步骤【01】
880 9
|
小程序 PHP 图形学
热门小游戏源码(Python+PHP)下载-微信小程序游戏源码Unity发实战指南​
本文详解如何结合Python、PHP与Unity开发并部署小游戏至微信小程序。涵盖技术选型、Pygame实战、PHP后端对接、Unity转换适配及性能优化,提供从原型到发布的完整指南,助力开发者快速上手并发布游戏。
|
C# 图形学 开发者
Unity开发中使用UnityWebRequest从HTTP服务器下载资源。
总之,UnityWebRequest就是游戏开发者手中的万能钓鱼竿,既可以获取文本数据,也能钓上图片资源,甚至是那声音的涟漪。使用UnityWebRequest的时候,你需要精心准备,比如确定URL、配置请求类型和头信息;发起请求;巧妙处理钓获的数据;还需要机智面对网络波澜,处理各种可能出现的错误。按照这样的过程,数据的钓取将会是一次既轻松愉快也效率高效的编程钓鱼之旅。
864 18
|
测试技术 C# 图形学
掌握Unity调试与测试的终极指南:从内置调试工具到自动化测试框架,全方位保障游戏品质不踩坑,打造流畅游戏体验的必备技能大揭秘!
【9月更文挑战第1天】在开发游戏时,Unity 引擎让创意变为现实。但软件开发中难免遇到 Bug,若不解决,将严重影响用户体验。调试与测试成为确保游戏质量的最后一道防线。本文介绍如何利用 Unity 的调试工具高效排查问题,并通过 Profiler 分析性能瓶颈。此外,Unity Test Framework 支持自动化测试,提高开发效率。结合单元测试与集成测试,确保游戏逻辑正确无误。对于在线游戏,还需进行压力测试以验证服务器稳定性。总之,调试与测试贯穿游戏开发全流程,确保最终作品既好玩又稳定。
1458 4
|
前端开发 图形学 开发者
【独家揭秘】那些让你的游戏瞬间鲜活起来的Unity UI动画技巧:从零开始打造动态按钮,提升玩家交互体验的绝招大公开!
【9月更文挑战第1天】在游戏开发领域,Unity 是最受欢迎的游戏引擎之一,其强大的跨平台发布能力和丰富的功能集让开发者能够迅速打造出高质量的游戏。优秀的 UI 设计对于游戏至关重要,尤其是在手游市场,出色的 UI 能给玩家留下深刻的第一印象。Unity 的 UGUI 系统提供了一整套解决方案,包括 Canvas、Image 和 Button 等组件,支持添加各种动画效果。
1563 3
|
设计模式 存储 人工智能
深度解析Unity游戏开发:从零构建可扩展与可维护的游戏架构,让你的游戏项目在模块化设计、脚本对象运用及状态模式处理中焕发新生,实现高效迭代与团队协作的完美平衡之路
【9月更文挑战第1天】游戏开发中的架构设计是项目成功的关键。良好的架构能提升开发效率并确保项目的长期可维护性和可扩展性。在使用Unity引擎时,合理的架构尤为重要。本文探讨了如何在Unity中实现可扩展且易维护的游戏架构,包括模块化设计、使用脚本对象管理数据、应用设计模式(如状态模式)及采用MVC/MVVM架构模式。通过这些方法,可以显著提高开发效率和游戏质量。例如,模块化设计将游戏拆分为独立模块。
1276 3
|
图形学 C#
超实用!深度解析Unity引擎,手把手教你从零开始构建精美的2D平面冒险游戏,涵盖资源导入、角色控制与动画、碰撞检测等核心技巧,打造沉浸式游戏体验完全指南
【8月更文挑战第31天】本文是 Unity 2D 游戏开发的全面指南,手把手教你从零开始构建精美的平面冒险游戏。首先,通过 Unity Hub 创建 2D 项目并导入游戏资源。接着,编写 `PlayerController` 脚本来实现角色移动,并添加动画以增强视觉效果。最后,通过 Collider 2D 组件实现碰撞检测等游戏机制。每一步均展示 Unity 在 2D 游戏开发中的强大功能。
1774 6
|
图形学 缓存 算法
掌握这五大绝招,让您的Unity游戏瞬间加载完毕,从此告别漫长等待,大幅提升玩家首次体验的满意度与留存率!
【8月更文挑战第31天】游戏的加载时间是影响玩家初次体验的关键因素,特别是在移动设备上。本文介绍了几种常见的Unity游戏加载优化方法,包括资源的预加载与异步加载、使用AssetBundles管理动态资源、纹理和模型优化、合理利用缓存系统以及脚本优化。通过具体示例代码展示了如何实现异步加载场景,并提出了针对不同资源的优化策略。综合运用这些技术可以显著缩短加载时间,提升玩家满意度。
2056 6
|
图形学 开发者 存储
超越基础教程:深度拆解Unity地形编辑器的每一个隐藏角落,让你的游戏世界既浩瀚无垠又细节满满——从新手到高手的全面技巧升级秘籍
【8月更文挑战第31天】Unity地形编辑器是游戏开发中的重要工具,可快速创建复杂多变的游戏环境。本文通过比较不同地形编辑技术,详细介绍如何利用其功能构建广阔且精细的游戏世界,并提供具体示例代码,展示从基础地形绘制到植被与纹理添加的全过程。通过学习这些技巧,开发者能显著提升游戏画面质量和玩家体验。
1697 3

热门文章

最新文章