// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using FlaxEditor.Content.Settings; using FlaxEditor.SceneGraph; using FlaxEditor.Utilities; using FlaxEngine; using FlaxEngine.Utilities; namespace FlaxEditor.States { /// /// In this state editor is simulating game. /// /// [HideInEditor] public sealed class PlayingState : EditorState { private readonly DuplicateScenes _duplicateScenes = new DuplicateScenes(); private readonly List _selectedObjects = new List(); /// /// Gets a value indicating whether any scene was dirty before entering the play mode. /// public bool WasDirty => _duplicateScenes.WasDirty; /// public override bool CanEditScene => true; /// public override bool CanChangeScene => true; /// public override bool CanUseUndoRedo => false; /// public override bool CanEnterPlayMode => true; /// /// Occurs when play mode is starting (before scene duplicating). /// public event Action SceneDuplicating; /// /// Occurs when play mode is starting (after scene duplicating). /// public event Action SceneDuplicated; /// /// Occurs when play mode is applying game settings. Can be used to cache the editor local state of some settings. /// public event Action GameSettingsApplying; /// /// Occurs when play mode applied game settings. Can be used to preserve the editor local state of some settings. /// public event Action GameSettingsApplied; /// /// Occurs when play mode is ending (before scene restoring). /// public event Action SceneRestoring; /// /// Occurs when play mode is ending (after scene restoring). /// public event Action SceneRestored; /// /// Gets or sets a value indicating whether game logic is paused. /// public bool IsPaused { get => Time.GamePaused; set { if (!IsActive) throw new InvalidOperationException(); Time.GamePaused = value; } } /// /// True if play mode is starting. /// public bool IsPlayModeStarting; /// /// True if play mode is ending. /// public bool IsPlayModeEnding; internal PlayingState(Editor editor) : base(editor) { SetupEditorEnvOptions(); } private void CacheSelection() { Profiler.BeginEvent("PlayingState.CacheSelection"); _selectedObjects.Clear(); var selection = Editor.SceneEditing.Selection; if (selection.Count != 0) { for (int i = 0; i < selection.Count; i++) _selectedObjects.Add(selection[i].ID); selection.Clear(); Editor.SceneEditing.OnSelectionChanged(); } Profiler.EndEvent(); } private void RestoreSelection() { Profiler.BeginEvent("PlayingState.RestoreSelection"); var selection = Editor.SceneEditing.Selection; var count = selection.Count; selection.Clear(); for (int i = 0; i < _selectedObjects.Count; i++) { var node = SceneGraphFactory.FindNode(_selectedObjects[i]); if (node != null) selection.Add(node); } if (selection.Count != count) Editor.SceneEditing.OnSelectionChanged(); Profiler.EndEvent(); } /// public override string Status => "Play Mode!"; /// public override void OnEnter() { Profiler.BeginEvent("PlayingState.OnEnter"); IsPlayModeStarting = true; Editor.OnPlayBeginning(); CacheSelection(); // Remove references to the scene objects Editor.Scene.ClearRefsToSceneObjects(); // Apply game settings (user may modify them before the gameplay) GameSettingsApplying?.Invoke(); GameSettings.Apply(); GameSettingsApplied?.Invoke(); // Duplicate editor scene for simulation SceneDuplicating?.Invoke(); _duplicateScenes.GatherSceneData(); Editor.Internal_SetPlayMode(true); IsPaused = false; PluginManager.Internal_InitializeGamePlugins(); _duplicateScenes.CreateScenes(); SceneDuplicated?.Invoke(); RestoreSelection(); Editor.OnPlayBegin(); IsPlayModeStarting = false; Profiler.EndEvent(); Time.Synchronize(); } private void SetupEditorEnvOptions() { Time.TimeScale = 1.0f; Physics.DefaultScene.AutoSimulation = true; Screen.CursorVisible = true; Screen.CursorLock = CursorLockMode.None; } /// public override void UpdateFPS() { // Game drives the Time settings } /// public override void OnExit(State nextState) { Profiler.BeginEvent("PlayingState.OnExit"); IsPlayModeEnding = true; Editor.OnPlayEnding(); IsPaused = true; // Remove references to the scene objects Editor.Scene.ClearRefsToSceneObjects(); // Restore editor scene SceneRestoring?.Invoke(); _duplicateScenes.UnloadScenes(); PluginManager.Internal_DeinitializeGamePlugins(); Editor.Internal_SetPlayMode(false); _duplicateScenes.RestoreSceneData(); SceneRestored?.Invoke(); // Restore game settings and state for editor environment SetupEditorEnvOptions(); var win = Editor.Windows.GameWin?.Root; if (win != null) win.Cursor = CursorType.Default; IsPaused = true; RestoreSelection(); Editor.OnPlayEnd(); IsPlayModeEnding = false; Profiler.EndEvent(); Time.Synchronize(); } } }