C# 在差异场景统一中寻找参照

C# 在差异场景统一中寻找参照,c#,unity3d,C#,Unity3d,我不知道如何找到referenceGameObjectbut different Scene并设置onclick when difference Scene,所以我有一个GameManager,它可以管理所有场景,但只能在主菜单上使用。所以我决定让Dontdestroyonload,问题从这里开始,所以当我玩main游戏场景时,inspector的GameManager字段会找到,但我不能拖放不同的场景,对吗?这使我困惑 如果游戏管理器在主菜单场景中运行,问题是如何在onClick事件中拖放,比

我不知道如何找到reference
GameObject
but different Scene并设置onclick when difference Scene,所以我有一个GameManager,它可以管理所有场景,但只能在主菜单上使用。所以我决定让
Dontdestroyonload
,问题从这里开始,所以当我玩
main游戏
场景时,inspector的GameManager字段会找到,但我不能拖放不同的场景,对吗?这使我困惑

如果游戏管理器在
主菜单
场景中运行,问题是如何在
onClick
事件中拖放,比如我希望暂停按钮处于活动状态或游戏中的其他内容

]

我尝试了
onloadscene(场景s,模式)
,但什么都没发生,这里是GameManager的纸条:

public static GameManager gameManager;

[Header("Main Menu panels")]
public GameObject startPanel;
public GameObject settingPanel;
public GameObject levelPanel;

[Header("InGame Panels")]
#region Panel
public GameObject pausePanel;
public GameObject ObjectivePanel;
public GameObject shopPanel;

private int click = 0;

[Header("Int Tweaks")]
public int indexLevel;
public int onlevel;

public bool isPaused;
_levelSelect LevelSelect;
public static GameManager Instance { set; get; }
public int levelindexPlayerPrefs;

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

void Start()
{
    LevelSelect = FindObjectOfType<_levelSelect>();
    OnStart();
    onlevel = int.Parse(LevelSelect.levelIndex) + 1;
    indexLevel = int.Parse(LevelSelect.levelIndex);
    getPlayerData();
}


// Update is called once per frame
void Update()
{
    ExitApp();
}

public void OnStart()
{
    startPanel.SetActive(true);
    settingPanel.SetActive(false);
    levelPanel.SetActive(false);
}

#region Buttons

public void startbutton()
{
    levelPanel.SetActive(true);
    startPanel.SetActive(false);
    settingPanel.SetActive(false);
}

public void backButtonMainMenu()
{
    levelPanel.SetActive(false);
    startPanel.SetActive(true);
    settingPanel.SetActive(false);
}

public void settingbutton()
{
    levelPanel.SetActive(false);
    startPanel.SetActive(false);
    settingPanel.SetActive(true);
}

public void PauseButton()
{
    Time.timeScale = 0f;
    pausePanel.SetActive(true);
    ObjectivePanel.SetActive(false);
}

public void Resume()
{
    Time.timeScale = 1f;
}

#endregion

public void ExitApp()
{
    if (Input.GetKey(KeyCode.Escape))
    {
        click++;
        StartCoroutine(ClickTime());
        if (click>1)
        {
            print("Exit Game");
            Application.Quit();
        }
    }
}

IEnumerator ClickTime()
{
    yield return new WaitForSeconds(0.5f);
    click = 0;
}

public void getPlayerData()
{
    levelindexPlayerPrefs = PlayerPrefs.GetInt("LevelIndex", 0);
}

public void updateLevel(int Index)
{
    if (levelindexPlayerPrefs < Index)
    {
        PlayerPrefs.SetInt("LevelIndex", Index);
        levelindexPlayerPrefs = PlayerPrefs.GetInt("LevelIndex");
    }
}

#region onloadedScenePickRefferences

private void OnEnable()
{
    SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDisable()
{
    SceneManager.sceneLoaded -= OnSceneLoaded;
}

void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
    pausePanel = GameObject.FindGameObjectWithTag("PausePanel");
    ObjectivePanel = GameObject.FindGameObjectWithTag("ObjectivePanel");
}

#endregion

//public IEnumerator EndChapter()
//{
//    updateLevel(indexLevel + 1);
//    getPlayerData();
//}
公共静态游戏管理器游戏管理器;
[标题(“主菜单面板”)]
公共游戏对象startPanel;
公共游戏对象设置面板;
公共游戏对象级面板;
[标题(“InGame面板”)]
#地区小组
公共游戏对象pausePanel;
公共游戏对象对象面板;
公共游戏对象商店面板;
私有int click=0;
[标题(“整数调整”)]
公共指数级;
公共国际层面;
公共场所;
_levelSelect levelSelect;
公共静态GameManager实例{set;get;}
公共int-levelIndExplayerRefs;
私人空间
{
如果(gameManager!=null)
{
实例=此;
摧毁(游戏对象);
}
其他的
{
DontDestroyOnLoad(游戏对象);
}
}
void Start()
{
LevelSelect=FindObjectOfType();
OnStart();
onlevel=int.Parse(LevelSelect.levelIndex)+1;
indexLevel=int.Parse(LevelSelect.levelIndex);
getPlayerData();
}
//每帧调用一次更新
无效更新()
{
ExitApp();
}
public void OnStart()
{
startPanel.SetActive(真);
settingPanel.SetActive(设置激活)(错误);
levelPanel.SetActive(假);
}
#区域按钮
公共无效开始按钮()
{
levelPanel.SetActive(真);
startPanel.SetActive(假);
settingPanel.SetActive(设置激活)(错误);
}
公共无效backButtonMainMenu()
{
levelPanel.SetActive(假);
startPanel.SetActive(真);
settingPanel.SetActive(设置激活)(错误);
}
公共无效设置按钮()
{
levelPanel.SetActive(假);
startPanel.SetActive(假);
settingPanel.SetActive(真);
}
公共无效暂停按钮()
{
Time.timeScale=0f;
pausePanel.SetActive(真);
ObjectivePanel.SetActive(假);
}
公众简历()
{
Time.timeScale=1f;
}
#端区
公营机构
{
if(Input.GetKey(KeyCode.Escape))
{
点击++;
开始例行程序(单击时间());
如果(单击>1)
{
打印(“退出游戏”);
Application.Quit();
}
}
}
IEnumerator ClickTime()
{
收益率返回新的WaitForSeconds(0.5f);
单击=0;
}
公共无效getPlayerData()
{
levelindexPlayerPrefs=PlayerPrefs.GetInt(“LevelIndex”,0);
}
公共void updateLevel(int索引)
{
if(levelindexPlayerPrefs<索引)
{
PlayerPrefs.SetInt(“LevelIndex”,索引);
levelindexPlayerPrefs=PlayerPrefs.GetInt(“LevelIndex”);
}
}
#区域超负荷情景差异
私有void OnEnable()
{
SceneManager.sceneLoaded+=在sceneLoaded上;
}
私有无效不可撤销()
{
scenemager.sceneLoaded-=在sceneLoaded上;
}
void OnSceneLoaded(场景,加载场景模式)
{
pausePanel=GameObject.FindGameObjectWithTag(“pausePanel”);
ObjectivePanel=GameObject.FindGameObjectWithTag(“ObjectivePanel”);
}
#端区
//公共IEnumerator EndChapter()
//{
//更新级别(索引级别+1);
//getPlayerData();
//}

以下是我可能会做的:

使用
static
类存储和共享所有引用。它不必出现在任何场景中,只需“生活”在资产中:

public static class GlobalReferences
{
    // as example just for one reference but you can implement the rest equally yourself

    // here this class actually stores the reference
    private static GameObject startPanel;

    // A public property in order to add some logic
    // other classes will always access and set the value through this property
    public static GameObject StartPanel
    {
        get
        {
            // if the reference exists return it right away
            if(startPanel) return startPanel;

            // as a fallback try to find it 
            startPanel = GameObject.FindGameObjectWithTag("StartPanel");

            // ofcourse it might still fail when you simply try to access it 
            // in a moment it doesn't exist yet
            return startPanel;
        }

        set
        {
            startPanel = value;

            // invoke an event to tell all listeners that the startPanel
            // was just assigned
            OnStartPanelReady?.Invoke();
        }
    }

    // An event you will invoke after assigning a value making sure that
    // other scripts only access this value after it has been set
    // you can even directly pass the reference in
    public static event Action<GameObject> OnStartPanelReady;
}
在其他场景中,在添加侦听器之前已加载,以便在其他场景准备就绪后立即执行任务:

public class ExampleConsumer : MonoBehaviour
{
    [Header("Debug")]
    [SerializeField] private GameObject startPanel;

    private void Awake()
    {
        // Try to get the reference
        startPanel = GlobalReferences.StartPanel;

        // if this failed then wait until it is ready
        if(!startPanel)
        {
            // it is save to remove callbacks even if not added yet
            // makes sure a listener is always only added once
            GlobalReferences.OnStartPanelReady -= OnStartPanelReady;
            GlobalReferences.OnStartPanelReady += OnStartPanelReady;
        }
        // otherwise already do what you want
        else
        {
            OnStartPanelReady(startPanel);
        }
    }

    private void OnDestroy()
    {
        // always make sure to clean up callbacks when not needed anymore!
        GlobalReferences.OnStartPanelReady -= OnStartPanelReady;
    }

    private void OnStartPanelReady(GameObject newStartPanel)
    {
        startPanel = newStartPanel;
        // always make sure to clean up callbacks when not needed anymore!
        GlobalReferences.OnStartPanelReady -= OnStartPanelReady;

        // NOTE: It is possible that at this point it is null anyway if another
        // class has set this actively to null ;)

        if(startPanel)
        {
            // Now do something with the startPanel
        }
    }
}
另一种情况是,当您需要在新加载的场景中引用主场景时。。。它应该已经设置,因为主场景是首先加载的,并且已经分配了相应的引用



现在,您可以选择这个
静态
类,也可以简单地为每个引用实现相同的逻辑,这些引用需要在相应的组件中直接共享,您可以通过拖放
引用它们。。这没有什么区别,因为您将使用静态字段和事件,这些字段和事件不绑定到任何实例,而是绑定到类型本身。

Unity不能保证同时加载两个场景,因此不允许保存跨场景引用(即,在编辑器中拖放)。您需要管理对象发现(
FindObjectOfType
等)在运行时,就像您已经在
OnSceneLoaded
@中所做的那样,沉浸式是的,但是什么都没有发生,对于
onclick
,这是不可能的referenced@Syarifabdurrahman您也可以在运行时通过类似于
someButton.onClick.AddListener(()=>{DoSomething();})的方式向
onClick
添加回调(但它们不会出现在检查器中!)。但是,该脚本是在主场景中还是在稍后加载的场景中?问题可能是在将回调添加到之前调用了
sceneLoaded
it@derHugo首先在主菜单中,当然,当我进入主游戏场景时,这个脚本也在那个场景中,这就是为什么我使用don't destroy onload,因为我认为如果我在
Awake
中只使用1
GameManager
这会很好,你为什么要这样做
Instance=this;摧毁(游戏对象)??这将立即销毁您刚才分配的引用
实例
,因此它将始终为
null
。这也会立即导致调用
OnDisable
,因此
OnSceneLoaded
可能根本就不会执行?或者。。。实际上,
gameManager
从未设置到
public class ExampleConsumer : MonoBehaviour
{
    [Header("Debug")]
    [SerializeField] private GameObject startPanel;

    private void Awake()
    {
        // Try to get the reference
        startPanel = GlobalReferences.StartPanel;

        // if this failed then wait until it is ready
        if(!startPanel)
        {
            // it is save to remove callbacks even if not added yet
            // makes sure a listener is always only added once
            GlobalReferences.OnStartPanelReady -= OnStartPanelReady;
            GlobalReferences.OnStartPanelReady += OnStartPanelReady;
        }
        // otherwise already do what you want
        else
        {
            OnStartPanelReady(startPanel);
        }
    }

    private void OnDestroy()
    {
        // always make sure to clean up callbacks when not needed anymore!
        GlobalReferences.OnStartPanelReady -= OnStartPanelReady;
    }

    private void OnStartPanelReady(GameObject newStartPanel)
    {
        startPanel = newStartPanel;
        // always make sure to clean up callbacks when not needed anymore!
        GlobalReferences.OnStartPanelReady -= OnStartPanelReady;

        // NOTE: It is possible that at this point it is null anyway if another
        // class has set this actively to null ;)

        if(startPanel)
        {
            // Now do something with the startPanel
        }
    }
}