【问题标题】:How can I get reference to none static variables and use them inside static methods?如何获取对非静态变量的引用并在静态方法中使用它们?
【发布时间】:2020-10-19 21:52:38
【问题描述】:

或者也许使变量静态并在编辑器中获取对它们的引用。 该脚本位于我的游戏场景中。

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

public class SceneFader : MonoBehaviour
{
    #region FIELDS
    public GameObject fadeOutUIGameobjectImage;
    public float fadeSpeed = 0.8f;
    public bool loaded = false;

    private static Image fadeOutUIImage;

    private void Start()
    {
        
    }

    public enum FadeDirection
    {
        In, //Alpha = 1
        Out // Alpha = 0
    }
    #endregion

    #region FADE
    public static IEnumerator Fade(FadeDirection fadeDirection)
    {
        fadeOutUIGameobjectImage.SetActive(true);

        float alpha = (fadeDirection == FadeDirection.Out) ? 1 : 0;
        float fadeEndValue = (fadeDirection == FadeDirection.Out) ? 0 : 1;
        if (fadeDirection == FadeDirection.Out)
        {
            while (alpha >= fadeEndValue)
            {
                SetColorImage(ref alpha, fadeDirection);
                yield return null;
            }
            fadeOutUIGameobjectImage.SetActive(false);
        }
        else
        {
            fadeOutUIGameobjectImage.SetActive(true);
            while (alpha <= fadeEndValue)
            {
                SetColorImage(ref alpha, fadeDirection);
                yield return null;
            }
        }
    }
    #endregion

    #region HELPERS
    public static IEnumerator FadeAndLoadSceneNewGame(FadeDirection fadeDirection, string sceneToLoad)
    {
        yield return Fade(fadeDirection);

        loaded = false;
        SceneManager.LoadScene(sceneToLoad);   
    }

    public static IEnumerator FadeAndLoadSceneLoadGame(FadeDirection fadeDirection, string sceneToLoad)
    {
        yield return Fade(fadeDirection);

        loaded = true;
        SceneManager.LoadScene(sceneToLoad);

        var saveLoad = GameObject.Find("Save System").GetComponent<SaveLoad>();
        saveLoad.Load();
    }

    private static void SetColorImage(ref float alpha, FadeDirection fadeDirection)
    {
        if(fadeOutUIImage == null)
        {
            fadeOutUIImage = fadeOutUIGameobjectImage.GetComponent<Image>();
        }

        fadeOutUIImage.color = new Color(fadeOutUIImage.color.r, fadeOutUIImage.color.g, fadeOutUIImage.color.b, alpha);
        alpha += Time.deltaTime * (1.0f / fadeSpeed) * ((fadeDirection == FadeDirection.Out) ? -1 : 1);
    }
    #endregion
}

现在,tope 中的变量是公共的,但不是静态的,所以我不能在脚本的其余部分中使用它们,如果我将它们设为静态,我无法在编辑器中引用它们我尝试在当它们是静态的但它们为空时开始。

这个脚本位于我的主菜单场景中,我认为将游戏场景中的两个方法设为 public static 会更容易从主菜单场景中调用它们:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEditor;
using Cinemachine;
using UnityStandardAssets.Characters.ThirdPerson;

public class MenuController : MonoBehaviour
{
    #region Default Values
    [Header("Default Menu Values")]
    [SerializeField] private float defaultVolume;
    [SerializeField] private int defaultSen;
    [SerializeField] private bool defaultInvertY;

    [Header("Levels To Load")]
    public string _newGameButtonLevel;
    private string levelToLoad;
    public GameObject player;

    private int menuNumber;
    #endregion

    #region Menu Dialogs
    [Header("Main Menu Components")]
    [SerializeField] private GameObject menuDefaultCanvas;
    [SerializeField] private GameObject GeneralSettingsCanvas;
    [SerializeField] private GameObject graphicsMenu;
    [SerializeField] private GameObject soundMenu;
    [SerializeField] private GameObject controlsMenu;
    [SerializeField] private GameObject confirmationMenu;
    [Space(10)]
    [Header("Menu Popout Dialogs")]
    [SerializeField] private GameObject noSaveDialog;
    [SerializeField] private GameObject newGameDialog;
    [SerializeField] private GameObject loadGameDialog;
    #endregion

    #region Slider Linking
    [Header("Menu Sliders")]
    [SerializeField] private Text controllerSenText;
    [SerializeField] private Slider controllerSenSlider;
    public float controlSenFloat = 2f;
    [Space(10)]
    [SerializeField] private Text volumeText;
    [SerializeField] private Slider volumeSlider;
    [Space(10)]
    [SerializeField] private Toggle invertYToggle;
    #endregion

    #region Initialisation - Button Selection & Menu Order
    private void Start()
    {
        menuNumber = 1;
    }
    #endregion

    //MAIN SECTION
    public IEnumerator ConfirmationBox()
    {
        confirmationMenu.SetActive(true);
        yield return new WaitForSeconds(2);
        confirmationMenu.SetActive(false);
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            if (menuNumber == 2 || menuNumber == 7 || menuNumber == 8)
            {
                GoBackToMainMenu();
                ClickSound();
            }

            else if (menuNumber == 3 || menuNumber == 4 || menuNumber == 5)
            {
                GoBackToOptionsMenu();
                ClickSound();
            }

            else if (menuNumber == 6) //CONTROLS MENU
            {
                ClickSound();
            }
        }
    }

    private void ClickSound()
    {
        GetComponent<AudioSource>().Play();
    }

    #region Menu Mouse Clicks
    public void MouseClick(string buttonType)
    {
        if (buttonType == "Controls")
        {
            controlsMenu.SetActive(true);
            menuNumber = 6;
        }

        if (buttonType == "Graphics")
        {
            GeneralSettingsCanvas.SetActive(false);
            graphicsMenu.SetActive(true);
            menuNumber = 3;
        }

        if (buttonType == "Sound")
        {
            GeneralSettingsCanvas.SetActive(false);
            soundMenu.SetActive(true);
            menuNumber = 4;
        }

        if (buttonType == "Exit")
        {
            Debug.Log("YES QUIT!");
            Application.Quit();
        }

        if (buttonType == "Options")
        {
            menuDefaultCanvas.SetActive(false);
            GeneralSettingsCanvas.SetActive(true);
            menuNumber = 2;
        }

        if (buttonType == "LoadGame")
        {
            menuDefaultCanvas.SetActive(false);
            loadGameDialog.SetActive(true);
            menuNumber = 8;
        }

        if (buttonType == "NewGame")
        {
            menuDefaultCanvas.SetActive(false);
            newGameDialog.SetActive(true);
            menuNumber = 7;
        }
    }
    #endregion

    public void VolumeSlider(float volume)
    {
        AudioListener.volume = volume;
        volumeText.text = volume.ToString("0.0");
    }

    public void VolumeApply()
    {
        PlayerPrefs.SetFloat("masterVolume", AudioListener.volume);
        Debug.Log(PlayerPrefs.GetFloat("masterVolume"));
        StartCoroutine(ConfirmationBox());
    }

    public void ControllerSen()
    {
        controllerSenText.text = controllerSenSlider.value.ToString("0");
        controlSenFloat = controllerSenSlider.value;
    }

    #region ResetButton
    public void ResetButton(string GraphicsMenu)
    {
        if (GraphicsMenu == "Audio")
        {
            AudioListener.volume = defaultVolume;
            volumeSlider.value = defaultVolume;
            volumeText.text = defaultVolume.ToString("0.0");
            VolumeApply();
        }

        if (GraphicsMenu == "Graphics")
        {
            controllerSenText.text = defaultSen.ToString("0");
            controllerSenSlider.value = defaultSen;
            controlSenFloat = defaultSen;

            invertYToggle.isOn = false;
        }
    }
    #endregion

    #region Dialog Options - This is where we load what has been saved in player prefs!
    public void ClickNewGameDialog(string ButtonType)
    {
        if (ButtonType == "Yes")
        {
            newGameDialog.SetActive(false);
            StartCoroutine(SceneFader.FadeAndLoadSceneNewGame(SceneFader.FadeDirection.In, _newGameButtonLevel));
        }

        if (ButtonType == "No")
        {
            GoBackToMainMenu();
        }
    }

    public void ClickLoadGameDialog(string ButtonType)
    {
        if (ButtonType == "Yes")
        {
            newGameDialog.SetActive(false);
            StartCoroutine(SceneFader.FadeAndLoadSceneLoadGame(SceneFader.FadeDirection.In, _newGameButtonLevel)); 
        }

        if (ButtonType == "No")
        {
            GoBackToMainMenu();
        }
    }
    #endregion

    #region Back to Menus
    public void GoBackToOptionsMenu()
    {
        GeneralSettingsCanvas.SetActive(true);
        graphicsMenu.SetActive(false);
        soundMenu.SetActive(false);

        VolumeApply();

        menuNumber = 2;
    }

    public void GoBackToMainMenu()
    {
        menuDefaultCanvas.SetActive(true);
        newGameDialog.SetActive(false);
        loadGameDialog.SetActive(false);
        noSaveDialog.SetActive(false);
        GeneralSettingsCanvas.SetActive(false);
        graphicsMenu.SetActive(false);
        soundMenu.SetActive(false);
        menuNumber = 1;
    }

    public void ClickQuitOptions()
    {
        GoBackToMainMenu();
    }

    public void ClickNoSaveDialog()
    {
        GoBackToMainMenu();
    }
    #endregion
}

在这个脚本中我有两个事件:

我从主菜单中的 OnClick 按钮调用它们的 ClickNewGameDialog 和 ClickLoadGameDialog。

我创建了这个脚本并将它添加到游戏场景中的一个空游戏对象中:

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

public class NewGame : MonoBehaviour
{
    // Here you store the actual instance
    private static NewGame _instance;

    // Public read-only access property
    public static NewGame Instance
    {
        get
        {
            // if already set simply return directly
            if (_instance) return _instance;

            // Otherwise try to find it in the scene
            _instance = FindObjectOfType<NewGame>();
            if (_instance) return _instance;

            // Otherwise create it now
            _instance = new GameObject(nameof(NewGame)).AddComponent<NewGame>();

            return _instance;
        }
    }

    private bool _gameStarted;
    public static bool GameStarted => Instance._gameStarted;

    private void Awake()
    {
        if (_instance && _instance != this)
        {
            // There already exist another instance 
            Destroy(this.gameObject);
            return;
        }

        // Otherwise this is the active instance and should not be destroyed
        _instance = this;
        DontDestroyOnLoad(this.gameObject);

        SceneManager.sceneLoaded += OnSceneLoaded;

        // update it once now 
        _gameStarted = SceneManager.GetActiveScene().buildIndex != 0;
    }

    void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        _gameStarted = scene.buildIndex != 0;
    }
}

但是当我尝试在游戏场景中使用它时:

if(NewGame.GameStarted != true)

GameStarted 始终为 true 绝不为 false,因此即使玩家单击了“加载游戏”按钮,它也始终在盯着新游戏。

【问题讨论】:

  • 那你为什么要创建static方法来访问实例变量呢?
  • @UnholySheep 因为这个方法在我的游戏场景中,我想从我的主菜单场景中访问这些方法,我从新游戏按钮和加载游戏按钮调用这两个方法。
  • 这并不能解释为什么您将它们设为静态,您可以轻松访问您的类的实例,方法是让这些按钮具有对它的引用,或者在运行时获取它(通过 @ 987654327@电话)
  • @UnholySheep 用我的主菜单场景中的脚本和两个按钮事件更新了我的问题,你能告诉我如何引用游戏场景中的方法吗?我希望能够在游戏场景中的任何脚本中知道玩家是否单击了“新游戏”按钮或“加载游戏”按钮。而且我不确定在主菜单脚本中仅使用公共静态布尔值是否是个好主意。
  • 您可能希望有一个管理器类,它要么是 static class,要么附加到标有 Object.DontDestroyOnLoad 的对象上:docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html。将 static 字段放入 MonoBehaviour 派生类将无法很好地配合 Unity 在场景更改期间处理此类类的方式

标签: c# unity3d


【解决方案1】:

使用单色设计模式。创建一个引用您的脚本的静态变量,如下所示:

public static YourCodeBehaviour Instance;

然后在 Awake 方法中,您只需检查它是否为空,如果为真,则设置它,就像这样。

private void Awake() {
    if(Instance == null)
        Instance = this;
    else {
        Destroy(gameObject);
    }
}

然后你得到了一个实例,你可以从你的静态方法中访问它,因为实例是静态的并且得到了实际的活动实例。

祝你好运!

【讨论】:

    猜你喜欢
    • 2019-03-10
    • 2013-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-25
    相关资源
    最近更新 更多