// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.Xml; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; using FlaxEditor.Options; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; namespace FlaxEditor.Windows { /// /// Provides in-editor play mode simulation. /// /// public class GameWindow : EditorWindow { private readonly RenderOutputControl _viewport; private readonly GameRoot _guiRoot; private bool _showGUI = true; private bool _showDebugDraw = false; private bool _isMaximized = false, _isUnlockingMouse = false; private bool _cursorVisible = true; private float _gameStartTime; private GUI.Docking.DockState _maximizeRestoreDockState; private GUI.Docking.DockPanel _maximizeRestoreDockTo; private CursorLockMode _cursorLockMode = CursorLockMode.None; // Viewport scaling variables private List _defaultViewportScaling = new List(); private List _customViewportScaling = new List(); private float _viewportAspectRatio = 1; private float _windowAspectRatio = 1; private bool _useAspect = false; private bool _freeAspect = true; /// /// Gets the viewport. /// public RenderOutputControl Viewport => _viewport; /// /// Gets or sets a value indicating whether show game GUI in the view or keep it hidden. /// public bool ShowGUI { get => _showGUI; set { if (value != _showGUI) { _showGUI = value; _guiRoot.Visible = value; } } } /// /// Gets or sets a value indicating whether show Debug Draw shapes in the view or keep it hidden. /// public bool ShowDebugDraw { get => _showDebugDraw; set => _showDebugDraw = value; } /// /// Gets or sets a value indicating whether game window is maximized (only in play mode). /// private bool IsMaximized { get => _isMaximized; set { if (_isMaximized == value) return; _isMaximized = value; var rootWindow = RootWindow; if (value) { // Maximize _maximizeRestoreDockTo = _dockedTo; _maximizeRestoreDockState = _dockedTo.TryGetDockState(out _); if (_maximizeRestoreDockState != GUI.Docking.DockState.Float) { var monitorBounds = Platform.GetMonitorBounds(PointToScreen(Size * 0.5f)); ShowFloating(monitorBounds.Location + new Float2(200, 200), Float2.Zero, WindowStartPosition.Manual); rootWindow = RootWindow; } if (rootWindow != null && !rootWindow.IsMaximized) rootWindow.Maximize(); } else { // Restore if (rootWindow != null) rootWindow.Restore(); if (_maximizeRestoreDockTo != null && _maximizeRestoreDockTo.IsDisposing) _maximizeRestoreDockTo = null; Show(_maximizeRestoreDockState, _maximizeRestoreDockTo); } } } /// /// Gets or sets a value indicating whether center mouse position on window focus in play mode. Helps when working with games that lock mouse cursor. /// public bool CenterMouseOnFocus { get; set; } /// /// Gets or sets a value indicating whether auto-focus game window on play mode start. /// public bool FocusOnPlay { get; set; } private enum ViewportScaleType { Resolution = 0, Aspect = 1, } private class ViewportScaleOptions { /// /// The name. /// public string Label; /// /// The Type of scaling to do. /// public ViewportScaleType ScaleType; /// /// The width and height to scale by. /// public Int2 Size; /// /// If the scaling is active. /// public bool Active; } private class GameRoot : ContainerControl { public bool EnableEvents => !Time.GamePaused; public override bool OnCharInput(char c) { if (!EnableEvents) return false; return base.OnCharInput(c); } public override DragDropEffect OnDragDrop(ref Float2 location, DragData data) { if (!EnableEvents) return DragDropEffect.None; return base.OnDragDrop(ref location, data); } public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) { if (!EnableEvents) return DragDropEffect.None; return base.OnDragEnter(ref location, data); } public override void OnDragLeave() { if (!EnableEvents) return; base.OnDragLeave(); } public override DragDropEffect OnDragMove(ref Float2 location, DragData data) { if (!EnableEvents) return DragDropEffect.None; return base.OnDragMove(ref location, data); } public override bool OnKeyDown(KeyboardKeys key) { if (!EnableEvents) return false; return base.OnKeyDown(key); } public override void OnKeyUp(KeyboardKeys key) { if (!EnableEvents) return; base.OnKeyUp(key); } public override bool OnMouseDoubleClick(Float2 location, MouseButton button) { if (!EnableEvents) return false; return base.OnMouseDoubleClick(location, button); } public override bool OnMouseDown(Float2 location, MouseButton button) { if (!EnableEvents) return false; return base.OnMouseDown(location, button); } public override void OnMouseEnter(Float2 location) { if (!EnableEvents) return; base.OnMouseEnter(location); } public override void OnMouseLeave() { if (!EnableEvents) return; base.OnMouseLeave(); } public override void OnMouseMove(Float2 location) { if (!EnableEvents) return; base.OnMouseMove(location); } public override bool OnMouseUp(Float2 location, MouseButton button) { if (!EnableEvents) return false; return base.OnMouseUp(location, button); } public override bool OnMouseWheel(Float2 location, float delta) { if (!EnableEvents) return false; return base.OnMouseWheel(location, delta); } } /// /// Initializes a new instance of the class. /// /// The editor. public GameWindow(Editor editor) : base(editor, true, ScrollBars.None) { Title = "Game"; AutoFocus = true; FlaxEditor.Utilities.Utils.SetupCommonInputActions(this); var task = MainRenderTask.Instance; // Setup viewport _viewport = new RenderOutputControl(task) { AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, AutoFocus = false, Parent = this }; task.PostRender += OnPostRender; // Override the game GUI root _guiRoot = new GameRoot { AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, //Visible = false, AutoFocus = false, Parent = _viewport }; RootControl.GameRoot = _guiRoot; SizeChanged += control => { ResizeViewport(); }; Editor.StateMachine.PlayingState.SceneDuplicating += PlayingStateOnSceneDuplicating; Editor.StateMachine.PlayingState.SceneRestored += PlayingStateOnSceneRestored; // Link editor options Editor.Options.OptionsChanged += OnOptionsChanged; OnOptionsChanged(Editor.Options.Options); } private void ChangeViewportRatio(ViewportScaleOptions v) { if (v == null) return; if (v.Size.Y <= 0 || v.Size.X <= 0) { return; } if (string.Equals(v.Label, "Free Aspect") && v.Size == new Int2(1, 1)) { _freeAspect = true; _useAspect = true; } else { switch (v.ScaleType) { case ViewportScaleType.Aspect: _useAspect = true; _freeAspect = false; break; case ViewportScaleType.Resolution: _useAspect = false; _freeAspect = false; break; } } _viewportAspectRatio = (float)v.Size.X / v.Size.Y; if (!_freeAspect) { if (!_useAspect) { _viewport.KeepAspectRatio = true; _viewport.CustomResolution = new Int2(v.Size.X, v.Size.Y); } else { _viewport.CustomResolution = new Int2?(); _viewport.KeepAspectRatio = true; } } else { _viewport.CustomResolution = new Int2?(); _viewport.KeepAspectRatio = false; } ResizeViewport(); } private void ResizeViewport() { if (!_freeAspect) { _windowAspectRatio = Width / Height; } else { _windowAspectRatio = 1; } var scaleWidth = _viewportAspectRatio / _windowAspectRatio; var scaleHeight = _windowAspectRatio / _viewportAspectRatio; if (scaleHeight < 1) { _viewport.Bounds = new Rectangle(0, Height * (1 - scaleHeight) / 2, Width, Height * scaleHeight); } else { _viewport.Bounds = new Rectangle(Width * (1 - scaleWidth) / 2, 0, Width * scaleWidth, Height); } PerformLayout(); } private void OnPostRender(GPUContext context, ref RenderContext renderContext) { // Debug Draw shapes if (_showDebugDraw) { var task = _viewport.Task; // Draw actors debug shapes manually if editor viewport is hidden (game viewport task is always rendered before editor viewports) var editWindowViewport = Editor.Windows.EditWin.Viewport; if (editWindowViewport.Task.LastUsedFrame != Engine.FrameCount) { var drawDebugData = editWindowViewport.DebugDrawData; drawDebugData.Clear(); var selectedParents = editWindowViewport.TransformGizmo.SelectedParents; if (selectedParents.Count > 0) { for (int i = 0; i < selectedParents.Count; i++) { if (selectedParents[i].IsActiveInHierarchy) selectedParents[i].OnDebugDraw(drawDebugData); } } unsafe { fixed (IntPtr* actors = drawDebugData.ActorsPtrs) { DebugDraw.DrawActors(new IntPtr(actors), drawDebugData.ActorsCount, true); } } } DebugDraw.Draw(ref renderContext, task.OutputView); } } private void OnOptionsChanged(EditorOptions options) { CenterMouseOnFocus = options.Interface.CenterMouseOnGameWinFocus; FocusOnPlay = options.Interface.FocusGameWinOnPlay; } private void PlayingStateOnSceneDuplicating() { // Remove remaining GUI controls so loaded scene can add own GUI //_guiRoot.DisposeChildren(); // Show GUI //_guiRoot.Visible = _showGUI; } private void PlayingStateOnSceneRestored() { // Remove remaining GUI controls so loaded scene can add own GUI //_guiRoot.DisposeChildren(); // Hide GUI //_guiRoot.Visible = false; } /// protected override bool CanOpenContentFinder => !Editor.IsPlayMode; /// protected override bool CanUseNavigation => false; /// public override void OnPlayBegin() { _gameStartTime = Time.UnscaledGameTime; } /// public override void OnPlayEnd() { IsMaximized = false; Cursor = CursorType.Default; } /// public override void OnShowContextMenu(ContextMenu menu) { base.OnShowContextMenu(menu); // Viewport Brightness { var brightness = menu.AddButton("Viewport Brightness"); brightness.CloseMenuOnClick = false; var brightnessValue = new FloatValueBox(_viewport.Brightness, 140, 2, 50.0f, 0.001f, 10.0f, 0.001f) { Parent = brightness }; brightnessValue.ValueChanged += () => _viewport.Brightness = brightnessValue.Value; } // Viewport Resolution { var resolution = menu.AddButton("Viewport Resolution"); resolution.CloseMenuOnClick = false; var resolutionValue = new FloatValueBox(_viewport.ResolutionScale, 140, 2, 50.0f, 0.1f, 4.0f, 0.001f) { Parent = resolution }; resolutionValue.ValueChanged += () => _viewport.ResolutionScale = resolutionValue.Value; } // Viewport aspect ratio { // Create default scaling options if they dont exist from deserialization. if (_defaultViewportScaling.Count == 0) { _defaultViewportScaling.Add(new ViewportScaleOptions { Label = "Free Aspect", ScaleType = ViewportScaleType.Aspect, Size = new Int2(1, 1), Active = true, }); _defaultViewportScaling.Add(new ViewportScaleOptions { Label = "16:9 Aspect", ScaleType = ViewportScaleType.Aspect, Size = new Int2(16, 9), Active = false, }); _defaultViewportScaling.Add(new ViewportScaleOptions { Label = "16:10 Aspect", ScaleType = ViewportScaleType.Aspect, Size = new Int2(16, 10), Active = false, }); _defaultViewportScaling.Add(new ViewportScaleOptions { Label = "1920x1080 Resolution", ScaleType = ViewportScaleType.Resolution, Size = new Int2(1920, 1080), Active = false, }); _defaultViewportScaling.Add(new ViewportScaleOptions { Label = "2560x1440 Resolution", ScaleType = ViewportScaleType.Resolution, Size = new Int2(2560, 1440), Active = false, }); } var vsMenu = menu.AddChildMenu("Viewport Size").ContextMenu; CreateViewportSizingContextMenu(vsMenu); } // Take Screenshot { var takeScreenshot = menu.AddButton("Take Screenshot"); takeScreenshot.Clicked += TakeScreenshot; } menu.AddSeparator(); // Show GUI { var button = menu.AddButton("Show GUI"); button.CloseMenuOnClick = false; var checkbox = new CheckBox(140, 2, ShowGUI) { Parent = button }; checkbox.StateChanged += x => ShowGUI = x.Checked; } // Show Debug Draw { var button = menu.AddButton("Show Debug Draw"); button.CloseMenuOnClick = false; var checkbox = new CheckBox(140, 2, ShowDebugDraw) { Parent = button }; checkbox.StateChanged += x => ShowDebugDraw = x.Checked; } menu.MinimumWidth = 200; menu.AddSeparator(); } private void CreateViewportSizingContextMenu(ContextMenu vsMenu) { // Add default viewport sizing options for (int i = 0; i < _defaultViewportScaling.Count; i++) { var viewportScale = _defaultViewportScaling[i]; var button = vsMenu.AddButton(viewportScale.Label); button.CloseMenuOnClick = false; button.Icon = viewportScale.Active ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; button.Tag = viewportScale; if (viewportScale.Active) ChangeViewportRatio(viewportScale); button.Clicked += () => { if (button.Tag == null) return; // Reset selected icon on all buttons foreach (var child in vsMenu.Items) { if (child is ContextMenuButton cmb && cmb.Tag is ViewportScaleOptions v) { if (cmb == button) { v.Active = true; button.Icon = Style.Current.CheckBoxTick; ChangeViewportRatio(v); } else if (v.Active) { cmb.Icon = SpriteHandle.Invalid; v.Active = false; } } } }; } if (_defaultViewportScaling.Count != 0) vsMenu.AddSeparator(); // Add custom viewport options for (int i = 0; i < _customViewportScaling.Count; i++) { var viewportScale = _customViewportScaling[i]; var childCM = vsMenu.AddChildMenu(viewportScale.Label); childCM.CloseMenuOnClick = false; childCM.Icon = viewportScale.Active ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; childCM.Tag = viewportScale; if (viewportScale.Active) ChangeViewportRatio(viewportScale); var applyButton = childCM.ContextMenu.AddButton("Apply"); applyButton.Tag = childCM.Tag = viewportScale; applyButton.CloseMenuOnClick = false; applyButton.Clicked += () => { if (childCM.Tag == null) return; // Reset selected icon on all buttons foreach (var child in vsMenu.Items) { if (child is ContextMenuButton cmb && cmb.Tag is ViewportScaleOptions v) { if (child == childCM) { v.Active = true; childCM.Icon = Style.Current.CheckBoxTick; ChangeViewportRatio(v); } else if (v.Active) { cmb.Icon = SpriteHandle.Invalid; v.Active = false; } } } }; var deleteButton = childCM.ContextMenu.AddButton("Delete"); deleteButton.CloseMenuOnClick = false; deleteButton.Clicked += () => { if (childCM.Tag == null) return; var v = (ViewportScaleOptions)childCM.Tag; if (v.Active) { v.Active = false; _defaultViewportScaling[0].Active = true; ChangeViewportRatio(_defaultViewportScaling[0]); } _customViewportScaling.Remove(v); vsMenu.DisposeAllItems(); CreateViewportSizingContextMenu(vsMenu); vsMenu.PerformLayout(); }; } if (_customViewportScaling.Count != 0) vsMenu.AddSeparator(); // Add button var add = vsMenu.AddButton("Add..."); add.CloseMenuOnClick = false; add.Clicked += () => { var popup = new ContextMenuBase { Size = new Float2(230, 125), ClipChildren = false, CullChildren = false, }; popup.Show(add, new Float2(add.Width, 0)); var nameLabel = new Label { Parent = popup, AnchorPreset = AnchorPresets.TopLeft, Text = "Name", HorizontalAlignment = TextAlignment.Near, }; nameLabel.LocalX += 10; nameLabel.LocalY += 10; var nameTextBox = new TextBox { Parent = popup, AnchorPreset = AnchorPresets.TopLeft, IsMultiline = false, }; nameTextBox.LocalX += 100; nameTextBox.LocalY += 10; var typeLabel = new Label { Parent = popup, AnchorPreset = AnchorPresets.TopLeft, Text = "Type", HorizontalAlignment = TextAlignment.Near, }; typeLabel.LocalX += 10; typeLabel.LocalY += 35; var typeDropdown = new Dropdown { Parent = popup, AnchorPreset = AnchorPresets.TopLeft, Items = { "Aspect", "Resolution" }, SelectedItem = "Aspect", Width = nameTextBox.Width }; typeDropdown.LocalY += 35; typeDropdown.LocalX += 100; var whLabel = new Label { Parent = popup, AnchorPreset = AnchorPresets.TopLeft, Text = "Width & Height", HorizontalAlignment = TextAlignment.Near, }; whLabel.LocalX += 10; whLabel.LocalY += 60; var wValue = new IntValueBox(16) { Parent = popup, AnchorPreset = AnchorPresets.TopLeft, MinValue = 1, Width = 55, }; wValue.LocalY += 60; wValue.LocalX += 100; var hValue = new IntValueBox(9) { Parent = popup, AnchorPreset = AnchorPresets.TopLeft, MinValue = 1, Width = 55, }; hValue.LocalY += 60; hValue.LocalX += 165; var submitButton = new Button { Parent = popup, AnchorPreset = AnchorPresets.TopLeft, Text = "Submit", Width = 70, }; submitButton.LocalX += 40; submitButton.LocalY += 90; submitButton.Clicked += () => { Enum.TryParse(typeDropdown.SelectedItem, out ViewportScaleType type); var combineString = type == ViewportScaleType.Aspect ? ":" : "x"; var name = nameTextBox.Text + " (" + wValue.Value + combineString + hValue.Value + ") " + typeDropdown.SelectedItem; var newViewportOption = new ViewportScaleOptions { ScaleType = type, Label = name, Size = new Int2(wValue.Value, hValue.Value), }; _customViewportScaling.Add(newViewportOption); vsMenu.DisposeAllItems(); CreateViewportSizingContextMenu(vsMenu); vsMenu.PerformLayout(); }; var cancelButton = new Button { Parent = popup, AnchorPreset = AnchorPresets.TopLeft, Text = "Cancel", Width = 70, }; cancelButton.LocalX += 120; cancelButton.LocalY += 90; cancelButton.Clicked += () => { nameTextBox.Clear(); typeDropdown.SelectedItem = "Aspect"; hValue.Value = 9; wValue.Value = 16; popup.Hide(); }; }; } /// public override void Draw() { base.Draw(); if (Camera.MainCamera == null) { var style = Style.Current; Render2D.DrawText(style.FontLarge, "No camera", new Rectangle(Float2.Zero, Size), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center); } // Selected UI controls outline bool drawAnySelectedControl = false; // TODO: optimize this (eg. cache list of selected UIControl's when selection gets changed) var selection = Editor.SceneEditing.Selection; for (var i = 0; i < selection.Count; i++) { if (selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null) { if (!drawAnySelectedControl) { drawAnySelectedControl = true; Render2D.PushTransform(ref _viewport._cachedTransform); } var options = Editor.Options.Options.Visual; var control = controlActor.Control; var bounds = control.EditorBounds; bounds = Rectangle.FromPoints(control.PointToParent(_viewport, bounds.Location), control.PointToParent(_viewport, bounds.Size)); Render2D.DrawRectangle(bounds, options.SelectionOutlineColor0, options.UISelectionOutlineSize); } } if (drawAnySelectedControl) Render2D.PopTransform(); // Play mode hints and overlay if (Editor.StateMachine.IsPlayMode) { var style = Style.Current; float time = Time.UnscaledGameTime - _gameStartTime; float timeout = 3.0f; float fadeOutTime = 1.0f; float animTime = time - timeout; if (animTime < 0) { var alpha = Mathf.Saturate(-animTime / fadeOutTime); var rect = new Rectangle(new Float2(6), Size - 12); var text = "Press Shift+F11 to unlock the mouse"; Render2D.DrawText(style.FontSmall, text, rect + new Float2(1.0f), style.Background * alpha, TextAlignment.Near, TextAlignment.Far); Render2D.DrawText(style.FontSmall, text, rect, style.Foreground * alpha, TextAlignment.Near, TextAlignment.Far); } timeout = 1.0f; fadeOutTime = 0.6f; animTime = time - timeout; if (animTime < 0) { float alpha = Mathf.Saturate(-animTime / fadeOutTime); Render2D.DrawRectangle(new Rectangle(new Float2(4), Size - 8), Color.Orange * alpha); } // Add overlay during debugger breakpoint hang if (Editor.Simulation.IsDuringBreakpointHang) { var bounds = new Rectangle(Float2.Zero, Size); Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f)); Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center); } } } /// /// Unlocks the mouse if game window is focused during play mode. /// public void UnlockMouseInPlay() { if (Editor.StateMachine.IsPlayMode) { // Cache cursor _cursorVisible = Screen.CursorVisible; _cursorLockMode = Screen.CursorLock; Screen.CursorVisible = true; if (Screen.CursorLock == CursorLockMode.Clipped) Screen.CursorLock = CursorLockMode.None; // Defocus _isUnlockingMouse = true; Focus(null); _isUnlockingMouse = false; Editor.Windows.MainWindow.Focus(); if (Editor.Windows.PropertiesWin.IsDocked) Editor.Windows.PropertiesWin.Focus(); } } /// /// Focuses the game viewport. Shows the window if hidden or unselected. /// public void FocusGameViewport() { if (!IsDocked) { ShowFloating(); } else if (!IsSelected) { SelectTab(false); RootWindow?.Window?.Focus(); } Focus(); } /// /// Takes the screenshot of the current viewport. /// public void TakeScreenshot() { Screenshot.Capture(_viewport.Task); } /// /// Takes the screenshot of the current viewport. /// /// The output file path. public void TakeScreenshot(string path) { Screenshot.Capture(_viewport.Task, path); } /// public override bool OnKeyDown(KeyboardKeys key) { switch (key) { case KeyboardKeys.F12: Screenshot.Capture(string.Empty); return true; case KeyboardKeys.F11: if (Root.GetKey(KeyboardKeys.Shift)) { // Unlock mouse in game mode UnlockMouseInPlay(); return true; } else if (Editor.IsPlayMode) { // Maximized game window toggle IsMaximized = !IsMaximized; return true; } break; } // Prevent closing the game window tab during a play session if (Editor.StateMachine.IsPlayMode && Editor.Options.Options.Input.CloseTab.Process(this, key)) { return true; } return base.OnKeyDown(key); } /// public override void OnStartContainsFocus() { base.OnStartContainsFocus(); if (Editor.StateMachine.IsPlayMode && !Editor.StateMachine.PlayingState.IsPaused) { // Center mouse in play mode if (CenterMouseOnFocus) { var center = PointToWindow(Size * 0.5f); Root.MousePosition = center; } // Restore cursor if (_cursorLockMode != CursorLockMode.None) Screen.CursorLock = _cursorLockMode; if (!_cursorVisible) Screen.CursorVisible = false; } } /// public override void OnEndContainsFocus() { base.OnEndContainsFocus(); if (!_isUnlockingMouse) { // Cache cursor _cursorVisible = Screen.CursorVisible; _cursorLockMode = Screen.CursorLock; // Restore cursor visibility (could be hidden by the game) if (!_cursorVisible) Screen.CursorVisible = true; } } /// public override Control OnNavigate(NavDirection direction, Float2 location, Control caller, List visited) { // Block leaking UI navigation focus outside the game window if (IsFocused && caller != this) { // Pick the first UI control if game UI is not focused yet foreach (var child in _guiRoot.Children) { if (child.Visible) return child.OnNavigate(direction, Float2.Zero, this, visited); } } return null; } /// public override bool OnMouseDown(Float2 location, MouseButton button) { var result = base.OnMouseDown(location, button); // Catch user focus if (!ContainsFocus) Focus(); return result; } /// public override bool UseLayoutData => true; /// public override void OnLayoutSerialize(XmlWriter writer) { writer.WriteAttributeString("ShowGUI", ShowGUI.ToString()); writer.WriteAttributeString("ShowDebugDraw", ShowDebugDraw.ToString()); writer.WriteAttributeString("DefaultViewportScaling", JsonSerializer.Serialize(_defaultViewportScaling)); writer.WriteAttributeString("CustomViewportScaling", JsonSerializer.Serialize(_customViewportScaling)); } /// public override void OnLayoutDeserialize(XmlElement node) { if (bool.TryParse(node.GetAttribute("ShowGUI"), out bool value1)) ShowGUI = value1; if (bool.TryParse(node.GetAttribute("ShowDebugDraw"), out value1)) ShowDebugDraw = value1; if (node.HasAttribute("CustomViewportScaling")) _customViewportScaling = JsonSerializer.Deserialize>(node.GetAttribute("CustomViewportScaling")); for (int i = 0; i < _customViewportScaling.Count; i++) { if (_customViewportScaling[i].Active) ChangeViewportRatio(_customViewportScaling[i]); } if (node.HasAttribute("DefaultViewportScaling")) _defaultViewportScaling = JsonSerializer.Deserialize>(node.GetAttribute("DefaultViewportScaling")); for (int i = 0; i < _defaultViewportScaling.Count; i++) { if (_defaultViewportScaling[i].Active) ChangeViewportRatio(_defaultViewportScaling[i]); } } /// public override void OnLayoutDeserialize() { ShowGUI = true; ShowDebugDraw = false; } } }