You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity C#农场游戏等级系统问题:无法限制指定等级种植作物

Unity农场游戏「等级限制作物种植」功能修复

我正在使用Unity开发一款农场游戏,已实现EXP系统,但无法完成「达到指定玩家等级才可种植对应作物」的功能。尝试修改代码后,不仅未禁用未达等级的作物按钮,还出现大量错误,卡壳数周,寻求解决方案。

原代码

管理农场的FarmManager脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class FarmManager : MonoBehaviour
{
    [Header("Money Settings")]
    public PlantItem selectPlant;
    public bool isPlanting = false;
    public int money = 100;
    public Text moneyTxt;

    [Header("Button Color Settings")]
    public Color buyColor = Color.green;
    public Color cancelColor = Color.red;
    public Color lockedColor = Color.grey;

    [Header("Button Audio Settings")]
    public AudioSource clickSound;

    [Header("Cursor Settings")]
    public Texture2D cursorArrow;
    public Texture2D cursorPickaxe;

    [Header("XP Settings")]
    public int maxExp;
    public float updatedExp;
    public float plusXp;
    public Image ExpBar;
    public float expIncreasedPerSecond;
    public int playerLevel;
    public Text levelText;
    public Text xpLevel;

    // Start is called before the first frame update
    void Start()
    {
        Cursor.SetCursor(cursorArrow, Vector2.zero, CursorMode.ForceSoftware);
        moneyTxt.text = "$"+money;
        playerLevel =1;
        expIncreasedPerSecond =5f;
        maxExp = 25;
        updatedExp = 0;
        plusXp = 0;
        
    }


    void Update () {
        //updatedExp += expIncreasedPerSecond * Time.deltaTime;
        
        ExpBar.fillAmount = updatedExp / maxExp;

        levelText.text = playerLevel + "";
        xpLevel.text = updatedExp + "/" + maxExp;

        if (updatedExp >= maxExp)
        {
            plusXp = updatedExp-maxExp;
            playerLevel++;
            updatedExp=0+plusXp;
            maxExp+=maxExp;
        }
    }


    public void SelectPlant(PlantItem newPlant)
    {
        if(selectPlant == newPlant)
        {
            Debug.Log("Deselected" + selectPlant.plant.plantName);
            selectPlant.btnImage.color = buyColor;
            selectPlant.btnTxt.text = "Buy";
            selectPlant = null;
            isPlanting = false;
            clickSound.Play();
            
        }
        else
        {
            if(selectPlant!=null)
            {
                selectPlant.btnImage.color = buyColor;
                selectPlant.btnTxt.text = "Buy";
            }
            
            selectPlant = newPlant;
            selectPlant.btnImage.color = cancelColor;
            selectPlant.btnTxt.text = "Cancel";
            Debug.Log("Selected" + selectPlant.plant.plantName);
            isPlanting = true;
            clickSound.Play();

        }
    }

    public void Transaction(int value)
    {
        money+=value;
        moneyTxt.text = "$"+money;
    }


    public void GiveXP (float value) 
    {
        updatedExp += value;
    }
    
}

作物信息PlantObject可编辑对象代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "New Plant", menuName ="Plant")]

public class PlantObject : ScriptableObject
{
    [Header("Plant settings")]
    public string plantName;
    public Sprite[] plantStages;
    public float timeBtwStages;
    [Header("Money Settings")]
    public int buyPrice;
    public int sellPrice;
    [Header("XP Settings")]
    public int xpAmount;
    public int levelNeeded;
    [Header("Plant Icon")]
    public Sprite icon;
}

尝试修改后的错误代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class FarmManager : MonoBehaviour
{
    [Header("Money Settings")]
    public PlantItem selectPlant;
    public bool isPlanting = false;
    public int money = 100;
    public Text moneyTxt;

    [Header("Button Color Settings")]
    public Color buyColor = Color.green;
    public Color cancelColor = Color.red;
    public Color lockedColor = Color.grey;

    [Header("Button Audio Settings")]
    public AudioSource clickSound;

    [Header("Cursor Settings")]
    public Texture2D cursorArrow;
    public Texture2D cursorPickaxe;

    [Header("XP Settings")]
    public int maxExp;
    public float updatedExp;
    public float plusXp;
    public Image ExpBar;
    public float expIncreasedPerSecond;
    public int playerLevel;
    public Text levelText;
    public Text xpLevel;

    // Start is called before the first frame update
    void Start()
    {
        Cursor.SetCursor(cursorArrow, Vector2.zero, CursorMode.ForceSoftware);
        moneyTxt.text = "$"+money;
        playerLevel =1;
        expIncreasedPerSecond =5f;
        maxExp = 25;
        updatedExp = 0;
        plusXp = 0;
        
    }


    void Update () {
        //updatedExp += expIncreasedPerSecond * Time.deltaTime;
        
        ExpBar.fillAmount = updatedExp / maxExp;

        levelText.text = playerLevel + "";
        xpLevel.text = updatedExp + "/" + maxExp;

        if (updatedExp >= maxExp)
        {
            plusXp = updatedExp-maxExp;
            playerLevel++;
            updatedExp=0+plusXp;
            maxExp+=maxExp;
        }
    }


    public void SelectPlant(PlantItem newPlant)
    {
        if(selectPlant == newPlant)
        {
            Debug.Log("Deselected" + selectPlant.plant.plantName);
            selectPlant.btnImage.color = buyColor;
            selectPlant.btnTxt.text = "Buy";
            selectPlant = null;
            isPlanting = false;
            clickSound.Play();
            
        }
        else
        {
            if(selectPlant!=null)
            {
                selectPlant.btnImage.color = buyColor;
                selectPlant.btnTxt.text = "Buy";
            }
            
            selectPlant = newPlant;
            selectPlant.btnImage.color = cancelColor;
            selectPlant.btnTxt.text = "Cancel";
            Debug.Log("Selected" + selectPlant.plant.plantName);
            isPlanting = true;
            clickSound.Play();

        }
        if (selectPlant.plant.levelNeeded > playerLevel)
        {
            selectPlant.btnImage.color = lockedColor;

        }

    }

    public void Transaction(int value)
    {
        money+=value;
        moneyTxt.text = "$"+money;
    }


    public void GiveXP (float value) 
    {
        updatedExp += value;
    }
    
}

问题分析与修复方案

核心问题

  1. 空引用错误:取消选择作物后selectPlant为null,此时访问selectPlant.plant.levelNeeded会触发空引用异常。
  2. 未初始化按钮状态:游戏启动时未根据初始等级设置作物按钮锁定状态,未达等级的按钮仍显示可点击。
  3. 等级提升后未更新按钮:玩家升级后,未自动解锁对应等级的作物按钮。
  4. 锁定作物仍可被选择:点击未达等级的作物按钮时,仍会执行选择逻辑,没有拦截。

修复后的完整代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class FarmManager : MonoBehaviour
{
    [Header("Money Settings")]
    public PlantItem selectPlant;
    public bool isPlanting = false;
    public int money = 100;
    public Text moneyTxt;

    [Header("Button Color Settings")]
    public Color buyColor = Color.green;
    public Color cancelColor = Color.red;
    public Color lockedColor = Color.grey;

    [Header("Button Audio Settings")]
    public AudioSource clickSound;

    [Header("Cursor Settings")]
    public Texture2D cursorArrow;
    public Texture2D cursorPickaxe;

    [Header("XP Settings")]
    public int maxExp;
    public float updatedExp;
    public float plusXp;
    public Image ExpBar;
    public float expIncreasedPerSecond;
    public int playerLevel;
    public Text levelText;
    public Text xpLevel;

    // 存储所有作物按钮的列表,需在Inspector中赋值
    public List<PlantItem> allPlantItems;

    // Start is called before the first frame update
    void Start()
    {
        Cursor.SetCursor(cursorArrow, Vector2.zero, CursorMode.ForceSoftware);
        moneyTxt.text = "$"+money;
        playerLevel =1;
        expIncreasedPerSecond =5f;
        maxExp = 25;
        updatedExp = 0;
        plusXp = 0;

        // 初始化所有作物按钮状态
        UpdatePlantButtonStates();
    }


    void Update () {
        //updatedExp += expIncreasedPerSecond * Time.deltaTime;
        
        ExpBar.fillAmount = updatedExp / maxExp;

        levelText.text = playerLevel + "";
        xpLevel.text = updatedExp + "/" + maxExp;

        if (updatedExp >= maxExp)
        {
            plusXp = updatedExp-maxExp;
            playerLevel++;
            updatedExp=0+plusXp;
            maxExp+=maxExp;

            // 升级后更新所有作物按钮状态
            UpdatePlantButtonStates();
        }
    }


    public void SelectPlant(PlantItem newPlant)
    {
        // 先判断当前作物是否锁定
        if (newPlant.plant.levelNeeded > playerLevel)
        {
            Debug.Log("等级不足,无法种植" + newPlant.plant.plantName);
            newPlant.btnImage.color = lockedColor;
            newPlant.btnTxt.text = "锁定";
            return; // 直接返回,不执行选择逻辑
        }

        if(selectPlant == newPlant)
        {
            Debug.Log("Deselected" + selectPlant.plant.plantName);
            selectPlant.btnImage.color = buyColor;
            selectPlant.btnTxt.text = "Buy";
            selectPlant = null;
            isPlanting = false;
            clickSound.Play();
            
        }
        else
        {
            if(selectPlant!=null)
            {
                selectPlant.btnImage.color = buyColor;
                selectPlant.btnTxt.text = "Buy";
            }
            
            selectPlant = newPlant;
            selectPlant.btnImage.color = cancelColor;
            selectPlant.btnTxt.text = "Cancel";
            Debug.Log("Selected" + selectPlant.plant.plantName);
            isPlanting = true;
            clickSound.Play();
        }
    }

    // 更新所有作物按钮的锁定/可用状态
    private void UpdatePlantButtonStates()
    {
        foreach (var plantItem in allPlantItems)
        {
            if (plantItem.plant.levelNeeded > playerLevel)
            {
                plantItem.btnImage.color = lockedColor;
                plantItem.btnTxt.text = "锁定";
                plantItem.GetComponent<Button>().interactable = false; // 禁用按钮点击
            }
            else
            {
                plantItem.btnImage.color = buyColor;
                plantItem.btnTxt.text = "Buy";
                plantItem.GetComponent<Button>().interactable = true; // 启用按钮点击
            }
        }
    }

    public void Transaction(int value)
    {
        money+=value;
        moneyTxt.text = "$"+money;
    }


    public void GiveXP (float value) 
    {
        updatedExp += value;
    }
    
}

关键修改点说明

  1. 新增allPlantItems列表:在Inspector中把所有作物按钮的PlantItem组件拖入列表,方便批量更新状态。
  2. 初始化与升级时更新按钮:在Start和玩家升级时调用UpdatePlantButtonStates,确保按钮状态与当前等级匹配。
  3. 拦截锁定作物的点击:在SelectPlant开头先判断等级,不足则直接返回,不执行选择逻辑,并设置按钮为锁定状态。
  4. 禁用锁定按钮的交互:在UpdatePlantButtonStates中设置按钮的interactable属性,彻底禁止未达等级按钮的点击事件。

额外注意事项

  • 确保PlantItem类包含btnImage、btnTxt和plant(引用PlantObject)的公共字段,否则代码会报错。
  • 在Inspector中正确赋值allPlantItems列表,把所有作物按钮对应的PlantItem组件添加进去。

内容的提问来源于stack exchange,提问作者Fl4shÝ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 01:55:19