From 40ebde624cb4b930c50fd5896654e7af1e966696 Mon Sep 17 00:00:00 2001 From: Haze Weathers Date: Tue, 9 Dec 2025 14:43:30 -0600 Subject: [PATCH] initial project setup --- .editorconfig | 4 ++ .gitattributes | 2 + .gitignore | 3 + globals/background.tscn | 12 ++++ globals/display.gd | 109 +++++++++++++++++++++++++++++++++++++ globals/display.gd.uid | 1 + globals/display.tscn | 35 ++++++++++++ globals/scene_stack.gd | 36 ++++++++++++ globals/scene_stack.gd.uid | 1 + icon.svg | 1 + icon.svg.import | 43 +++++++++++++++ project.godot | 40 ++++++++++++++ scenes/test_scene.tscn | 9 +++ 13 files changed, 296 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 globals/background.tscn create mode 100644 globals/display.gd create mode 100644 globals/display.gd.uid create mode 100644 globals/display.tscn create mode 100644 globals/scene_stack.gd create mode 100644 globals/scene_stack.gd.uid create mode 100644 icon.svg create mode 100644 icon.svg.import create mode 100644 project.godot create mode 100644 scenes/test_scene.tscn diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f28239b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +root = true + +[*] +charset = utf-8 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0af181c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Godot 4+ specific ignores +.godot/ +/android/ diff --git a/globals/background.tscn b/globals/background.tscn new file mode 100644 index 0000000..7e401dd --- /dev/null +++ b/globals/background.tscn @@ -0,0 +1,12 @@ +[gd_scene format=3 uid="uid://cmh8bcjad3433"] + +[node name="Background" type="CanvasLayer"] +layer = -2147483648 + +[node name="ClearColor" type="ColorRect" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 1) diff --git a/globals/display.gd b/globals/display.gd new file mode 100644 index 0000000..751162f --- /dev/null +++ b/globals/display.gd @@ -0,0 +1,109 @@ +extends Node + + +enum ScaleMode { + ## Stretches to fit as close as possible while maintaining the game's aspect ratio. + ASPECT, + ## Stretches to fit as close as possible while maintaining aspect ratio and an integer scale. + INTEGER, + ## Stretches to cover the whole screen, warping the aspect ratio. + STRETCH, +} + + +## Strategy to use for scaling from game-native resolution to the window/screen size. +@export var scale_mode: ScaleMode = ScaleMode.ASPECT: + set(value): + scale_mode = value + if is_node_ready(): + _update_scale() + +## Directory to scan screen filters from. +@export_dir var filters_dir: String = "res://" + +@export_group("Internal References") +@export var viewport: SubViewport +@export var viewport_container: SubViewportContainer +@export var _native_filters_layer: CanvasLayer + + +## The native resolution of the game. +var size: Vector2i = Vector2i( + ProjectSettings.get_setting("display/window/size/viewport_width"), + ProjectSettings.get_setting("display/window/size/viewport_height"), +) + +## Whether each filter is enabled or disabled. +var filters_enabled: Dictionary[StringName, bool] = {} + + +var _filter_instances: Dictionary[StringName, ColorRect] = {} + + +func _enter_tree() -> void: + get_tree().scene_changed.connect(_on_scene_changed) + get_tree().root.size_changed.connect(_update_scale) + + # populate screen filters + for file in ResourceLoader.list_directory(filters_dir): + var material = load(filters_dir.path_join(file)) as Material + if material: + var id = StringName(file.get_basename()) + + if _filter_instances.has(id): + push_error("Screen filter %s exists in two different resource files. Only one will exist" % id) + _filter_instances[id].queue_free() + _filter_instances.erase(id) + + var instance = ColorRect.new() + instance.material = material + if material.get_meta(&"filter_native_resolution", false): + _native_filters_layer.add_child(instance) + else: + viewport_container.add_child(instance) + _filter_instances[id] = instance + filters_enabled[id] = true + + _update_scale.call_deferred() + + var current_scene = get_tree().current_scene + if current_scene and current_scene != self: + if current_scene.get_parent(): + current_scene.reparent.call_deferred(viewport) + else: + viewport.add_child.call_deferred(current_scene) + + +func _on_scene_changed() -> void: + for child in viewport.get_children(): + child.queue_free() + get_tree().current_scene.reparent(viewport) + + +func _update_scale() -> void: + var screen_size = Vector2(get_tree().root.size) + var size_ratio = screen_size / Vector2(size) + + DisplayServer.window_set_min_size(size) + + viewport.size = size + viewport_container.pivot_offset = Vector2(size) * 0.5 + viewport_container.position = screen_size * 0.5 - Vector2(size) * 0.5 + + match scale_mode: + ScaleMode.ASPECT: + # get the minimum ratio and use for both axes + var min_scale = minf(size_ratio.x, size_ratio.y) + viewport_container.scale = Vector2(min_scale, min_scale) + ScaleMode.INTEGER: + # get the minimum ratio and use for both axes after flooring + var min_scale = floorf(minf(size_ratio.x, size_ratio.y)) + viewport_container.scale = Vector2(min_scale, min_scale) + ScaleMode.STRETCH: + # just use the ratio as-is + viewport_container.scale = size_ratio + + # update screen filters state + for filter in filters_enabled: + _filter_instances[filter].visible = filters_enabled[filter] + _filter_instances[filter].custom_minimum_size = Vector2(size) diff --git a/globals/display.gd.uid b/globals/display.gd.uid new file mode 100644 index 0000000..76403f2 --- /dev/null +++ b/globals/display.gd.uid @@ -0,0 +1 @@ +uid://kwa8v1dhwlie diff --git a/globals/display.tscn b/globals/display.tscn new file mode 100644 index 0000000..b445dc0 --- /dev/null +++ b/globals/display.tscn @@ -0,0 +1,35 @@ +[gd_scene load_steps=2 format=3 uid="uid://bixq77klfo561"] + +[ext_resource type="Script" uid="uid://kwa8v1dhwlie" path="res://globals/display.gd" id="1_1seuv"] + +[node name="Display" type="Node" node_paths=PackedStringArray("viewport", "viewport_container", "_native_filters_layer")] +script = ExtResource("1_1seuv") +scale_mode = 1 +filters_dir = "res://assets/screen_filters" +viewport = NodePath("SubViewportContainer/SubViewport") +viewport_container = NodePath("SubViewportContainer") +_native_filters_layer = NodePath("SubViewportContainer/SubViewport/NativeResolutionFilters") + +[node name="SubViewportContainer" type="SubViewportContainer" parent="."] +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -128.0 +offset_top = -96.0 +offset_right = 128.0 +offset_bottom = 96.0 +grow_horizontal = 2 +grow_vertical = 2 +pivot_offset = Vector2(128, 96) + +[node name="SubViewport" type="SubViewport" parent="SubViewportContainer"] +handle_input_locally = false +snap_2d_transforms_to_pixel = true +canvas_item_default_texture_filter = 0 +size = Vector2i(256, 192) +render_target_update_mode = 4 + +[node name="NativeResolutionFilters" type="CanvasLayer" parent="SubViewportContainer/SubViewport"] +layer = 2147483647 diff --git a/globals/scene_stack.gd b/globals/scene_stack.gd new file mode 100644 index 0000000..58eb49c --- /dev/null +++ b/globals/scene_stack.gd @@ -0,0 +1,36 @@ +extends Node + + +## The stack of scenes. +var scenes: Array[Node] + + +func _enter_tree() -> void: + scenes.push_back(get_tree().current_scene) + + +## Pushes a new scene onto the stack. It will replace the current scene until it is popped. +func push_scene(scene: Node) -> void: + scenes.push_back(scene) + get_tree().current_scene = scene + get_tree().scene_changed.emit() + + +## Pops the current scene off the stack, returning to the scene below it. If there are no +## scenes left, the game quits. +func pop_scene() -> void: + scenes.pop_back().queue_free() + if scenes.is_empty(): + get_tree().quit() + return + + get_tree().current_scene = scenes.back() + get_tree().scene_changed.emit() + + +## Swaps the current scene with a new scene. +func swap_scene(scene: Node) -> void: + scenes.pop_back().queue_free() + scenes.push_back(scene) + get_tree().current_scene = scene + get_tree().scene_changed.emit() diff --git a/globals/scene_stack.gd.uid b/globals/scene_stack.gd.uid new file mode 100644 index 0000000..278c147 --- /dev/null +++ b/globals/scene_stack.gd.uid @@ -0,0 +1 @@ +uid://daxi080e2jmpi 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..d144347 --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c50bfqprpitev" +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..2c816de --- /dev/null +++ b/project.godot @@ -0,0 +1,40 @@ +; 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="Paratate" +run/main_scene="uid://dxsp66qpvm65b" +config/features=PackedStringArray("4.5", "GL Compatibility") +config/icon="res://icon.svg" + +[autoload] + +Background="*res://globals/background.tscn" +Display="*res://globals/display.tscn" +SceneStack="*res://globals/scene_stack.gd" + +[display] + +window/size/viewport_width=240 +window/size/viewport_height=320 + +[file_customization] + +folder_colors={ +"res://globals/": "orange", +"res://scenes/": "teal" +} + +[rendering] + +textures/canvas_textures/default_texture_filter=0 +renderer/rendering_method="gl_compatibility" +renderer/rendering_method.mobile="gl_compatibility" diff --git a/scenes/test_scene.tscn b/scenes/test_scene.tscn new file mode 100644 index 0000000..5b7c1ba --- /dev/null +++ b/scenes/test_scene.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=2 format=3 uid="uid://dxsp66qpvm65b"] + +[ext_resource type="Texture2D" uid="uid://c50bfqprpitev" path="res://icon.svg" id="1_g7g4h"] + +[node name="TestScene" type="Node"] + +[node name="Icon" type="Sprite2D" parent="."] +position = Vector2(110, 187) +texture = ExtResource("1_g7g4h")