//********************************** Banshee Engine (www.banshee3d.com) **************************************************// //**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************// using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.IO; using bs; namespace bs.Editor { /** @addtogroup General * @{ */ /// /// Available tools in the scene view. /// public enum SceneViewTool { View, Move, Rotate, Scale } /// /// Pivot mode used by the scene view tools. /// public enum HandlePivotMode { Center, Pivot } /// /// Coordinate mode used by the scene view tools. /// public enum HandleCoordinateMode { Local, World } /// /// Manages various generic and global settings relating to the editor. /// public class EditorApplication { internal const string CutBinding = "Cut"; internal const string CopyBinding = "Copy"; internal const string RenameBinding = "Rename"; internal const string DuplicateBinding = "Duplicate"; internal const string DeleteBinding = "Delete"; internal const string PasteBinding = "Paste"; internal const string EditorSceneDataPrefix = "__EditorSceneData"; /// /// Determines the active tool shown in the scene view. /// public static SceneViewTool ActiveSceneTool { get { return EditorSettings.ActiveSceneTool; } set { EditorSettings.ActiveSceneTool = value; } } /// /// Determines the coordinate mode used by the tools in the scene view. /// public static HandleCoordinateMode ActiveCoordinateMode { get { return EditorSettings.ActiveCoordinateMode; } set { EditorSettings.ActiveCoordinateMode = value; } } /// /// Determines the pivot mode used by the tools in the scene view. /// public static HandlePivotMode ActivePivotMode { get { return EditorSettings.ActivePivotMode; } set { EditorSettings.ActivePivotMode = value; } } /// /// Camera used for rendering the scene view. /// public static Camera SceneViewCamera { get { SceneWindow sceneWindow = EditorWindow.GetWindow(); return sceneWindow?.Camera.Camera; } } /// /// Absolute path to the folder containing the currently open project. /// public static string ProjectPath { get { return Internal_GetProjectPath(); } } /// /// Name of the currently open project. /// public static string ProjectName { get { return Internal_GetProjectName(); } } /// /// Checks is any project currently loaded. /// public static bool IsProjectLoaded { get { return Internal_GetProjectLoaded(); } } /// /// Determines is the game currently running in the editor, or is it stopped or paused. Setting this value to false /// will stop the game, but if you just want to pause it use property. /// public static bool IsPlaying { get { return Internal_GetIsPlaying(); } set { ToggleToolbarItem("Play", value); ToggleToolbarItem("Pause", false); if (!value) Selection.SceneObject = null; else { if (EditorSettings.GetBool(LogWindow.CLEAR_ON_PLAY_KEY, true)) { Debug.Clear(); LogWindow log = EditorWindow.GetWindow(); if (log != null) log.Refresh(); } } Internal_SetIsPlaying(value); } } /// /// Determines if the game is currently running in the editor, but paused. If the game is stopped and not running /// this will return false. If the game is not running and this is enabled, the game will start running but be /// immediately paused. /// public static bool IsPaused { get { return Internal_GetIsPaused(); } set { ToggleToolbarItem("Play", !value); ToggleToolbarItem("Pause", value); Internal_SetIsPaused(value); } } /// /// Returns true if the game is currently neither running nor paused. Use or /// to actually change these states. /// public static bool IsStopped { get { return !IsPlaying && !IsPaused; } } /// /// Checks whether the editor currently has focus. /// public static bool HasFocus { get { return Internal_HasFocus(); } } /// /// Returns true if the editor is waiting on a scene to be asynchronously loaded. /// public static bool IsSceneLoading { get { if (lastLoadedScene != null) return !lastLoadedScene.IsLoaded; return false; } } /// /// Returns the load progress of the scene that's being asynchronously loaded /// public static float SceneLoadProgress { get { if (lastLoadedScene != null) return Resources.GetLoadProgress(lastLoadedScene); return 0.0f; } } /// /// Triggered right before the project is being saved. /// public static event Action OnProjectSave; /// /// Render target that the main camera in the scene (if any) will render its view to. This generally means the main /// game window when running standalone, or the Game viewport when running in editor. /// internal static RenderTarget MainRenderTarget { set { IntPtr rtPtr = IntPtr.Zero; if (value != null) rtPtr = value.GetCachedPtr(); Internal_SetMainRenderTarget(rtPtr); } } /// /// Returns an object that can be used for storing data that persists throughout the entire editor session. /// internal static EditorPersistentData PersistentData => persistentData; /// /// Editor specific data for the currently loaded scene. Can be null if no scene is loaded. /// internal static EditorSceneData EditorSceneData { get => PersistentData.editorSceneData; private set => PersistentData.editorSceneData = value; } /// /// Returns the path where the script compiler is located at. /// internal static string CompilerPath { get { return Internal_GetCompilerPath(); } } /// /// Returns the absolute path to the executable capable of executing managed assemblies. /// internal static string MonoExecPath { get { return Internal_GetMonoExecPath(); } } /// /// Returns the path to the folder where the custom script assemblies are located at. /// internal static string ScriptAssemblyPath { get { return Internal_GetScriptAssemblyPath(); } } /// /// Returns the path to the folder where the .NET framework assemblies are located at. /// internal static string FrameworkAssemblyPath { get { return Internal_GetFrameworkAssemblyPath(); } } /// /// Name of the builtin assembly containing engine specific types. /// internal static string EngineAssemblyName { get { return Internal_GetEngineAssemblyName(); } } /// /// Name of the builtin assembly containing editor specific types. /// internal static string EditorAssemblyName { get { return Internal_GetEditorAssemblyName(); } } /// /// Name of the custom assembly compiled from non-editor scripts within the project. /// internal static string ScriptGameAssemblyName { get { return Internal_GetScriptGameAssemblyName(); } } /// /// Name of the custom assembly compiled from editor scripts within the project. /// internal static string ScriptEditorAssemblyName { get { return Internal_GetScriptEditorAssemblyName(); } } /// /// Returns the path to the folder where the builtin release script assemblies are located at. /// internal static string BuiltinReleaseAssemblyPath { get { return Internal_GetBuiltinReleaseAssemblyPath(); } } /// /// Returns the path to the folder where the builtin debug script assemblies are located at. /// internal static string BuiltinDebugAssemblyPath { get { return Internal_GetBuiltinDebugAssemblyPath(); } } internal static VirtualButton CutKey = new VirtualButton(CutBinding); internal static VirtualButton CopyKey = new VirtualButton(CopyBinding); internal static VirtualButton PasteKey = new VirtualButton(PasteBinding); internal static VirtualButton RenameKey = new VirtualButton(RenameBinding); internal static VirtualButton DuplicateKey = new VirtualButton(DuplicateBinding); internal static VirtualButton DeleteKey = new VirtualButton(DeleteBinding); private static FolderMonitor monitor; private static ScriptCodeManager codeManager; private static RRef lastLoadedScene; private static bool sceneDirty; private static bool unitTestsExecuted; private static EditorPersistentData persistentData; private static bool delayUnloadProject; private static Action delayUnloadCallback; #pragma warning disable 0414 private static EditorApplication instance; #pragma warning restore 0414 /// /// Constructs a new editor application. Called at editor start-up by the runtime, and any time assembly refresh /// occurs. /// internal EditorApplication() { instance = this; const string soName = "EditorPersistentData"; SceneObject so = Scene.Root.FindChild(soName); if (so == null) so = new SceneObject(soName, true); persistentData = so.GetComponent(); if (persistentData == null) persistentData = so.AddComponent(); codeManager = new ScriptCodeManager(); Scene.OnSceneLoad += OnSceneLoad; Scene.OnSceneUnload += OnSceneUnload; // Register controls InputConfiguration inputConfig = VirtualInput.KeyConfig; inputConfig.RegisterButton(SceneCamera.MoveForwardBinding, ButtonCode.W); inputConfig.RegisterButton(SceneCamera.MoveBackBinding, ButtonCode.S); inputConfig.RegisterButton(SceneCamera.MoveLeftBinding, ButtonCode.A); inputConfig.RegisterButton(SceneCamera.MoveRightBinding, ButtonCode.D); inputConfig.RegisterButton(SceneCamera.MoveUpBinding, ButtonCode.E); inputConfig.RegisterButton(SceneCamera.MoveDownBinding, ButtonCode.Q); inputConfig.RegisterButton(SceneCamera.MoveForwardBinding, ButtonCode.Up); inputConfig.RegisterButton(SceneCamera.MoveBackBinding, ButtonCode.Down); inputConfig.RegisterButton(SceneCamera.MoveLeftBinding, ButtonCode.Left); inputConfig.RegisterButton(SceneCamera.MoveRightBinding, ButtonCode.Right); inputConfig.RegisterButton(SceneCamera.FastMoveBinding, ButtonCode.LeftShift); inputConfig.RegisterButton(SceneCamera.RotateBinding, ButtonCode.MouseRight); inputConfig.RegisterButton(SceneCamera.PanBinding, ButtonCode.MouseMiddle); inputConfig.RegisterAxis(SceneCamera.HorizontalAxisBinding, InputAxis.MouseX); inputConfig.RegisterAxis(SceneCamera.VerticalAxisBinding, InputAxis.MouseY); inputConfig.RegisterAxis(SceneCamera.ScrollAxisBinding, InputAxis.MouseZ); inputConfig.RegisterButton(SceneWindow.ToggleProfilerOverlayBinding, ButtonCode.P, ButtonModifier.CtrlAlt); inputConfig.RegisterButton(SceneWindow.ViewToolBinding, ButtonCode.Q); inputConfig.RegisterButton(SceneWindow.FrameBinding, ButtonCode.F); inputConfig.RegisterButton(SceneWindow.MoveToolBinding, ButtonCode.W); inputConfig.RegisterButton(SceneWindow.RotateToolBinding, ButtonCode.E); inputConfig.RegisterButton(SceneWindow.ScaleToolBinding, ButtonCode.R); inputConfig.RegisterButton(CutBinding, ButtonCode.X, ButtonModifier.Ctrl); inputConfig.RegisterButton(CopyBinding, ButtonCode.C, ButtonModifier.Ctrl); inputConfig.RegisterButton(PasteBinding, ButtonCode.V, ButtonModifier.Ctrl); inputConfig.RegisterButton(DuplicateBinding, ButtonCode.D, ButtonModifier.Ctrl); inputConfig.RegisterButton(DeleteBinding, ButtonCode.Delete); inputConfig.RegisterButton(RenameBinding, ButtonCode.F2); if (IsProjectLoaded) { monitor = new FolderMonitor(ProjectLibrary.ResourceFolder); monitor.OnAdded += OnAssetModified; monitor.OnRemoved += OnAssetModified; monitor.OnModified += OnAssetModified; } } /// /// Updates with the current state of the active scene. /// internal static void UpdateEditorSceneData() { if (EditorSceneData == null) EditorSceneData = EditorSceneData.FromScene(Scene.Root); else EditorSceneData.UpdateFromScene(Scene.Root); HierarchyWindow hierarcyWindow = EditorWindow.GetWindow(); hierarcyWindow?.SaveHierarchyState(EditorSceneData); } /// /// Triggered when the scene has been loaded. /// /// UUID of the loaded scene. private static void OnSceneUnload(UUID uuid) { UpdateEditorSceneData(); string key = EditorSceneDataPrefix + uuid; ProjectSettings.SetObject(key, EditorSceneData); } /// /// Triggered when a scene is about to be unloaded. /// /// UUID of the scene to be unloaded. private static void OnSceneLoad(UUID uuid) { string key = EditorSceneDataPrefix + uuid; EditorSceneData = ProjectSettings.GetObject(key); if (EditorSceneData == null) EditorSceneData = EditorSceneData.FromScene(Scene.Root); } /// /// Triggered when the folder monitor detects an asset in the monitored folder was modified. /// /// Path to the modified file or folder. private static void OnAssetModified(string path) { ProjectLibrary.Refresh(path); } /// /// Called every frame by the runtime. /// internal void OnEditorUpdate() { // Update managers ProjectLibrary.Update(); codeManager.Update(); if (delayUnloadProject) { delayUnloadProject = false; UnloadProject(); delayUnloadCallback?.Invoke(); delayUnloadCallback = null; } } /// /// Manually triggers a global shortcut. /// /// Button for the shortcut. If this doesn't correspond to any shortcut, it is ignored. internal static void TriggerGlobalShortcut(VirtualButton btn) { IGlobalShortcuts window = null; if (btn != PasteKey) { // The system ensures elsewhere that only either a resource or a scene object is selected, but not both if (Selection.ResourcePaths.Length > 0) { window = EditorWindow.GetWindow(); } else if (Selection.SceneObjects.Length > 0) { window = EditorWindow.GetWindow(); if (window == null) window = EditorWindow.GetWindow(); } if (window != null) { if (btn == CopyKey) window.OnCopyPressed(); else if (btn == CutKey) window.OnCutPressed(); else if (btn == PasteKey) window.OnPastePressed(); else if (btn == DuplicateKey) window.OnDuplicatePressed(); else if (btn == RenameKey) window.OnRenamePressed(); else if (btn == DeleteKey) window.OnDeletePressed(); } } else { HierarchyWindow hierarchy = EditorWindow.GetWindow(); if (hierarchy != null && hierarchy.HasFocus) window = hierarchy; else { LibraryWindow library = EditorWindow.GetWindow(); if (library != null && library.HasFocus) window = library; } if (window != null) window.OnPastePressed(); } } /// /// Creates a new empty scene. /// [MenuItem("File/New Scene", 10051, true)] private static void NewScene() { LoadScene(null); } /// /// Opens a dialog that allows the user to select a new prefab to load as the current scene. If current scene /// is modified the user is offered a chance to save it. /// [MenuItem("File/Open Scene", ButtonModifier.Ctrl, ButtonCode.L, 10050)] private static void LoadScene() { string[] scenePaths; if (BrowseDialog.OpenFile(ProjectLibrary.ResourceFolder, "", false, out scenePaths)) { if (scenePaths.Length > 0) LoadScene(scenePaths[0]); } } /// /// Opens a dialog to allows the user to select a location where to save the current scene. If scene was previously /// saved it is instead automatically saved at the last location. /// public static void SaveScene(Action onSuccess = null, Action onFailure = null) { if (!Scene.ActiveSceneUUID.IsEmpty()) { string scenePath = ProjectLibrary.GetPath(Scene.ActiveSceneUUID); if (!string.IsNullOrEmpty(scenePath)) { if (Scene.IsGenericPrefab) { SaveGenericPrefab(onSuccess, onFailure); } else { SaveScene(scenePath); if (onSuccess != null) onSuccess(); } } else SaveSceneAs(onSuccess, onFailure); } else SaveSceneAs(onSuccess, onFailure); } /// /// Opens a dialog to allows the user to select a location where to save the current scene. /// public static void SaveSceneAs(Action onSuccess = null, Action onFailure = null) { string scenePath = ""; if (BrowseDialog.SaveFile(ProjectLibrary.ResourceFolder, "*.prefab", out scenePath)) { if (!PathEx.IsPartOf(scenePath, ProjectLibrary.ResourceFolder)) { DialogBox.Open("Error", "The location must be inside the Resources folder of the project.", DialogBox.Type.OK, x => { if (onFailure != null) onFailure(); }); } else { // TODO - If path points to an existing non-scene asset or folder I should delete it otherwise // Internal_SaveScene will silently fail. scenePath = Path.ChangeExtension(scenePath, ".prefab"); SaveScene(scenePath); } } else { // User canceled, so technically a success if (onSuccess != null) onSuccess(); } } /// /// Loads a prefab as the current scene at the specified path. If current scene is modified the user is offered a /// chance to save it. /// /// Path to a valid prefab relative to the resource folder. If path is empty a brand new /// scene will be loaded. public static void LoadScene(string path) { Action continueLoad = (scenePath) => { if (string.IsNullOrEmpty(path)) { Scene.Clear(); lastLoadedScene = null; } else lastLoadedScene = Scene.LoadAsync(path); SetSceneDirty(false); ProjectSettings.LastOpenScene = scenePath; ProjectSettings.Save(); }; Action dialogCallback = (result) => { if (result == DialogBox.ResultType.Yes) { SaveScene(); continueLoad(path); } else if (result == DialogBox.ResultType.No) continueLoad(path); }; if (IsSceneModified()) { DialogBox.Open("Warning", "You current scene has modifications. Do you wish to save them first?", DialogBox.Type.YesNoCancel, dialogCallback); } else continueLoad(path); } /// /// Saves the currently loaded scene to the specified path. /// /// Path relative to the resource folder. This can be the path to the existing scene /// prefab if it just needs updating. internal static void SaveScene(string path) { Prefab scene = Internal_SaveScene(path); Scene.SetActive(scene); ProjectLibrary.Refresh(true); SetSceneDirty(false); } /// /// Attempts to save the current scene by applying the changes to a prefab, instead of saving it as a brand new /// scene. This is necessary for generic prefabs that have don't have a scene root included in the prefab. If the /// object added any other objects to the root, or has moved or deleted the original generic prefab the user /// will be asked to save the scene normally, creating a brand new prefab. /// private static void SaveGenericPrefab(Action onSuccess = null, Action onFailure = null) { // Find prefab root SceneObject root = null; int numChildren = Scene.Root.GetNumChildren(); int numNormalChildren = 0; for (int i = 0; i < numChildren; i++) { SceneObject child = Scene.Root.GetChild(i); if (EditorUtility.IsInternal(child)) continue; UUID prefabUUID = PrefabUtility.GetPrefabUUID(child); if (prefabUUID == Scene.ActiveSceneUUID) root = child; // If user added any other prefabs other than the initial one, the scene no longer represents a generic // prefab (as we can now longer save it by applying changes only to that prefab) numNormalChildren++; if (numNormalChildren > 1) { root = null; break; } } if (root != null) { PrefabUtility.ApplyPrefab(root, false); ProjectLibrary.Refresh(true); SetSceneDirty(false); if (onSuccess != null) onSuccess(); } else { SaveSceneAs(onSuccess, onFailure); } } /// /// Checks does the folder at the provieded path contain a valid project. /// /// Absolute path to the root project folder. /// True if the folder contains a valid project. public static bool IsValidProject(string path) { return Internal_IsValidProject(path); } /// /// Contains a new project in the provided folder. /// /// Absolute path to the folder to create the project in. Name of this folder will be used as the /// project's name. public static void CreateProject(string path) { Internal_CreateProject(path); } /// /// Wrapper for menu items for method /// [MenuItem("File/Save Scene", ButtonModifier.Ctrl, ButtonCode.S, 10049)] [ToolbarItem("Save Scene", ToolbarIcon.SaveScene, "Save scene (Ctrl + S)", 1998)] private static void SaveSceneMenu() { SaveScene(); } /// /// Wrapper for menu items for method /// [MenuItem("File/Save Scene As", 10048)] private static void SaveSceneAsMenu() { SaveSceneAs(); } /// /// Opens a Project Window allowing you to browse for or create a project. /// [MenuItem("File/Open Project", 10100)] [ToolbarItem("Open Project", ToolbarIcon.OpenProject, "Project manager", 2000)] public static void BrowseForProject() { ProjectWindow.Open(); } /// /// Saves all data in the currently open project. /// [MenuItem("File/Save Project", 10099)] [ToolbarItem("Save Project", ToolbarIcon.SaveProject, "Save project", 1999)] public static void SaveProject() { OnProjectSave?.Invoke(); // Apply changes to any animation clips edited using the animation editor foreach (var KVP in persistentData.dirtyAnimClips) KVP.Value.SaveToClip(); // Save all dirty resources to disk foreach (var KVP in persistentData.dirtyResources) { UUID resourceUUID = KVP.Key; string path = ProjectLibrary.GetPath(resourceUUID); if (!IsNative(path)) continue; // Imported resources can't be changed Resource resource = ProjectLibrary.Load(path); if(resource != null) ProjectLibrary.Save(resource); } persistentData.dirtyAnimClips.Clear(); persistentData.dirtyResources.Clear(); SetStatusProject(false); Internal_SaveProject(); } /// /// Loads the project at the specified path. This method executes asynchronously. /// /// Absolute path to the project's root folder. public static void LoadProject(string path) { if (IsProjectLoaded && path == ProjectPath) return; if (!Internal_IsValidProject(path)) { Debug.LogWarning("Provided path: \"" + path + "\" is not a valid project."); return; } if (IsProjectLoaded) TryUnloadProject(() => Internal_LoadProject(path)); else Internal_LoadProject(path); // Triggers Internal_OnProjectLoaded when done } /// /// Closes the editor. /// public static void Quit() { Internal_Quit(); } /// /// Toggles an existing toolbar button into an on or off state which changes the visuals of the button. /// /// Name of the existing button to toggle /// True to toggle on, false to toggle off (default) public static void ToggleToolbarItem(string name, bool on) { Internal_ToggleToolbarItem(name, on); } /// /// Opens a folder in the default external application. /// /// Absolute path to the folder to open. public static void OpenFolder(string path) { Internal_OpenFolder(path); } /// /// Marks a resource as dirty so that it may be saved the next time the project is saved. Optionally you may also /// call to save it immediately. /// /// Resource to mark as dirty public static void SetDirty(Resource resource) { if (resource == null) return; SetStatusProject(true); persistentData.dirtyResources[resource.UUID] = resource; } /// /// Marks the current project dirty (requires saving in order for changes not to be lost). /// public static void SetProjectDirty() { SetStatusProject(true); } /// /// Marks the current scene as dirty. /// public static void SetSceneDirty() { SetSceneDirty(true); } /// /// Marks the current scene as clean or dirty. /// /// Should the scene be marked as clean or dirty. internal static void SetSceneDirty(bool dirty) { sceneDirty = dirty; SetStatusScene(Scene.ActiveSceneName, dirty); if (!dirty && !Scene.ActiveSceneUUID.IsEmpty()) persistentData.dirtyResources.Remove(Scene.ActiveSceneUUID); } /// /// Checks is the specific resource dirty and needs saving. /// /// Resource to check. /// True if the resource requires saving, false otherwise. public static bool IsDirty(Resource resource) { return persistentData.dirtyResources.ContainsKey(resource.UUID); } /// /// Checks does the path represent a native resource. /// /// Filename or path to check. /// True if the path represents a native resource. public static bool IsNative(string path) { string extension = Path.GetExtension(path); return extension == ".asset" || extension == ".prefab"; } /// /// Attempts to unload the currently loaded project. Offers the user a chance to save the current scene if it is /// modified. Automatically saves all project data before unloading. /// /// Callback to trigger when project project unload is done. private static void TryUnloadProject(Action onDone) { if (delayUnloadProject) return; AskToSaveSceneAndContinue( () => { if (ProjectLibrary.ImportInProgress) { ConfirmImportInProgressWindow.Show(); delayUnloadCallback = onDone; delayUnloadProject = true; } else { UnloadProject(); onDone?.Invoke(); } }); } /// /// Unloads the currently loaded project, without making any checks or requiring confirmation. /// private static void UnloadProject() { Scene.Clear(); if (monitor != null) { monitor.Destroy(); monitor = null; } LibraryWindow window = EditorWindow.GetWindow(); if (window != null) window.Reset(); SetSceneDirty(false); Internal_UnloadProject(); SetStatusProject(false); } /// /// Checks if the current scene is modified and asks the user to save the scene if it is. Triggers the /// callback when done, unless user cancels the operation. /// /// Callback to trigger after this method finishes. internal static void AskToSaveSceneAndContinue(Action next) { Action trySaveScene = null; trySaveScene = () => { SaveScene(next, trySaveScene); }; Action dialogCallback = (result) => { if (result == DialogBox.ResultType.Yes) trySaveScene(); else if (result == DialogBox.ResultType.No) next?.Invoke(); }; if (IsSceneModified()) { DialogBox.Open("Warning", "You current scene has modifications. Do you wish to save them first?", DialogBox.Type.YesNoCancel, dialogCallback); } else next?.Invoke(); } /// /// Reloads all script assemblies in case they were modified. This action is delayed and will be executed /// at the beginning of the next frame. /// public static void ReloadAssemblies() { UpdateEditorSceneData(); Internal_ReloadAssemblies(); } /// /// Changes the scene displayed on the status bar. /// /// Name of the scene. /// Whether to display the scene as modified or not. private static void SetStatusScene(string name, bool modified) { Internal_SetStatusScene(name, modified); } /// /// Changes the project state displayed on the status bar. /// /// Whether to display the project as modified or not. private static void SetStatusProject(bool modified) { Internal_SetStatusProject(modified); } /// /// Displays or hides the "compilation in progress" visual on the status bar. /// /// True to display the visual, false otherwise. internal static void SetStatusCompiling(bool compiling) { Internal_SetStatusCompiling(compiling); } /// /// Displays or hides the "import in progress" visuals on the status bar and updates the related progress bar. /// /// True to display the visual, false otherwise. /// Percent in range [0, 1] to display on the progress bar. internal static void SetStatusImporting(bool importing, float percent) { Internal_SetStatusImporting(importing, percent); } /// /// Checks did we make any modifications to the scene since it was last saved. /// /// True if the scene was never saved, or was modified after last save. public static bool IsSceneModified() { return sceneDirty; } /// /// Runs a single frame of the game and pauses it. If the game is not currently running it will be started. /// public static void FrameStep() { if (IsStopped) { if (EditorSettings.GetBool(LogWindow.CLEAR_ON_PLAY_KEY, true)) { Debug.Clear(); LogWindow log = EditorWindow.GetWindow(); if (log != null) log.Refresh(); } } ToggleToolbarItem("Play", false); ToggleToolbarItem("Pause", true); Internal_FrameStep(); } /// /// Executes any editor-specific unit tests. This should be called after a project is loaded if possible. /// private static void RunUnitTests() { #if DEBUG Internal_RunUnitTests(); #endif } /// /// Triggered by the runtime when method completes. /// private static void Internal_OnProjectLoaded() { SetStatusProject(false); if (!unitTestsExecuted) { RunUnitTests(); unitTestsExecuted = true; } if (!IsProjectLoaded) { ProjectWindow.Open(); return; } string projectPath = ProjectPath; RecentProject[] recentProjects = EditorSettings.RecentProjects; bool foundPath = false; for (int i = 0; i < recentProjects.Length; i++) { if (PathEx.Compare(recentProjects[i].path, projectPath)) { recentProjects[i].accessTimestamp = (ulong)DateTime.Now.Ticks; EditorSettings.RecentProjects = recentProjects; foundPath = true; break; } } if (!foundPath) { List extendedRecentProjects = new List(); extendedRecentProjects.AddRange(recentProjects); RecentProject newProject = new RecentProject(); newProject.path = projectPath; newProject.accessTimestamp = (ulong)DateTime.Now.Ticks; extendedRecentProjects.Add(newProject); EditorSettings.RecentProjects = extendedRecentProjects.ToArray(); } EditorSettings.LastOpenProject = projectPath; EditorSettings.Save(); ProjectLibrary.Refresh(); if(monitor != null) { monitor.Destroy(); monitor = null; } monitor = new FolderMonitor(ProjectLibrary.ResourceFolder); monitor.OnAdded += OnAssetModified; monitor.OnRemoved += OnAssetModified; monitor.OnModified += OnAssetModified; if (!string.IsNullOrWhiteSpace(ProjectSettings.LastOpenScene)) { lastLoadedScene = Scene.LoadAsync(ProjectSettings.LastOpenScene); SetSceneDirty(false); } } /// /// Triggered by the runtime when the user clicks on the status bar. /// private static void Internal_OnStatusBarClicked() { EditorWindow.OpenWindow(); } [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_SetStatusScene(string name, bool modified); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_SetStatusProject(bool modified); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_SetStatusCompiling(bool compiling); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_SetStatusImporting(bool importing, float percent); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetProjectPath(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetProjectName(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern bool Internal_GetProjectLoaded(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetCompilerPath(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetMonoExecPath(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetBuiltinReleaseAssemblyPath(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetBuiltinDebugAssemblyPath(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetScriptAssemblyPath(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetFrameworkAssemblyPath(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetEngineAssemblyName(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetEditorAssemblyName(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetScriptGameAssemblyName(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string Internal_GetScriptEditorAssemblyName(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern Prefab Internal_SaveScene(string path); [MethodImpl(MethodImplOptions.InternalCall)] private static extern bool Internal_IsValidProject(string path); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_SaveProject(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_LoadProject(string path); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_UnloadProject(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_CreateProject(string path); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_ReloadAssemblies(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_OpenFolder(string path); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_RunUnitTests(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_Quit(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_ToggleToolbarItem(string name, bool on); [MethodImpl(MethodImplOptions.InternalCall)] private static extern bool Internal_GetIsPlaying(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_SetIsPlaying(bool value); [MethodImpl(MethodImplOptions.InternalCall)] private static extern bool Internal_GetIsPaused(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_SetIsPaused(bool value); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_FrameStep(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern void Internal_SetMainRenderTarget(IntPtr rendertarget); [MethodImpl(MethodImplOptions.InternalCall)] private static extern bool Internal_HasFocus(); } /** @} */ }