【用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);
}

配置

效果

关闭商店

源码

见本项目最后一节

目录
相关文章
|
3天前
|
存储 JSON 关系型数据库
【unity实战】制作unity数据保存和加载系统——大型游戏存储的最优解
【unity实战】制作unity数据保存和加载系统——大型游戏存储的最优解
12 2
|
3天前
|
图形学
【制作100个unity游戏之29】使用unity复刻经典游戏《愤怒的小鸟》(完结,附带项目源码)(上)
【制作100个unity游戏之29】使用unity复刻经典游戏《愤怒的小鸟》(完结,附带项目源码)
10 2
|
3天前
|
存储 JSON 图形学
【unity实战】制作unity数据保存和加载系统——小型游戏存储的最优解
【unity实战】制作unity数据保存和加载系统——小型游戏存储的最优解
6 0
|
3天前
|
图形学
【制作100个unity游戏之29】使用unity复刻经典游戏《愤怒的小鸟》(完结,附带项目源码)(下)
【制作100个unity游戏之29】使用unity复刻经典游戏《愤怒的小鸟》(完结,附带项目源码)(下)
8 0
|
3天前
|
存储 JSON 关系型数据库
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版13(完结,附带项目源码)
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版13(完结,附带项目源码)
9 0
|
3天前
|
图形学
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版3(附带项目源码)
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版3(附带项目源码)
14 2
|
3天前
|
图形学
【制作100个unity游戏之28】花半天时间用unity复刻童年4399经典小游戏《黄金矿工》(附带项目源码)
【制作100个unity游戏之28】花半天时间用unity复刻童年4399经典小游戏《黄金矿工》(附带项目源码)
13 0
|
3天前
|
图形学
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版2(附带项目源码)
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版2(附带项目源码)
8 1
|
3天前
|
图形学
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版12(附带项目源码)
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版12(附带项目源码)
9 0
|
3天前
|
存储 图形学
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版11(附带项目源码)
【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版11(附带项目源码)
8 0