Files
FlaxEngine/Source/Editor/States/PlayingState.cs
T
Ari Vuollet a2b730e5eb Fix inconsistent Time.GameTime when entering play mode
GameWindow entering effect does not reset properly sometimes when
play mode is entered due to game time getting reset after the
`OnPlayBegin` event.
2026-08-25 23:43:52 +03:00

220 lines
6.8 KiB
C#

// Copyright (c) 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
{
/// <summary>
/// In this state editor is simulating game.
/// </summary>
/// <seealso cref="FlaxEditor.States.EditorState" />
[HideInEditor]
public sealed class PlayingState : EditorState
{
private readonly DuplicateScenes _duplicateScenes = new DuplicateScenes();
private readonly List<Guid> _selectedObjects = new List<Guid>();
/// <summary>
/// Gets a value indicating whether any scene was dirty before entering the play mode.
/// </summary>
public bool WasDirty => _duplicateScenes.WasDirty;
/// <inheritdoc />
public override bool CanEditScene => true;
/// <inheritdoc />
public override bool CanChangeScene => true;
/// <inheritdoc />
public override bool CanUseUndoRedo => false;
/// <inheritdoc />
public override bool CanEnterPlayMode => true;
/// <summary>
/// Occurs when play mode is starting (before scene duplicating).
/// </summary>
public event Action SceneDuplicating;
/// <summary>
/// Occurs when play mode is starting (after scene duplicating).
/// </summary>
public event Action SceneDuplicated;
/// <summary>
/// Occurs when play mode is applying game settings. Can be used to cache the editor local state of some settings.
/// </summary>
public event Action GameSettingsApplying;
/// <summary>
/// Occurs when play mode applied game settings. Can be used to preserve the editor local state of some settings.
/// </summary>
public event Action GameSettingsApplied;
/// <summary>
/// Occurs when play mode is ending (before scene restoring).
/// </summary>
public event Action SceneRestoring;
/// <summary>
/// Occurs when play mode is ending (after scene restoring).
/// </summary>
public event Action SceneRestored;
/// <summary>
/// Gets or sets a value indicating whether game logic is paused.
/// </summary>
public bool IsPaused
{
get => Time.GamePaused;
set
{
if (!IsActive)
throw new InvalidOperationException();
Time.GamePaused = value;
}
}
/// <summary>
/// True if play mode is starting.
/// </summary>
public bool IsPlayModeStarting;
/// <summary>
/// True if play mode is ending.
/// </summary>
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();
}
/// <inheritdoc />
public override string Status => "Play Mode!";
/// <inheritdoc />
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();
Time.Synchronize(true);
Editor.OnPlayBegin();
IsPlayModeStarting = false;
Profiler.EndEvent();
}
private void SetupEditorEnvOptions()
{
Time.TimeScale = 1.0f;
Physics.DefaultScene.AutoSimulation = true;
Screen.CursorVisible = true;
Screen.CursorLock = CursorLockMode.None;
}
/// <inheritdoc />
public override void UpdateFPS()
{
// Game drives the Time settings
}
/// <inheritdoc />
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();
FlaxEngine.Scripting.FlushRemovedObjects();
Editor.WipeOutLeftoverSceneObjects();
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(true);
}
}
}