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

Unity 2D场景切换时静态生命值为何重置为100?

场景切换后静态生命值被重置问题解决

问题描述

两个带有血条的场景均挂载了Stat脚本,原本应显示当前生命值/最大生命值(如50/100),但场景切换后血条会变回100/100,静态变量currentHealth被莫名重置为初始值。

涉及代码

Stat脚本

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

public class Stat : MonoBehaviour
{
    public static int maxHealth;
    public static int currentHealth;

    public GameObject thisObject;
    public Slider slider;
    public TMP_Text statText;

    private void Awake()
    {
        maxHealth = 100;
        SetMaxValue(maxHealth);
        currentHealth = 100;
        SetValue(currentHealth);
    }

    private void Start()
    {
        SceneManager.activeSceneChanged += OnActiveSceneChanged;
    }
    void OnActiveSceneChanged(Scene oldScene, Scene newScene)
    {
        Debug.Log(currentHealth);
    }
    public void SetMaxValue(int value)
    {
        slider.maxValue = value;
        slider.value = value;
    }

    public void SetValue(int value)
    {
        slider.value = value;
        if (thisObject != null)
        {
            if (gameObject.name == "Health")
            {
                statText.text = currentHealth + " / " + maxHealth;
            }
        }
    }
}

Boat脚本

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

public class Boat : MonoBehaviour
{
    public Stat health;
    public GameOverScreen GameOverScreen;

    public void TakeDamage(int amount)
    {
        Stat.currentHealth -= amount;
        health.SetValue(Stat.currentHealth);

        if(Stat.currentHealth <= 0)
            GameOverScreen.GameOver();
    }
}

问题原因

虽然currentHealth是静态变量,但每个场景中的Stat实例在加载时都会执行Awake方法,而你在Awake里直接将currentHealth硬编码设为100。也就是说,切换场景后,新场景的Stat对象执行Awake时,会覆盖掉之前修改过的静态变量值,导致生命值重置。

静态变量属于类本身而非实例,但只要实例的Awake方法里对静态变量赋值,每个实例初始化时都会执行这个赋值操作,和变量是否静态无关。

解决方案

方案1:添加初始化标记避免重复赋值

在Stat类中添加一个静态标记,仅在第一次初始化时设置生命值默认值,后续实例只同步当前静态变量到UI:

public class Stat : MonoBehaviour
{
    public static int maxHealth;
    public static int currentHealth;
    private static bool _isInitialized = false; // 新增初始化标记

    // ... 其他成员变量

    private void Awake()
    {
        if (!_isInitialized)
        {
            // 仅第一次初始化时设置默认值
            maxHealth = 100;
            currentHealth = 100;
            _isInitialized = true;
        }
        // 同步当前静态变量到UI,不修改静态值
        SetMaxValue(maxHealth);
        SetValue(currentHealth);
    }

    // ... 其他方法不变
}

方案2:用DontDestroyOnLoad保持全局唯一实例

让一个Stat实例跨场景持久存在,作为唯一的生命值数据源,其他场景不再重复创建或初始化:

public class Stat : MonoBehaviour
{
    public static int maxHealth;
    public static int currentHealth;

    // ... 其他成员变量

    private void Awake()
    {
        // 检查是否已有实例存在,避免重复创建
        var existingInstances = FindObjectsOfType<Stat>();
        if (existingInstances.Length > 1)
        {
            Destroy(gameObject);
            return;
        }
        // 标记该对象跨场景不被销毁
        DontDestroyOnLoad(gameObject);

        // 仅第一次创建时初始化值
        maxHealth = 100;
        currentHealth = 100;
        SetMaxValue(maxHealth);
        SetValue(currentHealth);
    }

    // ... 其他方法不变
}

注意:这种方案下,其他场景的血条UI需要绑定到这个持久化的Stat实例,或者调整逻辑从该实例获取生命值数据。

方案3:用静态构造函数初始化静态变量

利用C#静态构造函数仅执行一次的特性,将静态变量的初始化与UI初始化分离:

public class Stat : MonoBehaviour
{
    public static int maxHealth;
    public static int currentHealth;

    // 静态构造函数,仅在类第一次被访问时执行一次
    static Stat()
    {
        maxHealth = 100;
        currentHealth = 100;
    }

    // ... 其他成员变量

    private void Awake()
    {
        // 仅同步当前静态变量到UI,不修改静态值
        SetMaxValue(maxHealth);
        SetValue(currentHealth);
    }

    // ... 其他方法不变
}

这种方式最简洁,静态构造函数的执行时机由CLR保证,不会重复触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 02:47:48