Unity3d - 在播放模式下加载特定场景

21

好的,我正在做一个小项目,其中有一个主菜单和10个关卡。我不时地编辑不同的关卡,并想尝试它们,但是我遇到了NullPointerException,因为我的关卡依赖于来自主菜单的某些变量才能正常工作,这意味着我必须修改我的关卡,然后加载我的主菜单并从那里开始播放。

在Unity编辑器中是否有方法可以在点击播放时默认加载特定场景,而不是当前场景?

显然,我可以将其解决为类似于

public bool goToMenu; //set this to true in my levels through inspector

Start()
{
    if (goToMenu)
        //then load main menu
}
但如果在播放模式下有一种设置默认级别加载的方法就会非常方便。我查看了偏好设置,但没有找到任何相关选项。
谢谢!

2
你可以看一下这个链接:http://wiki.unity3d.com/index.php/SceneAutoLoader。我自己没有使用过,但听起来可能会有所帮助。 - MotoSV
这是一种极其过时的方法,请不要这样做。如果你非得解决这个问题,虽然不应该这样做,那就使用下面提到的“脚本执行顺序”技巧。 - Fattie
是的:EditorSceneManager.playModeStartScene - Alexis Pautrot
5个回答

29

我写了一个简单的脚本,当你点击播放按钮时,它会加载构建设置中索引为0的场景。希望对某些人有用。

它会检测播放按钮是否被按下并加载场景,然后一切都回到正常状态。

哦!它会在打开Unity和编译脚本后自动执行,所以不需要手动执行它。只需将其放在Editor文件夹中即可运行。

#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;

[InitializeOnLoadAttribute]
public static class DefaultSceneLoader
{
    static DefaultSceneLoader(){
        EditorApplication.playModeStateChanged += LoadDefaultScene;
    }

    static void LoadDefaultScene(PlayModeStateChange state){
        if (state == PlayModeStateChange.ExitingEditMode) {
            EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo ();
        }

        if (state == PlayModeStateChange.EnteredPlayMode) {
            EditorSceneManager.LoadScene (0);
        }
    }
}
#endif

自动编辑器插件似乎是实现这一点的合适方式。 - Programmer
为了更加明确,这个插件位于 Assets/Editor/something.cs 中。因为它是“编辑器插件”。 - user18099
1
对于未来的搜索引擎用户:这在Unity 2022.1.x中可以直接使用,无需将其放入Editor文件夹中。 - rolfrudolfwolf
不幸的是,这仍然会加载您当前所在的场景,然后立即切换到第一个场景。这种方法的问题在于,可能打开的场景会抛出很多错误,因为它不应该是第一个加载的场景。 - Dror
仍在使用2022.3.2f1版本进行工作。是的,它首先加载当前场景并显示大量错误,因为在大多数情况下,它不应该是第一个加载的场景。但是在加载完场景后,所有的错误都将从控制台中消失。 - MGY

17

最简单的方法是将您的第0个场景设置为默认播放模式场景:

[InitializeOnLoad]
public class EditorInit
{
    static EditorInit()
    {
        var pathOfFirstScene = EditorBuildSettings.scenes[0].path;
        var sceneAsset = AssetDatabase.LoadAssetAtPath<SceneAsset>(pathOfFirstScene);
        EditorSceneManager.playModeStartScene = sceneAsset;
        Debug.Log(pathOfFirstScene + " was set as default play mode scene");
    }
}

3
这对我来说完美地运作了,我只是把它放在编辑器文件夹中的一个脚本里。 - Alex
1
如何取消自动加载?我已经删除了这个脚本,但编辑器仍然会加载第一个场景,可能会覆盖playModeStartScene,我需要修复它吗? - Alemkhan Utepkaliev
@AlemkhanUtepkaliev 你可能需要重新启动Unity。 - Fatih Türker
这是正确的方法。它比接受的答案更简单,并且不会产生潜在的错误日志,因为它不会先加载当前场景,然后再切换到其他场景。只需确保为UnityEngine、UnityEditor和UnityEditor.SceneManagement添加using语句即可。 - undefined

0

这是我编写的编辑器脚本,可以完全满足您的需求。 https://github.com/CSaratakij/SceneSelector 它具有选择特定场景的用户界面,并在进入播放模式时打开构建设置中的第一个场景的选项。 对于未来的访问者可能会有用。


被接受的答案是解决问题的方法,不需要在项目中安装更多东西,个人观点。 - MGY

0
我在编辑器文件夹中使用这个。
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;

[InitializeOnLoad]
public class DefaultSceneLoader : EditorWindow
{

    private const string defaultScenePath = "Assets/Scenes/Preload.unity";

    static DefaultSceneLoader()
    {
        EditorApplication.playModeStateChanged += LoadDefaultScene;
    }

    static void LoadDefaultScene(PlayModeStateChange state)
    {
        if (state == PlayModeStateChange.ExitingEditMode)
        {
            if (EditorSceneManager.GetActiveScene().path != defaultScenePath)
            {
               
                PlayerPrefs.SetString("dsl_lastPath", EditorSceneManager.GetActiveScene().path);
                PlayerPrefs.Save();

                EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();

                EditorApplication.delayCall += () =>
                {
                    EditorSceneManager.OpenScene(defaultScenePath);
                    EditorApplication.isPlaying = true;
                };

                EditorApplication.isPlaying = false;
            }
        }

        if (state == PlayModeStateChange.EnteredEditMode)
        {
            if (PlayerPrefs.HasKey("dsl_lastPath"))
            {
                EditorSceneManager.OpenScene(PlayerPrefs.GetString("dsl_lastPath"));
            }
        }
    }
}

-1

这是Unity中一个众所周知的基本问题。

不幸的是,目前没有好的解决方案

当你在游戏引擎中点击“播放”时,它应该跳转到预加载场景并从那里“播放”。

但不幸的是,在Unity中这种情况并不会发生。

每个有经验的Unity工程师都建议不要尝试自动化此过程。但如果你必须这样做...

/* 
ONLY FOR USE DURING DEVELOPMENT IN THE EDITOR
ONLY FOR USE DURING DEVELOPMENT IN THE EDITOR
ONLY FOR USE DURING DEVELOPMENT IN THE EDITOR

MUST RUN FIRST USING "SCRIPT EXECUTION ORDER" system
MUST RUN FIRST USING "SCRIPT EXECUTION ORDER" system
MUST RUN FIRST USING "SCRIPT EXECUTION ORDER" system

YOU MUST REMOVE THIS SCRIPT BEFORE BUILDING TO PRODUCTION
YOU MUST REMOVE THIS SCRIPT BEFORE BUILDING TO PRODUCTION
YOU MUST REMOVE THIS SCRIPT BEFORE BUILDING TO PRODUCTION
*/

using UnityEngine;
public class DevPreload:MonoBehaviour {

    void Awake() {

        GameObject check = GameObject.Find("__app");
        // "__app" is one of your DDOL gameobject in preload scene
        if (check==null)
            SceneManagement.SceneManager.LoadScene("_preload");
        }
    
    }

秘诀在于使用

Unity的脚本执行顺序系统

简单地在任何东西之前运行该脚本,它将按您所希望的方式执行。

然而,请注意,脚本执行顺序系统是一个极其糟糕的工程设计想法,因为它非常不可靠,所以没有人会使用它。

这个“技巧”仅供开发期间使用。 保持清醒!


实际上,忽略上一个评论 - 我刚刚重新回到Unity,因为我问了这个问题,我看了一下你在答案中包含的链接(我想你也问了同样的问题 - 伟大的思维者想得一样!)并且我使用了你在每个人的回答中包含的代码 - 运行得很好! - Tom
好奇的问,你问题中的链接中的代码“安全”吗?内部会有任何编译错误或构建错误吗? - Tom
知道了,谢谢!我会将这个答案标记为正确的,因为你的代码也可以工作 :) - Tom
虽然我同意整个答案,但我想指出,“编程中的全局变量”并不是一件坏事,当你知道自己在做什么时。在某些实际情况下,使用它们甚至比不惜一切代价避免它们更加优雅。但这有点跑题 :) - Masadow
哈哈,你提到那个偶然问题真有趣,@Masadow。确实全局变量有重要的作用。我会把我的偶然例子删掉。 - Fattie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接