如何在 C# 中存储和调用临时变量

How do I store and call a temporary variable in C#

提前致歉!初学者在这里。我发现自己越来越频繁地使用我将要描述的这个功能来存储临时变量并调用它。我尝试查找它,但没有找到与我的问题具体相关的答案。

我正在尝试做一些非常简单的事情,一旦我理解了它是如何完成的,我就可以将它应用到其他类型(等)。

假设我想改变灯光的颜色,但将原始灯光设置存储在一个变量中,然后在计时器 运行 结束后调用该设置。这是 Unity 中的游戏。

if (powerupEnabled)
        {
            CameraShake.instance.shakeDuration = 5;
            powerupTimer -= Time.deltaTime;
            snakeheadFire.SetActive(true);
            //lightIngame.intensity = 0.8f;
            Color original_color = lightIngame.color; // <<<< Trying to store original color set in game in variable
                            
            lightIngame.color = Color.red;
            if (lightIngame != null)
            {
                // add the amount of time that has passed since last frame
                timeElapsed += Time.deltaTime;

                // if the amount of time passed is greater than or equal to the delay
                if (timeElapsed >= delay)
                {
                    // reset the time elapsed
                    timeElapsed = 0;
                    // toggle the light
                    
                    ToggleLight();
                }
            }
            if (powerupTimer <= 0)
            {
                lightIngame.color = original_color; // <<<< Trying to restore original color set in game from variable
                CameraShake.instance.shakeDuration = 0;
                lightIngame.intensity = 1.12f;
                print("Timer stopped!");
                powerupTimer = 5f;
                snakeheadFire.SetActive(false);
                powerupEnabled = false;
            }
        }

所以基本上我尝试了 Color original_color = lightIngame.color,但是一旦我调回原始颜色,灯光就不会更改为原始设置。两条线(颜色变化)用'<<<<'

引用

我错过了什么?

如果该函数每帧都运行,则每帧都会创建临时变量。您需要一个可以在 powerupTimer 用完之前一直存在的变量。

我建议这样做:

private Color temp_value; // this lives in the class, not in the function.
void Update()
{
    if (powerupEnabled)
    {
        // do stuff

        if (powerupTimer <= 0)
        {
            setPowerupState(false); // disable powerup
        }
    }
}
void setPowerupState(bool enabled)
{
    powerupEnabled = enabled;
    if(enabled)
        temp_value = lightIngame.color; // store the original color once, not every frame.
    else
        lightIngame.color = temp_value; // restore the saved value.
}

C# 有两种不同类型的对象:值类型和引用类型。 值类型是简单类型,如 int、bool、double 等。

在大多数情况下,所有其他/更复杂的 类 都是引用类型,这意味着当您将变量分配给另一个变量时,C# 不会复制对象,而是只分配对象的内存地址。

这意味着,当您调用Color original_color = lightIngame.color;时,C#会将lightIngame.color的对象引用分配给您的变量original_color。该值未被复制,但两个变量都指向同一个对象。

当您之后为 lightIngame.color 分配一个新值时,您同时更改了 lightIngame.colororiginal_color 的值,因为它们都指向同一个对象。

要解决此问题,您需要在保存时创建 lightIngame.color 的副本,例如通过复制构造函数。

这不是特定于 unity 的问题,而是关于 c# 工作原理的一般事实。

可能lightIngame.color是引用类型

如果您在尝试保存其状态时深度复制 lightIngame.color,它可能会起作用。

如果深度复制不是一个选项,您或许可以用代理值表示 lightIngame.color

例如,您也许可以创建一个枚举 ReferenceColours,当您想要存储值时调用

var initialColour = lightIngame.color.ToReferenceColour().

当你想把它设置回来时,你可以这样做

lightIngame.color = var initialColour.ToLightInGameColour()

注意,ToReferenceColour()ToLightInGameColour() 必须是您创建的扩展方法。这里面的逻辑只是简单地处理两者之间的映射。

编辑 - 已通过对此 post 的评论解释说 Color 实际上是一个 UnityEngine.Color,它是一个结构。这意味着它将按值传递,因此上面的解决方案是不必要的。

已经提供了解释和一种可能的解决方案。

但是,您可以完全避免局部变量,方法是不在每帧 Update 中处理它,而是使用 Coroutine,其中在该例程的整个生命周期中都存在局部变量:

void Update()
{
    if (powerupEnabled)
    {
        // Make sure to start the routine only once
        powerupEnabled = false;
        StartCoroutine(PowerUpRoutine());
    }
}

private IEnumerator PowerUpRoutine()
{
    CameraShake.instance.shakeDuration = 5;
    snakeheadFire.SetActive(true);

    var original_color = lightIngame.color;

    lightIngame.color = Color.red;

    var powerupTimer = 5f;
    var timeElapsed = 0f;
    while(powerupTimer > 0 && lightIngame)
    {
        // add the amount of time that has passed since last frame
        timeElapsed += Time.deltaTime;

        // if the amount of time passed is greater than or equal to the delay
        if (timeElapsed >= delay)
        {
            // reset the time elapsed
            timeElapsed = 0;
            // toggle the light
                    
            ToggleLight();
        }

        // This tells Unity to "pause" this routine, render this frame
        // and continue from here in the next frame
        yield return null;

        powerupTimer -= Time.deltaTime;
    }
        
    lightIngame.color = original_color; 
    CameraShake.instance.shakeDuration = 0;
    lightIngame.intensity = 1.12f;
    print("Timer stopped!");
    snakeheadFire.SetActive(false);
}