From 3bb1a9316b9fa3781aa2df9030976f749e077351 Mon Sep 17 00:00:00 2001 From: Drew C Date: Sun, 11 Jan 2026 21:39:58 -0600 Subject: [PATCH] Initial project --- Engine/Controller/PlayerCamera.cs | 168 ++++++++++++++++++++++++++ Engine/Controller/PlayerCamera.cs.uid | 1 + Engine/Controller/PlayerCamera.tscn | 6 + Kimera.csproj | 7 ++ Kimera.sln | 19 +++ icon.svg | 1 + icon.svg.import | 43 +++++++ project.godot | 51 ++++++++ 8 files changed, 296 insertions(+) create mode 100644 Engine/Controller/PlayerCamera.cs create mode 100644 Engine/Controller/PlayerCamera.cs.uid create mode 100644 Engine/Controller/PlayerCamera.tscn create mode 100644 Kimera.csproj create mode 100644 Kimera.sln create mode 100644 icon.svg create mode 100644 icon.svg.import create mode 100644 project.godot diff --git a/Engine/Controller/PlayerCamera.cs b/Engine/Controller/PlayerCamera.cs new file mode 100644 index 0000000..39fe163 --- /dev/null +++ b/Engine/Controller/PlayerCamera.cs @@ -0,0 +1,168 @@ +using Godot; + +public partial class PlayerCamera : Camera3D +{ + [ExportCategory("Camera Movement")] + [Export] public float CameraPanSpeed { get; set; } = 20f; + [Export] public float CameraZoomSpeed { get; set; } = 20f; + [Export] public float CameraZoomMin { get; set; } = 10f; + [Export] public float CameraZoomMax { get; set; } = 50f; + + [ExportCategory("Edge Scrolling")] + [Export] public float EdgeScrollMargin { get; set; } = 20f; + [Export] public float EdgeScrollSpeed { get; set; } = 15f; + + [ExportCategory("Rotation")] + [Export] public float YawSensitivity { get; set; } = 0.5f; + [Export] public float PitchSensitivity { get; set; } = 0.18f; + [Export] public float StepDegreeMax { get; set; } = 3f; + [Export] public float PitchDegreeMin { get; set; } = 10f; + [Export] public float PitchDegreeMax { get; set; } = 80f; + [Export] public bool CaptureMiddleButton { get; set; } = false; + + Vector3 origin = Vector3.Zero; + float orbitDistance = 25f; + float currentHeight = 20f; + float orbitRadius = 20f; + bool isMiddleMouseRotating = false; + float yaw = 0f; + float pitch = 0f; + + public override void _Ready() + { + var pitchMin = Mathf.DegToRad(PitchDegreeMin); + var pitchMax = Mathf.DegToRad(PitchDegreeMax); + + pitch = Mathf.Clamp(pitch, pitchMin, pitchMax); + + UpdateCameraPosition(); + } + + public override void _Process(double delta) + { + var movement = Vector3.Zero; + + // Keyboard Input + if (Input.IsActionPressed("camera_pan_right")) + { + movement.X += 1f; + } + if (Input.IsActionPressed("camera_pan_left")) + { + movement.X -= 1f; + } + if (Input.IsActionPressed("camera_pan_up")) + { + movement.Z += 1f; + } + if (Input.IsActionPressed("camera_pan_down")) + { + movement.Z -= 1f; + } + + // Edge Scrolling + var mousePosition = GetViewport().GetMousePosition(); + var viewportSize = GetViewport().GetVisibleRect().Size; + + if (mousePosition.X < EdgeScrollMargin) + { + movement.X -= 1f; + } + if (mousePosition.X > viewportSize.X - EdgeScrollMargin) + { + movement.X += 1f; + } + if (mousePosition.Y < EdgeScrollMargin) + { + movement.Z -= 1f; + } + if (mousePosition.Y > viewportSize.Y - EdgeScrollMargin) + { + movement.Z += 1f; + } + + // Rotating + if (movement.Length() > 0f) + { + movement = movement.Normalized().Rotated(Vector3.Up, yaw); + origin += movement * CameraPanSpeed * (float)delta; + + UpdateCameraPosition(); + } + } + + public override void _UnhandledInput(InputEvent @event) + { + if (@event is InputEventMouseButton mouseButtonEvent) + { + if (mouseButtonEvent.Pressed) + { + var delta = (float)GetProcessDeltaTime(); + + if (mouseButtonEvent.ButtonIndex is MouseButton.WheelUp) + { + orbitDistance = Mathf.Max(CameraZoomMin, orbitDistance - CameraZoomSpeed * delta); + } + else if (mouseButtonEvent.ButtonIndex is MouseButton.WheelDown) + { + orbitDistance = Mathf.Min(CameraZoomMax, orbitDistance + CameraZoomSpeed * delta); + } + + UpdateCameraPosition(); + } + + if (mouseButtonEvent.ButtonIndex is MouseButton.Middle) + { + isMiddleMouseRotating = mouseButtonEvent.Pressed; + + if (CaptureMiddleButton) + { + Input.MouseMode = mouseButtonEvent.Pressed + ? Input.MouseModeEnum.Captured + : Input.MouseModeEnum.Visible; + } + } + } + else if (@event is InputEventMouseMotion mouseMotionEvent && isMiddleMouseRotating) + { + var viewportSize = GetViewport().GetVisibleRect().Size; + var viewMin = (float)Mathf.Min(viewportSize.X, viewportSize.Y); + var delta = (float)GetProcessDeltaTime() * 60f; + + // px => normalized fraction of screen -> radians, scaled by dt + var dx = (mouseMotionEvent.Relative.X / viewMin) * YawSensitivity * Mathf.Tau * delta; + var dy = (mouseMotionEvent.Relative.Y / viewMin) * PitchSensitivity * Mathf.Tau * delta; + + // Safety cap per event + var maxStep = Mathf.DegToRad(StepDegreeMax); + + dx = Mathf.Clamp(dx, -maxStep, maxStep); + dy = Mathf.Clamp(dy, -maxStep, maxStep); + + yaw -= dx; + pitch += dy; + + var pitchMin = Mathf.DegToRad(PitchDegreeMin); + var pitchMax = Mathf.DegToRad(PitchDegreeMax); + + pitch = Mathf.Clamp(pitch, pitchMax, pitchMax); + + UpdateCameraPosition(); + } + } + + void UpdateCameraPosition() + { + var direction = new Vector3( + x: Mathf.Sin(yaw) * Mathf.Cos(pitch), + y: Mathf.Sin(pitch), + z: Mathf.Cos(yaw) * Mathf.Cos(pitch) + ).Normalized(); + + Position = origin + direction * orbitDistance; + LookAt(origin, Vector3.Up); + + currentHeight = orbitDistance * Mathf.Sin(pitch); + orbitRadius = orbitDistance * Mathf.Cos(pitch); + } +} diff --git a/Engine/Controller/PlayerCamera.cs.uid b/Engine/Controller/PlayerCamera.cs.uid new file mode 100644 index 0000000..cb7169b --- /dev/null +++ b/Engine/Controller/PlayerCamera.cs.uid @@ -0,0 +1 @@ +uid://7pn3nacee2ay diff --git a/Engine/Controller/PlayerCamera.tscn b/Engine/Controller/PlayerCamera.tscn new file mode 100644 index 0000000..7f1783d --- /dev/null +++ b/Engine/Controller/PlayerCamera.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://badifqjgdh2os"] + +[ext_resource type="Script" uid="uid://7pn3nacee2ay" path="res://Engine/Controller/PlayerCamera.cs" id="1_ckl5i"] + +[node name="PlayerCamera" type="Camera3D"] +script = ExtResource("1_ckl5i") diff --git a/Kimera.csproj b/Kimera.csproj new file mode 100644 index 0000000..65eb1f8 --- /dev/null +++ b/Kimera.csproj @@ -0,0 +1,7 @@ + + + net8.0 + net9.0 + true + + \ No newline at end of file diff --git a/Kimera.sln b/Kimera.sln new file mode 100644 index 0000000..1c34de9 --- /dev/null +++ b/Kimera.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kimera", "Kimera.csproj", "{F2079354-F59C-4B77-953B-C291A1F03AD2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + ExportDebug|Any CPU = ExportDebug|Any CPU + ExportRelease|Any CPU = ExportRelease|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F2079354-F59C-4B77-953B-C291A1F03AD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2079354-F59C-4B77-953B-C291A1F03AD2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2079354-F59C-4B77-953B-C291A1F03AD2}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU + {F2079354-F59C-4B77-953B-C291A1F03AD2}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU + {F2079354-F59C-4B77-953B-C291A1F03AD2}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU + {F2079354-F59C-4B77-953B-C291A1F03AD2}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU + EndGlobalSection +EndGlobal diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..c6bbb7d --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..5a9526d --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cxnvsxc7sl1gs" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..996f647 --- /dev/null +++ b/project.godot @@ -0,0 +1,51 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="Kimera" +config/features=PackedStringArray("4.5", "C#", "Forward Plus") +config/icon="res://icon.svg" + +[dotnet] + +project/assembly_name="Kimera" + +[editor] + +version_control/plugin_name="GitPlugin" +version_control/autoload_on_startup=true + +[input] + +camera_pan_left={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +camera_pan_right={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +camera_pan_up={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +camera_pan_down={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +}