From ff62d67f548b7222ea9086e8c2362be1f10158f4 Mon Sep 17 00:00:00 2001 From: Dan Baker Date: Sun, 4 May 2025 17:53:46 +0100 Subject: [PATCH] Adds in-game debug menu addon Adds an in-game debug menu that displays performance metrics (FPS, frame times) and hardware/software information. The menu can be toggled using the F3 key (or a custom input binding). It has different display styles, ranging from a compact FPS display to a detailed view with graphs and system information. --- addons/debug_menu/LICENSE.md | 21 + addons/debug_menu/debug_menu.gd | 479 ++++++++++++++++++ addons/debug_menu/debug_menu.gd.uid | 1 + addons/debug_menu/debug_menu.tscn | 401 +++++++++++++++ addons/debug_menu/plugin.cfg | 7 + addons/debug_menu/plugin.gd | 29 ++ addons/debug_menu/plugin.gd.uid | 1 + assets/projectiles/basic_projectile.tscn | 20 + assets/sprites/enemies/slimes_blue.png | Bin 0 -> 7641 bytes assets/sprites/enemies/slimes_blue.png.import | 34 ++ assets/sprites/enemies/slimes_dark.png | Bin 0 -> 7763 bytes assets/sprites/enemies/slimes_dark.png.import | 34 ++ assets/sprites/enemies/slimes_green.png | Bin 0 -> 7537 bytes .../sprites/enemies/slimes_green.png.import | 34 ++ assets/sprites/enemies/slimes_pink.png | Bin 0 -> 7704 bytes assets/sprites/enemies/slimes_pink.png.import | 34 ++ assets/sprites/enemies/slimes_white.png | Bin 0 -> 7773 bytes .../sprites/enemies/slimes_white.png.import | 34 ++ assets/sprites/enemies/slimes_yellow.png | Bin 0 -> 7774 bytes .../sprites/enemies/slimes_yellow.png.import | 34 ++ assets/sprites/tilesets/forest.tres | 3 +- assets/weapons/ranged_weapon.tscn | 6 + enemies/scripts/enemy.gd | 1 + enemies/scripts/enemy.gd.uid | 1 + enemies/test_enemy.tscn | 70 +++ map/Map.tscn | 7 +- player/modifiers/fire_rate_additive.gd | 2 +- player/modifiers/fire_rate_multiplicative.gd | 2 +- player/player.tscn | 12 +- player/scripts/player.gd | 34 +- player/scripts/player_combat.gd | 30 ++ player/scripts/player_combat.gd.uid | 1 + player/scripts/player_movement.gd | 5 +- player/weapons/projectile.gd | 110 ++++ player/weapons/projectile.gd.uid | 1 + player/weapons/ranged_weapon.gd | 76 ++- project.godot | 9 +- 37 files changed, 1484 insertions(+), 49 deletions(-) create mode 100644 addons/debug_menu/LICENSE.md create mode 100644 addons/debug_menu/debug_menu.gd create mode 100644 addons/debug_menu/debug_menu.gd.uid create mode 100644 addons/debug_menu/debug_menu.tscn create mode 100644 addons/debug_menu/plugin.cfg create mode 100644 addons/debug_menu/plugin.gd create mode 100644 addons/debug_menu/plugin.gd.uid create mode 100644 assets/projectiles/basic_projectile.tscn create mode 100644 assets/sprites/enemies/slimes_blue.png create mode 100644 assets/sprites/enemies/slimes_blue.png.import create mode 100644 assets/sprites/enemies/slimes_dark.png create mode 100644 assets/sprites/enemies/slimes_dark.png.import create mode 100644 assets/sprites/enemies/slimes_green.png create mode 100644 assets/sprites/enemies/slimes_green.png.import create mode 100644 assets/sprites/enemies/slimes_pink.png create mode 100644 assets/sprites/enemies/slimes_pink.png.import create mode 100644 assets/sprites/enemies/slimes_white.png create mode 100644 assets/sprites/enemies/slimes_white.png.import create mode 100644 assets/sprites/enemies/slimes_yellow.png create mode 100644 assets/sprites/enemies/slimes_yellow.png.import create mode 100644 assets/weapons/ranged_weapon.tscn create mode 100644 enemies/scripts/enemy.gd create mode 100644 enemies/scripts/enemy.gd.uid create mode 100644 enemies/test_enemy.tscn create mode 100644 player/scripts/player_combat.gd create mode 100644 player/scripts/player_combat.gd.uid create mode 100644 player/weapons/projectile.gd create mode 100644 player/weapons/projectile.gd.uid diff --git a/addons/debug_menu/LICENSE.md b/addons/debug_menu/LICENSE.md new file mode 100644 index 0000000..54fc020 --- /dev/null +++ b/addons/debug_menu/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright © 2023-present Hugo Locurcio and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/addons/debug_menu/debug_menu.gd b/addons/debug_menu/debug_menu.gd new file mode 100644 index 0000000..a1ab064 --- /dev/null +++ b/addons/debug_menu/debug_menu.gd @@ -0,0 +1,479 @@ +extends CanvasLayer + +@export var fps: Label +@export var frame_time: Label +@export var frame_number: Label +@export var frame_history_total_avg: Label +@export var frame_history_total_min: Label +@export var frame_history_total_max: Label +@export var frame_history_total_last: Label +@export var frame_history_cpu_avg: Label +@export var frame_history_cpu_min: Label +@export var frame_history_cpu_max: Label +@export var frame_history_cpu_last: Label +@export var frame_history_gpu_avg: Label +@export var frame_history_gpu_min: Label +@export var frame_history_gpu_max: Label +@export var frame_history_gpu_last: Label +@export var fps_graph: Panel +@export var total_graph: Panel +@export var cpu_graph: Panel +@export var gpu_graph: Panel +@export var information: Label +@export var settings: Label + +## The number of frames to keep in history for graph drawing and best/worst calculations. +## Currently, this also affects how FPS is measured. +const HISTORY_NUM_FRAMES = 150 + +const GRAPH_SIZE = Vector2(150, 25) +const GRAPH_MIN_FPS = 10 +const GRAPH_MAX_FPS = 160 +const GRAPH_MIN_FRAMETIME = 1.0 / GRAPH_MIN_FPS +const GRAPH_MAX_FRAMETIME = 1.0 / GRAPH_MAX_FPS + +## Debug menu display style. +enum Style { + HIDDEN, ## Debug menu is hidden. + VISIBLE_COMPACT, ## Debug menu is visible, with only the FPS, FPS cap (if any) and time taken to render the last frame. + VISIBLE_DETAILED, ## Debug menu is visible with full information, including graphs. + MAX, ## Represents the size of the Style enum. +} + +## The style to use when drawing the debug menu. +var style := Style.HIDDEN: + set(value): + style = value + match style: + Style.HIDDEN: + visible = false + Style.VISIBLE_COMPACT, Style.VISIBLE_DETAILED: + visible = true + frame_number.visible = style == Style.VISIBLE_DETAILED + $DebugMenu/VBoxContainer/FrameTimeHistory.visible = style == Style.VISIBLE_DETAILED + $DebugMenu/VBoxContainer/FPSGraph.visible = style == Style.VISIBLE_DETAILED + $DebugMenu/VBoxContainer/TotalGraph.visible = style == Style.VISIBLE_DETAILED + $DebugMenu/VBoxContainer/CPUGraph.visible = style == Style.VISIBLE_DETAILED + $DebugMenu/VBoxContainer/GPUGraph.visible = style == Style.VISIBLE_DETAILED + information.visible = style == Style.VISIBLE_DETAILED + settings.visible = style == Style.VISIBLE_DETAILED + +# Value of `Time.get_ticks_usec()` on the previous frame. +var last_tick := 0 + +var thread := Thread.new() + +## Returns the sum of all values of an array (use as a parameter to `Array.reduce()`). +var sum_func := func avg(accum: float, number: float) -> float: return accum + number + +# History of the last `HISTORY_NUM_FRAMES` rendered frames. +var frame_history_total: Array[float] = [] +var frame_history_cpu: Array[float] = [] +var frame_history_gpu: Array[float] = [] +var fps_history: Array[float] = [] # Only used for graphs. + +var frametime_avg := GRAPH_MIN_FRAMETIME +var frametime_cpu_avg := GRAPH_MAX_FRAMETIME +var frametime_gpu_avg := GRAPH_MIN_FRAMETIME +var frames_per_second := float(GRAPH_MIN_FPS) +var frame_time_gradient := Gradient.new() + +func _init() -> void: + # This must be done here instead of `_ready()` to avoid having `visibility_changed` be emitted immediately. + visible = false + + if not InputMap.has_action("cycle_debug_menu"): + # Create default input action if no user-defined override exists. + # We can't do it in the editor plugin's activation code as it doesn't seem to work there. + InputMap.add_action("cycle_debug_menu") + var event := InputEventKey.new() + event.keycode = KEY_F3 + InputMap.action_add_event("cycle_debug_menu", event) + + +func _ready() -> void: + fps_graph.draw.connect(_fps_graph_draw) + total_graph.draw.connect(_total_graph_draw) + cpu_graph.draw.connect(_cpu_graph_draw) + gpu_graph.draw.connect(_gpu_graph_draw) + + fps_history.resize(HISTORY_NUM_FRAMES) + frame_history_total.resize(HISTORY_NUM_FRAMES) + frame_history_cpu.resize(HISTORY_NUM_FRAMES) + frame_history_gpu.resize(HISTORY_NUM_FRAMES) + + # NOTE: Both FPS and frametimes are colored following FPS logic + # (red = 10 FPS, yellow = 60 FPS, green = 110 FPS, cyan = 160 FPS). + # This makes the color gradient non-linear. + # Colors are taken from . + frame_time_gradient.set_color(0, Color8(239, 68, 68)) # red-500 + frame_time_gradient.set_color(1, Color8(56, 189, 248)) # light-blue-400 + frame_time_gradient.add_point(0.3333, Color8(250, 204, 21)) # yellow-400 + frame_time_gradient.add_point(0.6667, Color8(128, 226, 95)) # 50-50 mix of lime-400 and green-400 + + get_viewport().size_changed.connect(update_settings_label) + + # Display loading text while information is being queried, + # in case the user toggles the full debug menu just after starting the project. + information.text = "Loading hardware information...\n\n " + settings.text = "Loading project information..." + thread.start( + func(): + # Disable thread safety checks as they interfere with this add-on. + # This only affects this particular thread, not other thread instances in the project. + # See for details. + # Use a Callable so that this can be ignored on Godot 4.0 without causing a script error + # (thread safety checks were added in Godot 4.1). + if Engine.get_version_info()["hex"] >= 0x040100: + Callable(Thread, "set_thread_safety_checks_enabled").call(false) + + # Enable required time measurements to display CPU/GPU frame time information. + # These lines are time-consuming operations, so run them in a separate thread. + RenderingServer.viewport_set_measure_render_time(get_viewport().get_viewport_rid(), true) + update_information_label() + update_settings_label() + ) + + +func _input(event: InputEvent) -> void: + if event.is_action_pressed("cycle_debug_menu"): + style = wrapi(style + 1, 0, Style.MAX) as Style + + +func _exit_tree() -> void: + thread.wait_to_finish() + + +## Update hardware information label (this can change at runtime based on window +## size and graphics settings). This is only called when the window is resized. +## To update when graphics settings are changed, the function must be called manually +## using `DebugMenu.update_settings_label()`. +func update_settings_label() -> void: + settings.text = "" + if ProjectSettings.has_setting("application/config/version"): + settings.text += "Project Version: %s\n" % ProjectSettings.get_setting("application/config/version") + + var rendering_method := str(ProjectSettings.get_setting_with_override("rendering/renderer/rendering_method")) + var rendering_method_string := rendering_method + match rendering_method: + "forward_plus": + rendering_method_string = "Forward+" + "mobile": + rendering_method_string = "Forward Mobile" + "gl_compatibility": + rendering_method_string = "Compatibility" + settings.text += "Rendering Method: %s\n" % rendering_method_string + + var viewport := get_viewport() + + # The size of the viewport rendering, which determines which resolution 3D is rendered at. + var viewport_render_size := Vector2i() + + if viewport.content_scale_mode == Window.CONTENT_SCALE_MODE_VIEWPORT: + viewport_render_size = viewport.get_visible_rect().size + settings.text += "Viewport: %d×%d, Window: %d×%d\n" % [viewport.get_visible_rect().size.x, viewport.get_visible_rect().size.y, viewport.size.x, viewport.size.y] + else: + # Window size matches viewport size. + viewport_render_size = viewport.size + settings.text += "Viewport: %d×%d\n" % [viewport.size.x, viewport.size.y] + + # Display 3D settings only if relevant. + if viewport.get_camera_3d(): + var scaling_3d_mode_string := "(unknown)" + match viewport.scaling_3d_mode: + Viewport.SCALING_3D_MODE_BILINEAR: + scaling_3d_mode_string = "Bilinear" + Viewport.SCALING_3D_MODE_FSR: + scaling_3d_mode_string = "FSR 1.0" + Viewport.SCALING_3D_MODE_FSR2: + scaling_3d_mode_string = "FSR 2.2" + + var antialiasing_3d_string := "" + if viewport.scaling_3d_mode == Viewport.SCALING_3D_MODE_FSR2: + # The FSR2 scaling mode includes its own temporal antialiasing implementation. + antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "FSR 2.2" + if viewport.scaling_3d_mode != Viewport.SCALING_3D_MODE_FSR2 and viewport.use_taa: + # Godot's own TAA is ignored when using FSR2 scaling mode, as FSR2 provides its own TAA implementation. + antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "TAA" + if viewport.msaa_3d >= Viewport.MSAA_2X: + antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "%d× MSAA" % pow(2, viewport.msaa_3d) + if viewport.screen_space_aa == Viewport.SCREEN_SPACE_AA_FXAA: + antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "FXAA" + + settings.text += "3D scale (%s): %d%% = %d×%d" % [ + scaling_3d_mode_string, + viewport.scaling_3d_scale * 100, + viewport_render_size.x * viewport.scaling_3d_scale, + viewport_render_size.y * viewport.scaling_3d_scale, + ] + + if not antialiasing_3d_string.is_empty(): + settings.text += "\n3D Antialiasing: %s" % antialiasing_3d_string + + var environment := viewport.get_camera_3d().get_world_3d().environment + if environment: + if environment.ssr_enabled: + settings.text += "\nSSR: %d Steps" % environment.ssr_max_steps + + if environment.ssao_enabled: + settings.text += "\nSSAO: On" + if environment.ssil_enabled: + settings.text += "\nSSIL: On" + + if environment.sdfgi_enabled: + settings.text += "\nSDFGI: %d Cascades" % environment.sdfgi_cascades + + if environment.glow_enabled: + settings.text += "\nGlow: On" + + if environment.volumetric_fog_enabled: + settings.text += "\nVolumetric Fog: On" + var antialiasing_2d_string := "" + if viewport.msaa_2d >= Viewport.MSAA_2X: + antialiasing_2d_string = "%d× MSAA" % pow(2, viewport.msaa_2d) + + if not antialiasing_2d_string.is_empty(): + settings.text += "\n2D Antialiasing: %s" % antialiasing_2d_string + + +## Update hardware/software information label (this never changes at runtime). +func update_information_label() -> void: + var adapter_string := "" + # Make "NVIDIA Corporation" and "NVIDIA" be considered identical (required when using OpenGL to avoid redundancy). + if RenderingServer.get_video_adapter_vendor().trim_suffix(" Corporation") in RenderingServer.get_video_adapter_name(): + # Avoid repeating vendor name before adapter name. + # Trim redundant suffix sometimes reported by NVIDIA graphics cards when using OpenGL. + adapter_string = RenderingServer.get_video_adapter_name().trim_suffix("/PCIe/SSE2") + else: + adapter_string = RenderingServer.get_video_adapter_vendor() + " - " + RenderingServer.get_video_adapter_name().trim_suffix("/PCIe/SSE2") + + # Graphics driver version information isn't always availble. + var driver_info := OS.get_video_adapter_driver_info() + var driver_info_string := "" + if driver_info.size() >= 2: + driver_info_string = driver_info[1] + else: + driver_info_string = "(unknown)" + + var release_string := "" + if OS.has_feature("editor"): + # Editor build (implies `debug`). + release_string = "editor" + elif OS.has_feature("debug"): + # Debug export template build. + release_string = "debug" + else: + # Release export template build. + release_string = "release" + + var rendering_method := str(ProjectSettings.get_setting_with_override("rendering/renderer/rendering_method")) + var rendering_driver := str(ProjectSettings.get_setting_with_override("rendering/rendering_device/driver")) + var graphics_api_string := rendering_driver + if rendering_method != "gl_compatibility": + if rendering_driver == "d3d12": + graphics_api_string = "Direct3D 12" + elif rendering_driver == "metal": + graphics_api_string = "Metal" + elif rendering_driver == "vulkan": + if OS.has_feature("macos") or OS.has_feature("ios"): + graphics_api_string = "Vulkan via MoltenVK" + else: + graphics_api_string = "Vulkan" + else: + if rendering_driver == "opengl3_angle": + graphics_api_string = "OpenGL via ANGLE" + elif OS.has_feature("mobile") or rendering_driver == "opengl3_es": + graphics_api_string = "OpenGL ES" + elif OS.has_feature("web"): + graphics_api_string = "WebGL" + elif rendering_driver == "opengl3": + graphics_api_string = "OpenGL" + + information.text = ( + "%s, %d threads\n" % [OS.get_processor_name().replace("(R)", "").replace("(TM)", ""), OS.get_processor_count()] + + "%s %s (%s %s), %s %s\n" % [OS.get_name(), "64-bit" if OS.has_feature("64") else "32-bit", release_string, "double" if OS.has_feature("double") else "single", graphics_api_string, RenderingServer.get_video_adapter_api_version()] + + "%s, %s" % [adapter_string, driver_info_string] + ) + + +func _fps_graph_draw() -> void: + var fps_polyline := PackedVector2Array() + fps_polyline.resize(HISTORY_NUM_FRAMES) + for fps_index in fps_history.size(): + fps_polyline[fps_index] = Vector2( + remap(fps_index, 0, fps_history.size(), 0, GRAPH_SIZE.x), + remap(clampf(fps_history[fps_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0) + ) + # Don't use antialiasing to speed up line drawing, but use a width that scales with + # viewport scale to keep the line easily readable on hiDPI displays. + fps_graph.draw_polyline(fps_polyline, frame_time_gradient.sample(remap(frames_per_second, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0) + + +func _total_graph_draw() -> void: + var total_polyline := PackedVector2Array() + total_polyline.resize(HISTORY_NUM_FRAMES) + for total_index in frame_history_total.size(): + total_polyline[total_index] = Vector2( + remap(total_index, 0, frame_history_total.size(), 0, GRAPH_SIZE.x), + remap(clampf(frame_history_total[total_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0) + ) + # Don't use antialiasing to speed up line drawing, but use a width that scales with + # viewport scale to keep the line easily readable on hiDPI displays. + total_graph.draw_polyline(total_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0) + + +func _cpu_graph_draw() -> void: + var cpu_polyline := PackedVector2Array() + cpu_polyline.resize(HISTORY_NUM_FRAMES) + for cpu_index in frame_history_cpu.size(): + cpu_polyline[cpu_index] = Vector2( + remap(cpu_index, 0, frame_history_cpu.size(), 0, GRAPH_SIZE.x), + remap(clampf(frame_history_cpu[cpu_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0) + ) + # Don't use antialiasing to speed up line drawing, but use a width that scales with + # viewport scale to keep the line easily readable on hiDPI displays. + cpu_graph.draw_polyline(cpu_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_cpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0) + + +func _gpu_graph_draw() -> void: + var gpu_polyline := PackedVector2Array() + gpu_polyline.resize(HISTORY_NUM_FRAMES) + for gpu_index in frame_history_gpu.size(): + gpu_polyline[gpu_index] = Vector2( + remap(gpu_index, 0, frame_history_gpu.size(), 0, GRAPH_SIZE.x), + remap(clampf(frame_history_gpu[gpu_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0) + ) + # Don't use antialiasing to speed up line drawing, but use a width that scales with + # viewport scale to keep the line easily readable on hiDPI displays. + gpu_graph.draw_polyline(gpu_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_gpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0) + + +func _process(_delta: float) -> void: + if visible: + fps_graph.queue_redraw() + total_graph.queue_redraw() + cpu_graph.queue_redraw() + gpu_graph.queue_redraw() + + # Difference between the last two rendered frames in milliseconds. + var frametime := (Time.get_ticks_usec() - last_tick) * 0.001 + + frame_history_total.push_back(frametime) + if frame_history_total.size() > HISTORY_NUM_FRAMES: + frame_history_total.pop_front() + + # Frametimes are colored following FPS logic (red = 10 FPS, yellow = 60 FPS, green = 110 FPS, cyan = 160 FPS). + # This makes the color gradient non-linear. + frametime_avg = frame_history_total.reduce(sum_func) / frame_history_total.size() + frame_history_total_avg.text = str(frametime_avg).pad_decimals(2) + frame_history_total_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) + + var frametime_min: float = frame_history_total.min() + frame_history_total_min.text = str(frametime_min).pad_decimals(2) + frame_history_total_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) + + var frametime_max: float = frame_history_total.max() + frame_history_total_max.text = str(frametime_max).pad_decimals(2) + frame_history_total_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) + + frame_history_total_last.text = str(frametime).pad_decimals(2) + frame_history_total_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) + + var viewport_rid := get_viewport().get_viewport_rid() + var frametime_cpu := RenderingServer.viewport_get_measured_render_time_cpu(viewport_rid) + RenderingServer.get_frame_setup_time_cpu() + frame_history_cpu.push_back(frametime_cpu) + if frame_history_cpu.size() > HISTORY_NUM_FRAMES: + frame_history_cpu.pop_front() + + frametime_cpu_avg = frame_history_cpu.reduce(sum_func) / frame_history_cpu.size() + frame_history_cpu_avg.text = str(frametime_cpu_avg).pad_decimals(2) + frame_history_cpu_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) + + var frametime_cpu_min: float = frame_history_cpu.min() + frame_history_cpu_min.text = str(frametime_cpu_min).pad_decimals(2) + frame_history_cpu_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) + + var frametime_cpu_max: float = frame_history_cpu.max() + frame_history_cpu_max.text = str(frametime_cpu_max).pad_decimals(2) + frame_history_cpu_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) + + frame_history_cpu_last.text = str(frametime_cpu).pad_decimals(2) + frame_history_cpu_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) + + var frametime_gpu := RenderingServer.viewport_get_measured_render_time_gpu(viewport_rid) + frame_history_gpu.push_back(frametime_gpu) + if frame_history_gpu.size() > HISTORY_NUM_FRAMES: + frame_history_gpu.pop_front() + + frametime_gpu_avg = frame_history_gpu.reduce(sum_func) / frame_history_gpu.size() + frame_history_gpu_avg.text = str(frametime_gpu_avg).pad_decimals(2) + frame_history_gpu_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) + + var frametime_gpu_min: float = frame_history_gpu.min() + frame_history_gpu_min.text = str(frametime_gpu_min).pad_decimals(2) + frame_history_gpu_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) + + var frametime_gpu_max: float = frame_history_gpu.max() + frame_history_gpu_max.text = str(frametime_gpu_max).pad_decimals(2) + frame_history_gpu_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) + + frame_history_gpu_last.text = str(frametime_gpu).pad_decimals(2) + frame_history_gpu_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) + + frames_per_second = 1000.0 / frametime_avg + fps_history.push_back(frames_per_second) + if fps_history.size() > HISTORY_NUM_FRAMES: + fps_history.pop_front() + + fps.text = str(floor(frames_per_second)) + " FPS" + var frame_time_color := frame_time_gradient.sample(remap(frames_per_second, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)) + fps.modulate = frame_time_color + + frame_time.text = str(frametime).pad_decimals(2) + " mspf" + frame_time.modulate = frame_time_color + + var vsync_string := "" + match DisplayServer.window_get_vsync_mode(): + DisplayServer.VSYNC_ENABLED: + vsync_string = "V-Sync" + DisplayServer.VSYNC_ADAPTIVE: + vsync_string = "Adaptive V-Sync" + DisplayServer.VSYNC_MAILBOX: + vsync_string = "Mailbox V-Sync" + + if Engine.max_fps > 0 or OS.low_processor_usage_mode: + # Display FPS cap determined by `Engine.max_fps` or low-processor usage mode sleep duration + # (the lowest FPS cap is used). + var low_processor_max_fps := roundi(1000000.0 / OS.low_processor_usage_mode_sleep_usec) + var fps_cap := low_processor_max_fps + if Engine.max_fps > 0: + fps_cap = mini(Engine.max_fps, low_processor_max_fps) + frame_time.text += " (cap: " + str(fps_cap) + " FPS" + + if not vsync_string.is_empty(): + frame_time.text += " + " + vsync_string + + frame_time.text += ")" + else: + if not vsync_string.is_empty(): + frame_time.text += " (" + vsync_string + ")" + + frame_number.text = "Frame: " + str(Engine.get_frames_drawn()) + + last_tick = Time.get_ticks_usec() + + +func _on_visibility_changed() -> void: + if visible: + # Reset graphs to prevent them from looking strange before `HISTORY_NUM_FRAMES` frames + # have been drawn. + var frametime_last := (Time.get_ticks_usec() - last_tick) * 0.001 + fps_history.resize(HISTORY_NUM_FRAMES) + fps_history.fill(1000.0 / frametime_last) + frame_history_total.resize(HISTORY_NUM_FRAMES) + frame_history_total.fill(frametime_last) + frame_history_cpu.resize(HISTORY_NUM_FRAMES) + var viewport_rid := get_viewport().get_viewport_rid() + frame_history_cpu.fill(RenderingServer.viewport_get_measured_render_time_cpu(viewport_rid) + RenderingServer.get_frame_setup_time_cpu()) + frame_history_gpu.resize(HISTORY_NUM_FRAMES) + frame_history_gpu.fill(RenderingServer.viewport_get_measured_render_time_gpu(viewport_rid)) diff --git a/addons/debug_menu/debug_menu.gd.uid b/addons/debug_menu/debug_menu.gd.uid new file mode 100644 index 0000000..170cac6 --- /dev/null +++ b/addons/debug_menu/debug_menu.gd.uid @@ -0,0 +1 @@ +uid://bpsslluwe827a diff --git a/addons/debug_menu/debug_menu.tscn b/addons/debug_menu/debug_menu.tscn new file mode 100644 index 0000000..9bfc9d6 --- /dev/null +++ b/addons/debug_menu/debug_menu.tscn @@ -0,0 +1,401 @@ +[gd_scene load_steps=3 format=3 uid="uid://cggqb75a8w8r"] + +[ext_resource type="Script" path="res://addons/debug_menu/debug_menu.gd" id="1_p440y"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ki0n8"] +bg_color = Color(0, 0, 0, 0.25098) + +[node name="CanvasLayer" type="CanvasLayer" node_paths=PackedStringArray("fps", "frame_time", "frame_number", "frame_history_total_avg", "frame_history_total_min", "frame_history_total_max", "frame_history_total_last", "frame_history_cpu_avg", "frame_history_cpu_min", "frame_history_cpu_max", "frame_history_cpu_last", "frame_history_gpu_avg", "frame_history_gpu_min", "frame_history_gpu_max", "frame_history_gpu_last", "fps_graph", "total_graph", "cpu_graph", "gpu_graph", "information", "settings")] +layer = 128 +script = ExtResource("1_p440y") +fps = NodePath("DebugMenu/VBoxContainer/FPS") +frame_time = NodePath("DebugMenu/VBoxContainer/FrameTime") +frame_number = NodePath("DebugMenu/VBoxContainer/FrameNumber") +frame_history_total_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalAvg") +frame_history_total_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalMin") +frame_history_total_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalMax") +frame_history_total_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalLast") +frame_history_cpu_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUAvg") +frame_history_cpu_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUMin") +frame_history_cpu_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUMax") +frame_history_cpu_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPULast") +frame_history_gpu_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUAvg") +frame_history_gpu_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUMin") +frame_history_gpu_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUMax") +frame_history_gpu_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPULast") +fps_graph = NodePath("DebugMenu/VBoxContainer/FPSGraph/Graph") +total_graph = NodePath("DebugMenu/VBoxContainer/TotalGraph/Graph") +cpu_graph = NodePath("DebugMenu/VBoxContainer/CPUGraph/Graph") +gpu_graph = NodePath("DebugMenu/VBoxContainer/GPUGraph/Graph") +information = NodePath("DebugMenu/VBoxContainer/Information") +settings = NodePath("DebugMenu/VBoxContainer/Settings") + +[node name="DebugMenu" type="Control" parent="."] +custom_minimum_size = Vector2(400, 400) +layout_mode = 3 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -416.0 +offset_top = 8.0 +offset_right = -16.0 +offset_bottom = 408.0 +grow_horizontal = 0 +size_flags_horizontal = 8 +size_flags_vertical = 4 +mouse_filter = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="DebugMenu"] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -300.0 +offset_bottom = 374.0 +grow_horizontal = 0 +mouse_filter = 2 +theme_override_constants/separation = 0 + +[node name="FPS" type="Label" parent="DebugMenu/VBoxContainer"] +modulate = Color(0, 1, 0, 1) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 5 +theme_override_constants/line_spacing = 0 +theme_override_font_sizes/font_size = 18 +text = "60 FPS" +horizontal_alignment = 2 + +[node name="FrameTime" type="Label" parent="DebugMenu/VBoxContainer"] +modulate = Color(0, 1, 0, 1) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "16.67 mspf (cap: 123 FPS + Adaptive V-Sync)" +horizontal_alignment = 2 + +[node name="FrameNumber" type="Label" parent="DebugMenu/VBoxContainer"] +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "Frame: 1234" +horizontal_alignment = 2 + +[node name="FrameTimeHistory" type="GridContainer" parent="DebugMenu/VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 8 +mouse_filter = 2 +theme_override_constants/h_separation = 0 +theme_override_constants/v_separation = 0 +columns = 5 + +[node name="Spacer" type="Control" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +custom_minimum_size = Vector2(60, 0) +layout_mode = 2 +mouse_filter = 2 + +[node name="AvgHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "Average" +horizontal_alignment = 2 + +[node name="MinHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "Best" +horizontal_alignment = 2 + +[node name="MaxHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "Worst" +horizontal_alignment = 2 + +[node name="LastHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "Last" +horizontal_alignment = 2 + +[node name="TotalHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "Total:" +horizontal_alignment = 2 + +[node name="TotalAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +modulate = Color(0, 1, 0, 1) +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "123.45" +horizontal_alignment = 2 + +[node name="TotalMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +modulate = Color(0, 1, 0, 1) +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "123.45" +horizontal_alignment = 2 + +[node name="TotalMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +modulate = Color(0, 1, 0, 1) +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "123.45" +horizontal_alignment = 2 + +[node name="TotalLast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +modulate = Color(0, 1, 0, 1) +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "123.45" +horizontal_alignment = 2 + +[node name="CPUHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "CPU:" +horizontal_alignment = 2 + +[node name="CPUAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +modulate = Color(0, 1, 0, 1) +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "123.45" +horizontal_alignment = 2 + +[node name="CPUMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +modulate = Color(0, 1, 0, 1) +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "12.34" +horizontal_alignment = 2 + +[node name="CPUMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +modulate = Color(0, 1, 0, 1) +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "123.45" +horizontal_alignment = 2 + +[node name="CPULast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +modulate = Color(0, 1, 0, 1) +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "123.45" +horizontal_alignment = 2 + +[node name="GPUHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "GPU:" +horizontal_alignment = 2 + +[node name="GPUAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +modulate = Color(0, 1, 0, 1) +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "123.45" +horizontal_alignment = 2 + +[node name="GPUMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +modulate = Color(0, 1, 0, 1) +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "1.23" +horizontal_alignment = 2 + +[node name="GPUMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +modulate = Color(0, 1, 0, 1) +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "123.45" +horizontal_alignment = 2 + +[node name="GPULast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"] +modulate = Color(0, 1, 0, 1) +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "123.45" +horizontal_alignment = 2 + +[node name="FPSGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"] +layout_mode = 2 +mouse_filter = 2 +alignment = 2 + +[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/FPSGraph"] +custom_minimum_size = Vector2(0, 27) +layout_mode = 2 +size_flags_horizontal = 8 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "FPS: ↑" +vertical_alignment = 1 + +[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/FPSGraph"] +custom_minimum_size = Vector2(150, 25) +layout_mode = 2 +size_flags_vertical = 0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8") + +[node name="TotalGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"] +layout_mode = 2 +mouse_filter = 2 +alignment = 2 + +[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/TotalGraph"] +custom_minimum_size = Vector2(0, 27) +layout_mode = 2 +size_flags_horizontal = 8 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "Total: ↓" +vertical_alignment = 1 + +[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/TotalGraph"] +custom_minimum_size = Vector2(150, 25) +layout_mode = 2 +size_flags_vertical = 0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8") + +[node name="CPUGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"] +layout_mode = 2 +mouse_filter = 2 +alignment = 2 + +[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/CPUGraph"] +custom_minimum_size = Vector2(0, 27) +layout_mode = 2 +size_flags_horizontal = 8 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "CPU: ↓" +vertical_alignment = 1 + +[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/CPUGraph"] +custom_minimum_size = Vector2(150, 25) +layout_mode = 2 +size_flags_vertical = 0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8") + +[node name="GPUGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"] +layout_mode = 2 +mouse_filter = 2 +alignment = 2 + +[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/GPUGraph"] +custom_minimum_size = Vector2(0, 27) +layout_mode = 2 +size_flags_horizontal = 8 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "GPU: ↓" +vertical_alignment = 1 + +[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/GPUGraph"] +custom_minimum_size = Vector2(150, 25) +layout_mode = 2 +size_flags_vertical = 0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8") + +[node name="Information" type="Label" parent="DebugMenu/VBoxContainer"] +modulate = Color(1, 1, 1, 0.752941) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "12th Gen Intel(R) Core(TM) i0-1234K +Windows 12 64-bit (double precision), Vulkan 1.2.34 +NVIDIA GeForce RTX 1234, 123.45.67" +horizontal_alignment = 2 + +[node name="Settings" type="Label" parent="DebugMenu/VBoxContainer"] +modulate = Color(0.8, 0.84, 1, 0.752941) +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 3 +theme_override_font_sizes/font_size = 12 +text = "Project Version: 1.2.3 +Rendering Method: Forward+ +Window: 1234×567, Viewport: 1234×567 +3D Scale (FSR 1.0): 100% = 1234×567 +3D Antialiasing: TAA + 2× MSAA + FXAA +SSR: 123 Steps +SSAO: On +SSIL: On +SDFGI: 1 Cascades +Glow: On +Volumetric Fog: On +2D Antialiasing: 2× MSAA" +horizontal_alignment = 2 + +[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"] diff --git a/addons/debug_menu/plugin.cfg b/addons/debug_menu/plugin.cfg new file mode 100644 index 0000000..54100f7 --- /dev/null +++ b/addons/debug_menu/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Debug Menu" +description="In-game debug menu displaying performance metrics and hardware information" +author="Calinou" +version="1.2.0" +script="plugin.gd" diff --git a/addons/debug_menu/plugin.gd b/addons/debug_menu/plugin.gd new file mode 100644 index 0000000..5ec132e --- /dev/null +++ b/addons/debug_menu/plugin.gd @@ -0,0 +1,29 @@ +@tool +extends EditorPlugin + +func _enter_tree() -> void: + add_autoload_singleton("DebugMenu", "res://addons/debug_menu/debug_menu.tscn") + + # FIXME: This appears to do nothing. +# if not ProjectSettings.has_setting("application/config/version"): +# ProjectSettings.set_setting("application/config/version", "1.0.0") +# +# ProjectSettings.set_initial_value("application/config/version", "1.0.0") +# ProjectSettings.add_property_info({ +# name = "application/config/version", +# type = TYPE_STRING, +# }) +# +# if not InputMap.has_action("cycle_debug_menu"): +# InputMap.add_action("cycle_debug_menu") +# var event := InputEventKey.new() +# event.keycode = KEY_F3 +# InputMap.action_add_event("cycle_debug_menu", event) +# +# ProjectSettings.save() + + +func _exit_tree() -> void: + remove_autoload_singleton("DebugMenu") + # Don't remove the project setting's value and input map action, + # as the plugin may be re-enabled in the future. diff --git a/addons/debug_menu/plugin.gd.uid b/addons/debug_menu/plugin.gd.uid new file mode 100644 index 0000000..24ac21b --- /dev/null +++ b/addons/debug_menu/plugin.gd.uid @@ -0,0 +1 @@ +uid://bloj6h52fp002 diff --git a/assets/projectiles/basic_projectile.tscn b/assets/projectiles/basic_projectile.tscn new file mode 100644 index 0000000..f37b077 --- /dev/null +++ b/assets/projectiles/basic_projectile.tscn @@ -0,0 +1,20 @@ +[gd_scene load_steps=4 format=3 uid="uid://d2q0gh76v2wjm"] + +[ext_resource type="Script" uid="uid://d5tiwy16ivu6" path="res://player/weapons/projectile.gd" id="1_4urkw"] +[ext_resource type="Texture2D" uid="uid://b82eovf1htp4i" path="res://assets/sprites/characters/pink/Rock1.png" id="2_5842l"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_o4f18"] +radius = 1.0 + +[node name="BasicProjectile" type="Area2D"] +collision_layer = 16 +collision_mask = 8 +script = ExtResource("1_4urkw") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("CircleShape2D_o4f18") + +[node name="Sprite2D" type="Sprite2D" parent="."] +position = Vector2(5.96046e-08, 5.96046e-08) +scale = Vector2(0.3125, 0.3125) +texture = ExtResource("2_5842l") diff --git a/assets/sprites/enemies/slimes_blue.png b/assets/sprites/enemies/slimes_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..4dd1aa117dcea42f3c73826e9897ad683609f1ee GIT binary patch literal 7641 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 literal 0 HcmV?d00001 diff --git a/assets/sprites/enemies/slimes_blue.png.import b/assets/sprites/enemies/slimes_blue.png.import new file mode 100644 index 0000000..95ac8f9 --- /dev/null +++ b/assets/sprites/enemies/slimes_blue.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://gan2vkax8nwy" +path="res://.godot/imported/slimes_blue.png-714717b05c2104cf50fc9fd6f4afe1c1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/sprites/enemies/slimes_blue.png" +dest_files=["res://.godot/imported/slimes_blue.png-714717b05c2104cf50fc9fd6f4afe1c1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/assets/sprites/enemies/slimes_dark.png b/assets/sprites/enemies/slimes_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..e7ae015774d423ed15bfbadc56810d79077d3a8e GIT binary patch literal 7763 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 a@`frUYSjC0K+-BP;Gvemy(*2z5&r@G%~Op4 literal 0 HcmV?d00001 diff --git a/assets/sprites/enemies/slimes_dark.png.import b/assets/sprites/enemies/slimes_dark.png.import new file mode 100644 index 0000000..7be9f93 --- /dev/null +++ b/assets/sprites/enemies/slimes_dark.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ievi6pon3ptw" +path="res://.godot/imported/slimes_dark.png-b89c4cb22ec4d8d8ca0b6255a8a99a81.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/sprites/enemies/slimes_dark.png" +dest_files=["res://.godot/imported/slimes_dark.png-b89c4cb22ec4d8d8ca0b6255a8a99a81.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/assets/sprites/enemies/slimes_green.png b/assets/sprites/enemies/slimes_green.png new file mode 100644 index 0000000000000000000000000000000000000000..43ae58975b3e6abc6350fdb2905d90c87b509148 GIT binary patch literal 7537 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 B>Ky<8 literal 0 HcmV?d00001 diff --git a/assets/sprites/enemies/slimes_green.png.import b/assets/sprites/enemies/slimes_green.png.import new file mode 100644 index 0000000..11e43ff --- /dev/null +++ b/assets/sprites/enemies/slimes_green.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cqwmi3v81uhmf" +path="res://.godot/imported/slimes_green.png-08e345c322ad9d9ec7922af9737a7e3b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/sprites/enemies/slimes_green.png" +dest_files=["res://.godot/imported/slimes_green.png-08e345c322ad9d9ec7922af9737a7e3b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/assets/sprites/enemies/slimes_pink.png b/assets/sprites/enemies/slimes_pink.png new file mode 100644 index 0000000000000000000000000000000000000000..6c9e6b9a44bd6022203df564e1d600013aea0bcf GIT binary patch literal 7704 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

;SQ(IrsN&jtkq3LP5jx1iI!6Tw>BH40_e*Y z@?`cIB!F2}0Mu>O;<6st+@tmp?-diP0yuy96yBgLw4@X8_f&y%yzu^!cl{H#|4zj0 mp96=oU(FAoLV%-Puwl)Xnz*yw;(&QENJUBW>1PFt(EkOvoQiM& literal 0 HcmV?d00001 diff --git a/assets/sprites/enemies/slimes_white.png.import b/assets/sprites/enemies/slimes_white.png.import new file mode 100644 index 0000000..7ba66fa --- /dev/null +++ b/assets/sprites/enemies/slimes_white.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://badj3651o6jc8" +path="res://.godot/imported/slimes_white.png-fb1b6c0ee38065fb578a69501028a0b7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/sprites/enemies/slimes_white.png" +dest_files=["res://.godot/imported/slimes_white.png-fb1b6c0ee38065fb578a69501028a0b7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/assets/sprites/enemies/slimes_yellow.png b/assets/sprites/enemies/slimes_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..d7814af6b6aa69373e9086b94658cbd8440ab950 GIT binary patch literal 7774 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 P3{Y3mxnHVe>GyvCyKZ*B literal 0 HcmV?d00001 diff --git a/assets/sprites/enemies/slimes_yellow.png.import b/assets/sprites/enemies/slimes_yellow.png.import new file mode 100644 index 0000000..b9ccc2e --- /dev/null +++ b/assets/sprites/enemies/slimes_yellow.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cv7txwf7xhngf" +path="res://.godot/imported/slimes_yellow.png-7184a698ae163f66b11534f79ba701c9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/sprites/enemies/slimes_yellow.png" +dest_files=["res://.godot/imported/slimes_yellow.png-7184a698ae163f66b11534f79ba701c9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +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/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 diff --git a/assets/sprites/tilesets/forest.tres b/assets/sprites/tilesets/forest.tres index 09ff695..e161dc1 100644 --- a/assets/sprites/tilesets/forest.tres +++ b/assets/sprites/tilesets/forest.tres @@ -1131,7 +1131,8 @@ texture_region_size = Vector2i(32, 32) [resource] tile_size = Vector2i(32, 32) -physics_layer_0/collision_layer = 4 +physics_layer_0/collision_layer = 2 +physics_layer_0/collision_mask = 9 terrain_set_0/mode = 0 terrain_set_0/terrain_0/name = "Floor" terrain_set_0/terrain_0/color = Color(0.5, 0.4375, 0.25, 1) diff --git a/assets/weapons/ranged_weapon.tscn b/assets/weapons/ranged_weapon.tscn new file mode 100644 index 0000000..7309407 --- /dev/null +++ b/assets/weapons/ranged_weapon.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://cgxn1f4p4vik6"] + +[ext_resource type="Script" uid="uid://dcenqdci4hjes" path="res://player/weapons/ranged_weapon.gd" id="1_x1kyd"] + +[node name="RangedWeapon" type="Node2D"] +script = ExtResource("1_x1kyd") diff --git a/enemies/scripts/enemy.gd b/enemies/scripts/enemy.gd new file mode 100644 index 0000000..61510e1 --- /dev/null +++ b/enemies/scripts/enemy.gd @@ -0,0 +1 @@ +extends Node diff --git a/enemies/scripts/enemy.gd.uid b/enemies/scripts/enemy.gd.uid new file mode 100644 index 0000000..3252d34 --- /dev/null +++ b/enemies/scripts/enemy.gd.uid @@ -0,0 +1 @@ +uid://btwllkb7meyrw diff --git a/enemies/test_enemy.tscn b/enemies/test_enemy.tscn new file mode 100644 index 0000000..553831d --- /dev/null +++ b/enemies/test_enemy.tscn @@ -0,0 +1,70 @@ +[gd_scene load_steps=10 format=3 uid="uid://cait7d0k1kmsq"] + +[ext_resource type="Texture2D" uid="uid://gan2vkax8nwy" path="res://assets/sprites/enemies/slimes_blue.png" id="1_lcl3f"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_2bghh"] +radius = 14.0357 + +[sub_resource type="AtlasTexture" id="AtlasTexture_8ifd5"] +atlas = ExtResource("1_lcl3f") +region = Rect2(0, 0, 46, 33) + +[sub_resource type="AtlasTexture" id="AtlasTexture_bkvxi"] +atlas = ExtResource("1_lcl3f") +region = Rect2(46, 0, 46, 33) + +[sub_resource type="AtlasTexture" id="AtlasTexture_if8y3"] +atlas = ExtResource("1_lcl3f") +region = Rect2(92, 0, 46, 33) + +[sub_resource type="AtlasTexture" id="AtlasTexture_evp1i"] +atlas = ExtResource("1_lcl3f") +region = Rect2(138, 0, 46, 33) + +[sub_resource type="AtlasTexture" id="AtlasTexture_38q4i"] +atlas = ExtResource("1_lcl3f") +region = Rect2(184, 0, 46, 33) + +[sub_resource type="AtlasTexture" id="AtlasTexture_8o875"] +atlas = ExtResource("1_lcl3f") +region = Rect2(230, 0, 46, 33) + +[sub_resource type="SpriteFrames" id="SpriteFrames_ciurg"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_8ifd5") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_bkvxi") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_if8y3") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_evp1i") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_38q4i") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_8o875") +}], +"loop": true, +"name": &"idle", +"speed": 5.0 +}] + +[node name="TestEnemy" type="CharacterBody2D" groups=["enemies"]] +collision_layer = 8 +collision_mask = 7 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +position = Vector2(0, 1) +shape = SubResource("CircleShape2D_2bghh") + +[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."] +sprite_frames = SubResource("SpriteFrames_ciurg") +animation = &"idle" +autoplay = "idle" +frame_progress = 0.531099 diff --git a/map/Map.tscn b/map/Map.tscn index b4071fe..e98271a 100644 --- a/map/Map.tscn +++ b/map/Map.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=4 format=3 uid="uid://cfkusqucsap26"] +[gd_scene load_steps=5 format=3 uid="uid://cfkusqucsap26"] [ext_resource type="Script" uid="uid://begwu0icmrxyw" path="res://map/map.gd" id="1_l804v"] [ext_resource type="TileSet" uid="uid://c66l102pgntht" path="res://assets/sprites/tilesets/forest.tres" id="2_3nv2f"] +[ext_resource type="PackedScene" uid="uid://cait7d0k1kmsq" path="res://enemies/test_enemy.tscn" id="4_raxr4"] [ext_resource type="PackedScene" uid="uid://bo5aw2cad3akl" path="res://player/player.tscn" id="5_3nv2f"] [node name="Map" type="Node2D"] @@ -17,3 +18,7 @@ tile_set = ExtResource("2_3nv2f") tile_set = ExtResource("2_3nv2f") [node name="Player" parent="." instance=ExtResource("5_3nv2f")] +position = Vector2(20, 20) + +[node name="TestEnemy" parent="." instance=ExtResource("4_raxr4")] +position = Vector2(87, 72) diff --git a/player/modifiers/fire_rate_additive.gd b/player/modifiers/fire_rate_additive.gd index 9ffdbd8..aa62830 100644 --- a/player/modifiers/fire_rate_additive.gd +++ b/player/modifiers/fire_rate_additive.gd @@ -8,6 +8,6 @@ func _init(): description = "Increases fire rate by %0.1f shots per second" % fire_rate_bonus modifier_type = ModifierType.ADDITIVE -func apply_stats_modification(final_stats: Dictionary, base_stats: Dictionary) -> void: +func apply_stats_modification(final_stats: Dictionary, _base_stats: Dictionary) -> void: if final_stats.has("fire_rate"): final_stats.fire_rate += fire_rate_bonus \ No newline at end of file diff --git a/player/modifiers/fire_rate_multiplicative.gd b/player/modifiers/fire_rate_multiplicative.gd index 9a7867f..6b9fcf8 100644 --- a/player/modifiers/fire_rate_multiplicative.gd +++ b/player/modifiers/fire_rate_multiplicative.gd @@ -8,6 +8,6 @@ func _init(): description = "Increases fire rate by %d%%" % ((fire_rate_multiplier - 1.0) * 100) modifier_type = ModifierType.MULTIPLICATIVE -func apply_stats_modification(final_stats: Dictionary, base_stats: Dictionary) -> void: +func apply_stats_modification(final_stats: Dictionary, _base_stats: Dictionary) -> void: if final_stats.has("fire_rate"): final_stats.fire_rate *= fire_rate_multiplier \ No newline at end of file diff --git a/player/player.tscn b/player/player.tscn index 65945b8..4d7e767 100644 --- a/player/player.tscn +++ b/player/player.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=82 format=3 uid="uid://bo5aw2cad3akl"] +[gd_scene load_steps=83 format=3 uid="uid://bo5aw2cad3akl"] [ext_resource type="Script" uid="uid://bq038uo4cm6nv" path="res://player/scripts/player.gd" id="1_oul6g"] [ext_resource type="Texture2D" uid="uid://dqgq2c1h6yk3k" path="res://assets/sprites/characters/pink/Pink_Monster_Attack1_4.png" id="2_yllr7"] @@ -13,6 +13,7 @@ [ext_resource type="Texture2D" uid="uid://538sc3bsdell" path="res://assets/sprites/characters/pink/Pink_Monster_Throw_4.png" id="11_bjvpn"] [ext_resource type="Texture2D" uid="uid://efnfh4mf0ia2" path="res://assets/sprites/characters/pink/Pink_Monster_Walk_6.png" id="12_s7qer"] [ext_resource type="Texture2D" uid="uid://cyfq0x0h2qeof" path="res://assets/sprites/characters/pink/Pink_Monster_Walk+Attack_6.png" id="13_g4c7l"] +[ext_resource type="PackedScene" uid="uid://cgxn1f4p4vik6" path="res://assets/weapons/ranged_weapon.tscn" id="14_kb6p2"] [sub_resource type="CircleShape2D" id="CircleShape2D_rkbax"] @@ -541,7 +542,7 @@ animations = [{ "speed": 5.0 }] -[node name="Player" type="CharacterBody2D"] +[node name="Player" type="CharacterBody2D" groups=["friendly"]] collision_mask = 14 script = ExtResource("1_oul6g") @@ -551,8 +552,11 @@ shape = SubResource("CircleShape2D_rkbax") [node name="PlayerSprite" type="AnimatedSprite2D" parent="."] sprite_frames = SubResource("SpriteFrames_qjt2w") -animation = &"attack_2" -frame_progress = 0.752485 +animation = &"idle" +autoplay = "idle" +frame_progress = 0.749332 [node name="Camera2D" type="Camera2D" parent="."] zoom = Vector2(2, 2) + +[node name="RangedWeapon" parent="." instance=ExtResource("14_kb6p2")] diff --git a/player/scripts/player.gd b/player/scripts/player.gd index ab1dff0..e893280 100644 --- a/player/scripts/player.gd +++ b/player/scripts/player.gd @@ -1,9 +1,12 @@ extends CharacterBody2D @export var speed = 200 -@export var weapon: RangedWeapon @export var special_ability: Ability -@export var movement: PlayerMovement + +var weapon: RangedWeapon + +var movement: PlayerMovement +var combat: PlayerCombat # Last direction for idle state var last_direction = Vector2.DOWN @@ -11,19 +14,16 @@ var last_direction = Vector2.DOWN @onready var animated_sprite = $PlayerSprite func _ready(): - weapon = RangedWeapon.new() - Log.pr("Weapon", weapon) + weapon = $RangedWeapon + combat = PlayerCombat.new() - # Initialize the movement resource with references - if movement: - movement.player = self - movement.animated_sprite = animated_sprite - movement.last_direction = Vector2.DOWN # Default direction - else: - # Create a new resource instance if none was assigned in the editor - movement = PlayerMovement.new() - movement.player = self - movement.animated_sprite = animated_sprite + movement = PlayerMovement.new() + movement.player = self + movement.animated_sprite = animated_sprite + movement.speed = speed + + combat.player = self + combat.animated_sprite = animated_sprite Log.pr("Adding projectile size additive modifier") weapon.add_modifier(ProjectileSizeAdditive.new()) @@ -37,11 +37,15 @@ func _ready(): # Size is now 1.5 * 1.5 = 2.25 # Add another additive size modifier (+0.7 or 70% increase) - Log.pr("Adding another projectile size additive modifier", 0.7) + Log.pr("Adding another projectile size additive modifier", 2) var another_size_mod = ProjectileSizeAdditive.new() another_size_mod.size_increase = 0.7 weapon.add_modifier(another_size_mod) Log.pr(weapon.stats.get_stat("projectile_size")) + weapon.add_modifier(FireRateAdditive.new()) + + func _physics_process(delta): movement.process(delta) + combat.process(delta) diff --git a/player/scripts/player_combat.gd b/player/scripts/player_combat.gd new file mode 100644 index 0000000..21a25ae --- /dev/null +++ b/player/scripts/player_combat.gd @@ -0,0 +1,30 @@ +extends Resource +class_name PlayerCombat + +var player: CharacterBody2D +var animated_sprite: AnimatedSprite2D + +func process(_delta): + # Get mouse position in global coordinates + var mouse_position = player.get_global_mouse_position() + + # Get player position (assuming this script is attached to the player) + var player_position = player.global_position + + # Calculate direction vector from player to mouse + var direction = mouse_position - player_position + + # You can normalize this vector if you want a unit vector (length of 1) + # This is useful if you only care about direction, not distance + var normalized_direction = direction.normalized() + + if Input.is_action_pressed("fire"): + player.weapon.fire(normalized_direction) + # Update animation + #update_animation() + +func update_animation(): + Log.pr(animated_sprite.animation) + if animated_sprite.animation != "throw": + Log.pr('Throwing animation!') + animated_sprite.play("throw") diff --git a/player/scripts/player_combat.gd.uid b/player/scripts/player_combat.gd.uid new file mode 100644 index 0000000..1d95b1c --- /dev/null +++ b/player/scripts/player_combat.gd.uid @@ -0,0 +1 @@ +uid://cx4ugb3hbh8t1 diff --git a/player/scripts/player_movement.gd b/player/scripts/player_movement.gd index dea2c9e..b1d4f24 100644 --- a/player/scripts/player_movement.gd +++ b/player/scripts/player_movement.gd @@ -4,7 +4,7 @@ class_name PlayerMovement var player: CharacterBody2D var animated_sprite: AnimatedSprite2D -var speed: float = 300.0 +var speed: float var last_direction: Vector2 = Vector2.ZERO func process(_delta): @@ -36,6 +36,9 @@ func process(_delta): func update_animation(direction): var anim_name = "idle" # Default animation + + if animated_sprite.animation == "throw": + return # Don't change animation if throwing if direction == Vector2.ZERO: # Character is idle diff --git a/player/weapons/projectile.gd b/player/weapons/projectile.gd new file mode 100644 index 0000000..62e9956 --- /dev/null +++ b/player/weapons/projectile.gd @@ -0,0 +1,110 @@ +class_name Projectile extends Area2D + +signal on_hit(projectile, target) +signal on_spawned(projectile) +signal on_destroyed(projectile) + +@export var speed: float = 500.0 +@export var damage: float = 10.0 +@export var lifetime: float = 5.0 +@export var direction: Vector2 = Vector2.RIGHT +@export var is_friendly: bool = true + +# Modifier-related properties +var pierce_count: int = 0 +var has_explosive_impact: bool = true +var explosion_projectile_count: int = 2 +var explosion_projectile_damage_mult: float = 0.5 +var explosion_projectile_speed: float = 300.0 +var explosion_spread_angle: float = 360.0 # Full circle by default +# References +var source_weapon: RangedWeapon # Reference to the weapon that fired this +var lifetime_timer: Timer +# Add a variable to track the entity that triggered the explosion +var ignore_target = null + +func _ready(): + lifetime_timer = Timer.new() + add_child(lifetime_timer) + lifetime_timer.one_shot = true + lifetime_timer.wait_time = lifetime + lifetime_timer.connect("timeout", _on_lifetime_timeout) + lifetime_timer.start() + + emit_signal("on_spawned", self) + connect("body_entered", _on_body_entered) + +func _physics_process(delta): + position += direction * speed * delta + +func _on_body_entered(body): + # Check if this is a body we should ignore + if body == ignore_target: + return + + if body.is_in_group("enemies") and is_friendly: + Log.pr("Hit enemy: ", body.name) + # Deal damage to enemy + if body.has_method("take_damage"): + body.take_damage(damage) + + # Emit signal for modifiers to react to + emit_signal("on_hit", self, body) + + # Handle piercing + if pierce_count > 0: + pierce_count -= 1 + else: + # Handle explosive impact + if has_explosive_impact: + # Store the target that triggered the explosion + ignore_target = body + _trigger_explosion() + + # Destroy the projectile + destroy() + +func _trigger_explosion(): + # Create the explosion VFX + # var explosion = preload("res://scenes/explosion_effect.tscn").instantiate() + # explosion.global_position = global_position + # get_tree().root.add_child(explosion) + # Spawn the additional projectiles + if explosion_projectile_count > 0: + _spawn_explosion_projectiles() + +func _spawn_explosion_projectiles(): + # Calculate even angle distribution + var angle_step = explosion_spread_angle / explosion_projectile_count + var start_angle = - explosion_spread_angle / 2 + + for i in range(explosion_projectile_count): + # Create a new projectile + var new_proj = duplicate() + new_proj.global_position = global_position + + # Calculate new direction based on spread + var random_angle = randf_range(0, 2 * PI) + var new_dir = Vector2.RIGHT.rotated(random_angle) + + # Set properties for the new projectile + new_proj.direction = new_dir + new_proj.damage = damage * explosion_projectile_damage_mult + new_proj.speed = explosion_projectile_speed + + # Clear explosive properties so we don't get infinite loops + new_proj.has_explosive_impact = true + new_proj.explosion_projectile_count = 1 + + # Pass the ignore_target to the new projectiles + new_proj.ignore_target = ignore_target + + # Add to scene tree + get_tree().root.add_child(new_proj) + +func destroy(): + emit_signal("on_destroyed", self) + queue_free() + +func _on_lifetime_timeout(): + destroy() diff --git a/player/weapons/projectile.gd.uid b/player/weapons/projectile.gd.uid new file mode 100644 index 0000000..a9e306d --- /dev/null +++ b/player/weapons/projectile.gd.uid @@ -0,0 +1 @@ +uid://d5tiwy16ivu6 diff --git a/player/weapons/ranged_weapon.gd b/player/weapons/ranged_weapon.gd index 167bf68..99ab71a 100644 --- a/player/weapons/ranged_weapon.gd +++ b/player/weapons/ranged_weapon.gd @@ -7,10 +7,12 @@ signal projectile_spawned(projectile) # Base stats - will be modified by modifiers var base_stats = { "damage": 10.0, - "fire_rate": 2.0, # Shots per second + "fire_rate": 2.0, "projectile_speed": 500.0, "projectile_size": 1.0, "projectile_lifetime": 5.0, + "projectile_quantity": 1, + "projectile_spread": 33, "max_pierce": 0 } @@ -24,14 +26,15 @@ func _init() -> void: Log.pr(stats) add_child(stats) -func _ready(): - # Connect to stats updated signal - stats.connect("stats_updated", _on_stats_updated) - # Setup fire timer fire_timer = Timer.new() add_child(fire_timer) fire_timer.one_shot = true + + projectile_scene = preload("res://assets/projectiles/basic_projectile.tscn") + +func _ready(): + stats.connect("stats_updated", _on_stats_updated) fire_timer.connect("timeout", _on_fire_timer_timeout) # Initial update @@ -47,30 +50,55 @@ func fire(direction: Vector2): fire_timer.start(1.0 / stats.get_stat("fire_rate")) func _spawn_projectile(spawn_position: Vector2, spawn_direction: Vector2): - var projectile = projectile_scene.instantiate() - projectile.global_position = spawn_position - projectile.direction = spawn_direction + # Get projectile quantity and spread from stats + var quantity = stats.get_stat("projectile_quantity") + var spread_angle = stats.get_stat("projectile_spread") - # Apply stats to projectile - projectile.speed = stats.get_stat("projectile_speed") - projectile.damage = stats.get_stat("damage") - projectile.lifetime = stats.get_stat("projectile_lifetime") - projectile.pierce_count = stats.get_stat("max_pierce") - projectile.source_weapon = self + # Calculate the angle between each projectile + var angle_step = 0.0 + if quantity > 1 and spread_angle > 0: + angle_step = spread_angle / (quantity - 1) - # Apply size (scale) - var size = stats.get_stat("projectile_size") - projectile.scale = Vector2(size, size) + # Calculate starting angle (to center the spread) + var start_angle = - spread_angle / 2 - # Allow modifiers to directly modify the projectile - for modifier in stats.modifiers: - modifier.modify_projectile(projectile) - - get_tree().root.add_child(projectile) - projectile.emit_signal("on_spawned", projectile) - emit_signal("projectile_spawned", projectile) + # Spawn each projectile + for i in range(quantity): + var projectile = projectile_scene.instantiate() + projectile.global_position = spawn_position + + # Calculate the direction with spread + var direction = spawn_direction + if quantity > 1: + var current_angle = start_angle + (i * angle_step) + direction = spawn_direction.rotated(deg_to_rad(current_angle)) + + projectile.direction = direction + + # Apply stats to projectile + projectile.speed = stats.get_stat("projectile_speed") + projectile.damage = stats.get_stat("damage") + projectile.lifetime = stats.get_stat("projectile_lifetime") + projectile.source_weapon = self + + # Set base size + var size = stats.get_stat("projectile_size") + projectile.scale = Vector2(size, size) + + # Allow modifiers to directly modify the projectile + for modifier in stats.modifiers: + modifier.modify_projectile(projectile) + + # Add to scene tree + if get_tree() and get_tree().get_root(): + get_tree().get_root().add_child(projectile) + + # Emit the spawn signal + if projectile.has_signal("on_spawned"): + projectile.emit_signal("on_spawned", projectile) func add_modifier(modifier: Modifier): + Log.pr("Adding modifier: ", modifier) stats.add_modifier(modifier) func remove_modifier(modifier_id: String): diff --git a/project.godot b/project.godot index 7a567a2..fe612eb 100644 --- a/project.godot +++ b/project.godot @@ -21,6 +21,7 @@ RNG="*res://utility/RngUtility.gd" Global="*res://utility/Globals.gd" SceneSelector="*res://utility/SceneSelector.gd" MapBuilder="*res://utility/MapBuilder.gd" +DebugMenu="*res://addons/debug_menu/debug_menu.tscn" [display] @@ -30,7 +31,7 @@ window/stretch/scale_mode="integer" [editor_plugins] -enabled=PackedStringArray("res://addons/log/plugin.cfg") +enabled=PackedStringArray("res://addons/debug_menu/plugin.cfg", "res://addons/log/plugin.cfg") [input] @@ -54,6 +55,11 @@ move_right={ "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"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":100,"location":0,"echo":false,"script":null) ] } +fire={ +"deadzone": 0.2, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(115, 24),"global_position":Vector2(129, 97),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null) +] +} [layer_names] @@ -61,6 +67,7 @@ move_right={ 2d_physics/layer_2="Water" 2d_physics/layer_3="Objects" 2d_physics/layer_4="Enemies" +2d_physics/layer_5="Projectiles" [rendering]