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
相关产品推荐
相关产品推荐

