using HarmonyLib; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using UniverseLib; using UniverseLib.Input; using UniverseLib.UI; using UniverseLib.UI.Models; using UniverseLib.Utility; namespace UnityExplorer.UI.Panels { internal class FreeCamPanel : UEPanel { public FreeCamPanel(UIBase owner) : base(owner) { } public override string Name => "Freecam"; public override UIManager.Panels PanelType => UIManager.Panels.Freecam; public override int MinWidth => 400; public override int MinHeight => 320; public override Vector2 DefaultAnchorMin => new(0.4f, 0.4f); public override Vector2 DefaultAnchorMax => new(0.6f, 0.6f); public override bool NavButtonWanted => true; public override bool ShouldSaveActiveState => true; internal static bool inFreeCamMode; internal static bool usingGameCamera; internal static Camera ourCamera; internal static Camera lastMainCamera; internal static FreeCamBehaviour freeCamScript; internal static float desiredMoveSpeed = 10f; internal static Vector3 originalCameraPosition; internal static Quaternion originalCameraRotation; internal static Vector3? currentUserCameraPosition; internal static Quaternion? currentUserCameraRotation; internal static Vector3 previousMousePosition; internal static Vector3 lastSetCameraPosition; static ButtonRef startStopButton; static Toggle useGameCameraToggle; static InputFieldRef positionInput; static InputFieldRef moveSpeedInput; static ButtonRef inspectButton; internal static void BeginFreecam() { inFreeCamMode = true; previousMousePosition = InputManager.MousePosition; CacheMainCamera(); SetupFreeCamera(); inspectButton.GameObject.SetActive(true); } static void CacheMainCamera() { Camera currentMain = Camera.main; if (currentMain) { lastMainCamera = currentMain; originalCameraPosition = currentMain.transform.position; originalCameraRotation = currentMain.transform.rotation; if (currentUserCameraPosition == null) { currentUserCameraPosition = currentMain.transform.position; currentUserCameraRotation = currentMain.transform.rotation; } } else originalCameraRotation = Quaternion.identity; } static void SetupFreeCamera() { if (useGameCameraToggle.isOn) { if (!lastMainCamera) { ExplorerCore.LogWarning($"There is no previous Camera found, reverting to default Free Cam."); useGameCameraToggle.isOn = false; } else { usingGameCamera = true; ourCamera = lastMainCamera; } } if (!useGameCameraToggle.isOn) { usingGameCamera = false; if (lastMainCamera) lastMainCamera.enabled = false; } if (!ourCamera) { ourCamera = new GameObject("UE_Freecam").AddComponent(); ourCamera.gameObject.tag = "MainCamera"; GameObject.DontDestroyOnLoad(ourCamera.gameObject); ourCamera.gameObject.hideFlags = HideFlags.HideAndDontSave; } if (!freeCamScript) freeCamScript = ourCamera.gameObject.AddComponent(); ourCamera.transform.position = (Vector3)currentUserCameraPosition; ourCamera.transform.rotation = (Quaternion)currentUserCameraRotation; ourCamera.gameObject.SetActive(true); ourCamera.enabled = true; } internal static void EndFreecam() { inFreeCamMode = false; if (usingGameCamera) { ourCamera = null; if (lastMainCamera) { lastMainCamera.transform.position = originalCameraPosition; lastMainCamera.transform.rotation = originalCameraRotation; } } if (ourCamera) ourCamera.gameObject.SetActive(false); else inspectButton.GameObject.SetActive(false); if (freeCamScript) { GameObject.Destroy(freeCamScript); freeCamScript = null; } if (lastMainCamera) lastMainCamera.enabled = true; } static void SetCameraPosition(Vector3 pos) { if (!ourCamera || lastSetCameraPosition == pos) return; ourCamera.transform.position = pos; lastSetCameraPosition = pos; } internal static void UpdatePositionInput() { if (!ourCamera) return; lastSetCameraPosition = ourCamera.transform.position; positionInput.Text = ParseUtility.ToStringForInput(lastSetCameraPosition); } // ~~~~~~~~ UI construction / callbacks ~~~~~~~~ protected override void ConstructPanelContent() { startStopButton = UIFactory.CreateButton(ContentRoot, "ToggleButton", "Freecam"); UIFactory.SetLayoutElement(startStopButton.GameObject, minWidth: 150, minHeight: 25, flexibleWidth: 9999); startStopButton.OnClick += StartStopButton_OnClick; SetToggleButtonState(); AddSpacer(5); GameObject toggleObj = UIFactory.CreateToggle(ContentRoot, "UseGameCameraToggle", out useGameCameraToggle, out Text toggleText); UIFactory.SetLayoutElement(toggleObj, minHeight: 25, flexibleWidth: 9999); useGameCameraToggle.onValueChanged.AddListener(OnUseGameCameraToggled); useGameCameraToggle.isOn = false; toggleText.text = "Use Game Camera?"; AddSpacer(5); GameObject posRow = AddInputField("Position", "Freecam Pos:", "eg. 0 0 0", out positionInput, PositionInput_OnEndEdit); ButtonRef resetPosButton = UIFactory.CreateButton(posRow, "ResetButton", "Reset"); UIFactory.SetLayoutElement(resetPosButton.GameObject, minWidth: 70, minHeight: 25); resetPosButton.OnClick += OnResetPosButtonClicked; AddSpacer(5); AddInputField("MoveSpeed", "Move Speed:", "Default: 1", out moveSpeedInput, MoveSpeedInput_OnEndEdit); moveSpeedInput.Text = desiredMoveSpeed.ToString(); AddSpacer(5); string instructions = @"Controls: - WASD / Arrows: Movement - Space / PgUp: Move up - LeftCtrl / PgDown: Move down - Right Mouse Button: Free look - Shift: Super speed"; Text instructionsText = UIFactory.CreateLabel(ContentRoot, "Instructions", instructions, TextAnchor.UpperLeft); UIFactory.SetLayoutElement(instructionsText.gameObject, flexibleWidth: 9999, flexibleHeight: 9999); AddSpacer(5); inspectButton = UIFactory.CreateButton(ContentRoot, "InspectButton", "Inspect Free Camera"); UIFactory.SetLayoutElement(inspectButton.GameObject, flexibleWidth: 9999, minHeight: 25); inspectButton.OnClick += () => { InspectorManager.Inspect(ourCamera); }; inspectButton.GameObject.SetActive(false); AddSpacer(5); } void AddSpacer(int height) { GameObject obj = UIFactory.CreateUIObject("Spacer", ContentRoot); UIFactory.SetLayoutElement(obj, minHeight: height, flexibleHeight: 0); } GameObject AddInputField(string name, string labelText, string placeHolder, out InputFieldRef inputField, Action onInputEndEdit) { GameObject row = UIFactory.CreateHorizontalGroup(ContentRoot, $"{name}_Group", false, false, true, true, 3, default, new(1, 1, 1, 0)); Text posLabel = UIFactory.CreateLabel(row, $"{name}_Label", labelText); UIFactory.SetLayoutElement(posLabel.gameObject, minWidth: 100, minHeight: 25); inputField = UIFactory.CreateInputField(row, $"{name}_Input", placeHolder); UIFactory.SetLayoutElement(inputField.GameObject, minWidth: 125, minHeight: 25, flexibleWidth: 9999); inputField.Component.GetOnEndEdit().AddListener(onInputEndEdit); return row; } void StartStopButton_OnClick() { EventSystemHelper.SetSelectedGameObject(null); if (inFreeCamMode) EndFreecam(); else BeginFreecam(); SetToggleButtonState(); } void SetToggleButtonState() { if (inFreeCamMode) { RuntimeHelper.SetColorBlockAuto(startStopButton.Component, new(0.4f, 0.2f, 0.2f)); startStopButton.ButtonText.text = "End Freecam"; } else { RuntimeHelper.SetColorBlockAuto(startStopButton.Component, new(0.2f, 0.4f, 0.2f)); startStopButton.ButtonText.text = "Begin Freecam"; } } void OnUseGameCameraToggled(bool value) { EventSystemHelper.SetSelectedGameObject(null); if (!inFreeCamMode) return; EndFreecam(); BeginFreecam(); } void OnResetPosButtonClicked() { currentUserCameraPosition = originalCameraPosition; currentUserCameraRotation = originalCameraRotation; if (inFreeCamMode && ourCamera) { ourCamera.transform.position = (Vector3)currentUserCameraPosition; ourCamera.transform.rotation = (Quaternion)currentUserCameraRotation; } positionInput.Text = ParseUtility.ToStringForInput(originalCameraPosition); } void PositionInput_OnEndEdit(string input) { EventSystemHelper.SetSelectedGameObject(null); if (!ParseUtility.TryParse(input, out Vector3 parsed, out Exception parseEx)) { ExplorerCore.LogWarning($"Could not parse position to Vector3: {parseEx.ReflectionExToString()}"); UpdatePositionInput(); return; } SetCameraPosition(parsed); } void MoveSpeedInput_OnEndEdit(string input) { EventSystemHelper.SetSelectedGameObject(null); if (!ParseUtility.TryParse(input, out float parsed, out Exception parseEx)) { ExplorerCore.LogWarning($"Could not parse value: {parseEx.ReflectionExToString()}"); moveSpeedInput.Text = desiredMoveSpeed.ToString(); return; } desiredMoveSpeed = parsed; } } internal class FreeCamBehaviour : MonoBehaviour { #if CPP static FreeCamBehaviour() { UnhollowerRuntimeLib.ClassInjector.RegisterTypeInIl2Cpp(); } public FreeCamBehaviour(IntPtr ptr) : base(ptr) { } #endif internal void Update() { if (FreeCamPanel.inFreeCamMode) { if (!FreeCamPanel.ourCamera) { FreeCamPanel.EndFreecam(); return; } Transform transform = FreeCamPanel.ourCamera.transform; FreeCamPanel.currentUserCameraPosition = transform.position; FreeCamPanel.currentUserCameraRotation = transform.rotation; float moveSpeed = FreeCamPanel.desiredMoveSpeed * Time.deltaTime; if (InputManager.GetKey(KeyCode.LeftShift) || InputManager.GetKey(KeyCode.RightShift)) moveSpeed *= 10f; if (InputManager.GetKey(KeyCode.LeftArrow) || InputManager.GetKey(KeyCode.A)) transform.position += transform.right * -1 * moveSpeed; if (InputManager.GetKey(KeyCode.RightArrow) || InputManager.GetKey(KeyCode.D)) transform.position += transform.right * moveSpeed; if (InputManager.GetKey(KeyCode.UpArrow) || InputManager.GetKey(KeyCode.W)) transform.position += transform.forward * moveSpeed; if (InputManager.GetKey(KeyCode.DownArrow) || InputManager.GetKey(KeyCode.S)) transform.position += transform.forward * -1 * moveSpeed; if (InputManager.GetKey(KeyCode.Space) || InputManager.GetKey(KeyCode.PageUp)) transform.position += transform.up * moveSpeed; if (InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.PageDown)) transform.position += transform.up * -1 * moveSpeed; if (InputManager.GetMouseButton(1)) { Vector3 mouseDelta = InputManager.MousePosition - FreeCamPanel.previousMousePosition; float newRotationX = transform.localEulerAngles.y + mouseDelta.x * 0.3f; float newRotationY = transform.localEulerAngles.x - mouseDelta.y * 0.3f; transform.localEulerAngles = new Vector3(newRotationY, newRotationX, 0f); } FreeCamPanel.UpdatePositionInput(); FreeCamPanel.previousMousePosition = InputManager.MousePosition; } } } }