接到一个需求,在菜单栏中添加一个按钮,用来快速运行入口场景,并且在场景运行结束后,回到上一个正在编辑的场景。第一个功能:快速运行入口场景,已经实现。
本来以为第二个功能会非常简单,不就是保存一下上一个场景路径,场景运行结束后,加载上一个场景不就好了,但是在实际运行过程中出现了很大问题,这个问题在2017和2018的版本中都会产生,就是在编辑器代码定义变量,会多次被重置!原因是,自定义的类,会被多次构造,每次构造类里面的变量就会被重置,最致命的是,向引擎中注册的委托都会被清理掉!不知道是官方故意如此,还是一个bug!
先展示一下,此bug如何重现的。
public class StartMain
{
static string lastOpenSceneName;
static string lastOpenScenePath;
[InitializeOnLoadMethod]
static void Init()
{
EditorApplication.playModeStateChanged += HandleOnPlayModeChanged;
}
[MenuItem("Tools/StartMain", false, 1)]
static void StartMainScene()
{
if (UnityEngine.SceneManagement.SceneManager.GetActiveScene().isDirty)
{
if (EditorUtility.DisplayDialog("提示", "是否保存当前场景", "确定", "取消"))
{
EditorSceneManager.SaveOpenScenes();
}
}
lastOpenSceneName = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name
lastOpenScenePath = UnityEngine.SceneManagement.SceneManager.GetActiveScene().path;
if (!lastOpenSceneName.Equals("Main"))
{
EditorSceneManager.OpenScene("Assets/GameAssets/Map/Main.unity");
}
EditorApplication.ExecuteMenuItem("Edit/Play");
}
static void HandleOnPlayModeChanged(PlayModeStateChange playMode)
{
if (playMode.Equals(PlayModeStateChange.ExitingPlayMode))
{
EditorSceneManager.OpenScene(lastOpenScenePath);
lastOpenScenePath = null;
}
}
}
以上代码看似没有问题,但在运行过程中,Init()
函数会被执行两次,第一次在保存代码后,第二次在开始运行场景时。当点击StartMain
时,场景还没有运行,会给lastOpenScenePath
赋值,但是当场景运行时又再一次执行了Init()
函数,这时lastOpenScenePath
重置为null
,气不气!
可能会有人想,不用[InitializeOnLoadMethod]
这个标签,我用[InitializeOnLoad]
、[RuntimeInitializeOnLoadMethod]
,我已经尝试过了,没用!那干脆不用构造函数什么的,定义这些变量后,直接在StartMainScene
使用!但是依旧会被重置!甚至注册的委托也会被重置掉!
最后实在没办法了,只能使用PlayerPrefs
来保存变量值了。附上完整代码:
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
public class StartMain
{
//注:一定不要使用变量!!!构造函数会执行两次!!!
[InitializeOnLoadMethod]
static void Init()
{
EditorApplication.playModeStateChanged += HandleOnPlayModeChanged;
}
[MenuItem("Tools/StartMain", false, 1)]
static void StartMainScene()
{
if (UnityEngine.SceneManagement.SceneManager.GetActiveScene().isDirty)
{
if (EditorUtility.DisplayDialog("提示", "是否保存当前场景", "确定", "取消"))
{
EditorSceneManager.SaveOpenScenes();
}
}
PlayerPrefs.SetString("LastOpenScenePath", UnityEngine.SceneManagement.SceneManager.GetActiveScene().path);
PlayerPrefs.SetString("LastOpenSceneName", UnityEngine.SceneManagement.SceneManager.GetActiveScene().name);
if (!UnityEngine.SceneManagement.SceneManager.GetActiveScene().name.Equals("Main"))
{
EditorSceneManager.OpenScene("Assets/GameAssets/Map/Main.unity");
}
EditorApplication.ExecuteMenuItem("Edit/Play");
}
[MenuItem("Tools/StartMain", true, 1)]
static bool ValidStartMainScene()
{
return !Application.isPlaying;
}
//当退出场景时,自动打开上一个场景
static void HandleOnPlayModeChanged(PlayModeStateChange playMode)
{
string lastOpenSceneName = PlayerPrefs.GetString("LastOpenSceneName", null);
if (lastOpenSceneName.isNullOrEmpty())
{
return;
}
if (playMode.Equals(PlayModeStateChange.ExitingPlayMode) && !lastOpenSceneName.Equals("Main"))
{
PlayerPrefs.SetString("LastOpenSceneName", null);
//需要等待 完全退出 playmode
EditorApplication.update += UpdateWhenExiting;
}
}
static void UpdateWhenExiting()
{
if (!Application.isPlaying)
{
string lastOpenScenePath = PlayerPrefs.GetString("LastOpenScenePath", null);
if (!lastOpenScenePath.isNullOrEmpty())
{
EditorSceneManager.OpenScene(lastOpenScenePath);
PlayerPrefs.SetString("LastOpenScenePath", null);
}
EditorApplication.update -= UpdateWhenExiting;
}
}
}
网友评论