diff --git a/Fonts/AtkinsonHyperlegible-Regular.ttf b/Fonts/AtkinsonHyperlegible-Regular.ttf new file mode 100644 index 0000000..46def7f Binary files /dev/null and b/Fonts/AtkinsonHyperlegible-Regular.ttf differ diff --git a/Fonts/AtkinsonHyperlegible-Regular.ttf.import b/Fonts/AtkinsonHyperlegible-Regular.ttf.import new file mode 100644 index 0000000..7513512 --- /dev/null +++ b/Fonts/AtkinsonHyperlegible-Regular.ttf.import @@ -0,0 +1,38 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://dq8ojwul8884x" +path="res://.godot/imported/AtkinsonHyperlegible-Regular.ttf-8428368664fa87474099ab40902da12f.fontdata" + +[deps] + +source_file="res://Fonts/AtkinsonHyperlegible-Regular.ttf" +dest_files=["res://.godot/imported/AtkinsonHyperlegible-Regular.ttf-8428368664fa87474099ab40902da12f.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +multichannel_signed_distance_field=true +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[{ +"chars": [], +"glyphs": [], +"name": "New Configuration", +"size": Vector2i(16, 0) +}] +language_support={} +script_support={} +opentype_features={} diff --git a/BouncyMaterial.tres b/Resources/BouncyMaterial.tres similarity index 100% rename from BouncyMaterial.tres rename to Resources/BouncyMaterial.tres diff --git a/Resources/MenuTheme.tres b/Resources/MenuTheme.tres new file mode 100644 index 0000000..f2f306b --- /dev/null +++ b/Resources/MenuTheme.tres @@ -0,0 +1,11 @@ +[gd_resource type="Theme" load_steps=2 format=3 uid="uid://bk0vu443pyq1g"] + +[ext_resource type="FontFile" uid="uid://dq8ojwul8884x" path="res://Fonts/AtkinsonHyperlegible-Regular.ttf" id="1_aadm8"] + +[resource] +Button/font_sizes/font_size = 32 +Button/fonts/font = ExtResource("1_aadm8") +Label/font_sizes/font_size = 32 +Label/fonts/font = ExtResource("1_aadm8") +LineEdit/font_sizes/font_size = 32 +LineEdit/fonts/font = ExtResource("1_aadm8") diff --git a/Resources/ScoreLabelSettings.tres b/Resources/ScoreLabelSettings.tres new file mode 100644 index 0000000..bbcb0b8 --- /dev/null +++ b/Resources/ScoreLabelSettings.tres @@ -0,0 +1,7 @@ +[gd_resource type="LabelSettings" load_steps=2 format=3 uid="uid://dkyqusswobqb3"] + +[ext_resource type="FontFile" uid="uid://dq8ojwul8884x" path="res://Fonts/AtkinsonHyperlegible-Regular.ttf" id="1_hcuvo"] + +[resource] +font = ExtResource("1_hcuvo") +font_size = 64 diff --git a/Scenes/MainMenu.tscn b/Scenes/MainMenu.tscn deleted file mode 100644 index 14306d5..0000000 --- a/Scenes/MainMenu.tscn +++ /dev/null @@ -1,29 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://0vkrkvlevwkj"] - -[ext_resource type="Script" path="res://Scripts/Managers/MainMenuManager.cs" id="1_tuygx"] - -[sub_resource type="LabelSettings" id="LabelSettings_vv6u3"] -font_size = 64 - -[node name="MainMenu" type="VBoxContainer"] -offset_right = 800.0 -offset_bottom = 600.0 -size_flags_horizontal = 3 -size_flags_vertical = 3 -script = ExtResource("1_tuygx") - -[node name="Label" type="Label" parent="."] -layout_mode = 2 -text = "Pong -" -label_settings = SubResource("LabelSettings_vv6u3") -horizontal_alignment = 1 -vertical_alignment = 1 - -[node name="Button" type="Button" parent="."] -layout_mode = 2 -theme_override_font_sizes/font_size = 32 -text = "Play -" - -[connection signal="pressed" from="Button" to="." method="OnButtonPressed"] diff --git a/Scenes/Ball.tscn b/Scenes/Objects/Ball.tscn similarity index 80% rename from Scenes/Ball.tscn rename to Scenes/Objects/Ball.tscn index c3f165b..05e0125 100644 --- a/Scenes/Ball.tscn +++ b/Scenes/Objects/Ball.tscn @@ -1,6 +1,6 @@ -[gd_scene load_steps=4 format=3 uid="uid://67qguep14iko"] +[gd_scene load_steps=4 format=3] -[ext_resource type="Script" path="res://Scripts/Nodes/Ball.cs" id="1_474si"] +[ext_resource type="Script" path="res://Scripts/Objects/Ball.cs" id="1_474si"] [sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_yq7ub"] size = Vector2(50, 50) diff --git a/Scenes/Enemy.tscn b/Scenes/Objects/Enemy.tscn similarity index 84% rename from Scenes/Enemy.tscn rename to Scenes/Objects/Enemy.tscn index 031f189..73c317b 100644 --- a/Scenes/Enemy.tscn +++ b/Scenes/Objects/Enemy.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=5 format=3] -[ext_resource type="PhysicsMaterial" uid="uid://e05n66x8ug77" path="res://BouncyMaterial.tres" id="1_e3kk5"] -[ext_resource type="Script" path="res://Scripts/Nodes/Enemy.cs" id="1_fbrtv"] +[ext_resource type="PhysicsMaterial" uid="uid://e05n66x8ug77" path="res://Resources/BouncyMaterial.tres" id="1_e3kk5"] +[ext_resource type="Script" path="res://Scripts/Objects/Enemy.cs" id="1_fbrtv"] [sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_iw3nx"] size = Vector2(50, 75) diff --git a/Scenes/Paddle.tscn b/Scenes/Objects/Paddle.tscn similarity index 83% rename from Scenes/Paddle.tscn rename to Scenes/Objects/Paddle.tscn index 6ab4142..147a898 100644 --- a/Scenes/Paddle.tscn +++ b/Scenes/Objects/Paddle.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=5 format=3] -[ext_resource type="PhysicsMaterial" uid="uid://e05n66x8ug77" path="res://BouncyMaterial.tres" id="1_76uik"] -[ext_resource type="Script" path="res://Scripts/Nodes/Paddle.cs" id="1_uv7s3"] +[ext_resource type="PhysicsMaterial" uid="uid://e05n66x8ug77" path="res://Resources/BouncyMaterial.tres" id="1_76uik"] +[ext_resource type="Script" path="res://Scripts/Objects/Paddle.cs" id="1_uv7s3"] [sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_iw3nx"] size = Vector2(50, 75) diff --git a/Scenes/Walls.tscn b/Scenes/Objects/Walls.tscn similarity index 94% rename from Scenes/Walls.tscn rename to Scenes/Objects/Walls.tscn index a4a2866..1547fcd 100644 --- a/Scenes/Walls.tscn +++ b/Scenes/Objects/Walls.tscn @@ -1,6 +1,6 @@ -[gd_scene load_steps=8 format=3 uid="uid://dsvv7vqonylr1"] +[gd_scene load_steps=8 format=3] -[ext_resource type="Script" path="res://Scripts/Managers/WallManager.cs" id="1_mxer2"] +[ext_resource type="Script" path="res://Scripts/Managers/Walls.cs" id="1_mxer2"] [sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_f7jy5"] size = Vector2(30, 1) diff --git a/Scenes/Pong.tscn b/Scenes/Pong.tscn index a0b47d6..8141f75 100644 --- a/Scenes/Pong.tscn +++ b/Scenes/Pong.tscn @@ -1,14 +1,14 @@ -[gd_scene load_steps=7 format=3 uid="uid://wn48xnd0lstq"] +[gd_scene load_steps=7 format=3 uid="uid://bh3p1hnvsviu6"] -[ext_resource type="PackedScene" path="res://Scenes/Paddle.tscn" id="1_5rs0o"] -[ext_resource type="Script" path="res://Scripts/Managers/SceneManager.cs" id="1_m8437"] -[ext_resource type="PackedScene" uid="uid://co2rv0nci8sbm" path="res://Scenes/Score.tscn" id="2_f3jwj"] -[ext_resource type="PackedScene" uid="uid://67qguep14iko" path="res://Scenes/Ball.tscn" id="2_u2ksv"] -[ext_resource type="PackedScene" uid="uid://dsvv7vqonylr1" path="res://Scenes/Walls.tscn" id="3_jfis7"] -[ext_resource type="PackedScene" path="res://Scenes/Enemy.tscn" id="4_uwvof"] +[ext_resource type="PackedScene" path="res://Scenes/Objects/Paddle.tscn" id="1_5rs0o"] +[ext_resource type="Script" path="res://Scripts/Managers/PongSceneManager.cs" id="1_ee533"] +[ext_resource type="PackedScene" uid="uid://clsmrwvyrt7av" path="res://Scenes/UI/Score.tscn" id="2_f3jwj"] +[ext_resource type="PackedScene" path="res://Scenes/Objects/Ball.tscn" id="2_u2ksv"] +[ext_resource type="PackedScene" path="res://Scenes/Objects/Walls.tscn" id="3_jfis7"] +[ext_resource type="PackedScene" path="res://Scenes/Objects/Enemy.tscn" id="4_uwvof"] [node name="Pong" type="Node2D"] -script = ExtResource("1_m8437") +script = ExtResource("1_ee533") [node name="Score" parent="." instance=ExtResource("2_f3jwj")] diff --git a/Scenes/UI/Menus/MainMenu.tscn b/Scenes/UI/Menus/MainMenu.tscn new file mode 100644 index 0000000..8c9425f --- /dev/null +++ b/Scenes/UI/Menus/MainMenu.tscn @@ -0,0 +1,32 @@ +[gd_scene load_steps=4 format=3 uid="uid://buvptlse06qej"] + +[ext_resource type="Script" path="res://Scripts/Managers/UI/Menus/MainMenu.cs" id="1_essvw"] +[ext_resource type="Theme" uid="uid://bk0vu443pyq1g" path="res://Resources/MenuTheme.tres" id="1_qt6h4"] +[ext_resource type="LabelSettings" uid="uid://dkyqusswobqb3" path="res://Resources/ScoreLabelSettings.tres" id="2_mtqkf"] + +[node name="MainMenu" type="VBoxContainer"] +offset_right = 800.0 +offset_bottom = 600.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme = ExtResource("1_qt6h4") +script = ExtResource("1_essvw") + +[node name="Label" type="Label" parent="."] +layout_mode = 2 +text = "Pong" +label_settings = ExtResource("2_mtqkf") +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="PlayButton" type="Button" parent="."] +layout_mode = 2 +text = "Play +" + +[node name="SettingsButton" type="Button" parent="."] +layout_mode = 2 +text = "Settings" + +[connection signal="pressed" from="PlayButton" to="." method="OnPlayButtonPressed"] +[connection signal="pressed" from="SettingsButton" to="." method="OnSettingsButtonPressed"] diff --git a/Scenes/UI/Menus/Settings.tscn b/Scenes/UI/Menus/Settings.tscn new file mode 100644 index 0000000..ba54748 --- /dev/null +++ b/Scenes/UI/Menus/Settings.tscn @@ -0,0 +1,63 @@ +[gd_scene load_steps=5 format=3 uid="uid://ic7535p11pds"] + +[ext_resource type="Theme" uid="uid://bk0vu443pyq1g" path="res://Resources/MenuTheme.tres" id="1_p8g8v"] +[ext_resource type="Script" path="res://Scripts/Managers/UI/Menus/Settings.cs" id="2_vlocu"] +[ext_resource type="LabelSettings" uid="uid://dkyqusswobqb3" path="res://Resources/ScoreLabelSettings.tres" id="2_wkoeb"] +[ext_resource type="FontFile" uid="uid://dq8ojwul8884x" path="res://Fonts/AtkinsonHyperlegible-Regular.ttf" id="3_ngmnl"] + +[node name="Settings" type="VBoxContainer"] +offset_right = 800.0 +offset_bottom = 600.0 +theme = ExtResource("1_p8g8v") +script = ExtResource("2_vlocu") + +[node name="Label" type="Label" parent="."] +layout_mode = 2 +text = "Settings +" +label_settings = ExtResource("2_wkoeb") +horizontal_alignment = 1 + +[node name="ScrollContainer" type="ScrollContainer" parent="."] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="InternalMainContainer" type="VBoxContainer" parent="ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ScaleContainer" type="VBoxContainer" parent="ScrollContainer/InternalMainContainer"] +layout_mode = 2 +size_flags_horizontal = 4 + +[node name="Label" type="Label" parent="ScrollContainer/InternalMainContainer/ScaleContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +text = "Scale +" + +[node name="Scale" type="SpinBox" parent="ScrollContainer/InternalMainContainer/ScaleContainer"] +layout_mode = 2 +min_value = 1.0 +max_value = 2.0 +step = 0.01 +value = 1.0 +alignment = 1 + +[node name="SaveButton" type="Button" parent="."] +layout_mode = 2 +disabled = true +text = "Save +" + +[node name="BackButton" type="Button" parent="."] +layout_mode = 2 +size_flags_vertical = 8 +theme_override_fonts/font = ExtResource("3_ngmnl") +theme_override_font_sizes/font_size = 32 +text = "Back +" + +[connection signal="pressed" from="SaveButton" to="." method="OnSaveButtonPressed"] +[connection signal="pressed" from="BackButton" to="." method="OnBackButtonPressed"] diff --git a/Scenes/Score.tscn b/Scenes/UI/Score.tscn similarity index 52% rename from Scenes/Score.tscn rename to Scenes/UI/Score.tscn index ae48c00..ac98af8 100644 --- a/Scenes/Score.tscn +++ b/Scenes/UI/Score.tscn @@ -1,9 +1,7 @@ -[gd_scene load_steps=3 format=3 uid="uid://co2rv0nci8sbm"] +[gd_scene load_steps=3 format=3 uid="uid://clsmrwvyrt7av"] -[ext_resource type="Script" path="res://Scripts/Managers/ScoreManager.gd" id="1_bdtqr"] - -[sub_resource type="LabelSettings" id="LabelSettings_piogj"] -font_size = 64 +[ext_resource type="Script" path="res://Scripts/Managers/UI/Score.gd" id="1_ytifn"] +[ext_resource type="LabelSettings" uid="uid://dkyqusswobqb3" path="res://Resources/ScoreLabelSettings.tres" id="2_mh7dc"] [node name="Score" type="HFlowContainer"] z_index = 1 @@ -11,20 +9,20 @@ offset_left = -378.0 offset_top = -272.0 offset_right = 378.0 offset_bottom = 268.0 -script = ExtResource("1_bdtqr") +script = ExtResource("1_ytifn") [node name="Player1" type="Label" parent="."] layout_mode = 2 size_flags_horizontal = 3 text = "0 " -label_settings = SubResource("LabelSettings_piogj") +label_settings = ExtResource("2_mh7dc") vertical_alignment = 1 [node name="Player2" type="Label" parent="."] layout_mode = 2 size_flags_horizontal = 3 text = "0" -label_settings = SubResource("LabelSettings_piogj") +label_settings = ExtResource("2_mh7dc") horizontal_alignment = 2 vertical_alignment = 1 diff --git a/Scripts/Data/Settings.cs b/Scripts/Data/Settings.cs new file mode 100644 index 0000000..e613166 --- /dev/null +++ b/Scripts/Data/Settings.cs @@ -0,0 +1,20 @@ +using System; +using System.Text.Json.Serialization; + +namespace Pong.Scripts.Data; + +/// +/// a structure containing the data of the settings. +/// +public struct Settings +{ + /// + /// the scale of the game. this can be from 1.0 to 2.0 + /// + public double Scale; + + /// + /// the default values of settings. + /// + public static Settings Default => new() { Scale = 1.0 }; +} diff --git a/Scripts/Managers/MainMenuManager.cs b/Scripts/Managers/MainMenuManager.cs deleted file mode 100644 index 684302a..0000000 --- a/Scripts/Managers/MainMenuManager.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Godot; - -namespace Pong.Scripts.Managers; - -public partial class MainMenuManager : VBoxContainer -{ - // Called when the node enters the scene tree for the first time. - public override void _Ready() - { - GetTree().Root.SizeChanged += AdaptUiToScreenResolution; - AdaptUiToScreenResolution(); - } - - /// - /// code that executes when the start button is pressed. this loads the Pong scene to the root - /// of the scene tree. - /// - private void OnButtonPressed() - { - GetTree().Root.SizeChanged -= AdaptUiToScreenResolution; - GetTree().ChangeSceneToFile("res://Scenes/Pong.tscn"); - } - - private void AdaptUiToScreenResolution() - { - Size = DisplayServer.WindowGetSize(); - } -} diff --git a/Scripts/Managers/SceneManager.cs b/Scripts/Managers/PongSceneManager.cs similarity index 80% rename from Scripts/Managers/SceneManager.cs rename to Scripts/Managers/PongSceneManager.cs index 4565617..8169bcb 100644 --- a/Scripts/Managers/SceneManager.cs +++ b/Scripts/Managers/PongSceneManager.cs @@ -2,7 +2,7 @@ using Godot; namespace Pong.Scripts.Managers; -public partial class SceneManager : Node2D +public partial class PongSceneManager : Node2D { public override void _Process(double delta) { diff --git a/Scripts/Managers/SettingsManager.cs b/Scripts/Managers/SettingsManager.cs new file mode 100644 index 0000000..d6e533f --- /dev/null +++ b/Scripts/Managers/SettingsManager.cs @@ -0,0 +1,78 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Godot; +using Pong.Scripts.Data; + +namespace Pong.Scripts.Managers; + +public partial class SettingsManager : Node +{ + private const string SettingsPath = "user://settings.json"; + public Settings SettingsData = Settings.Default; + private static readonly JsonSerializerOptions SerializerOptions = new() { IncludeFields = true }; + public event Action SettingsChanged; + + public override async void _EnterTree() + { + await LoadSettings(); + } + + /// + /// load the settings data from the filesystem. + /// + private async Task LoadSettings() + { + try + { + if (!FileAccess.FileExists(SettingsPath)) + { + await SaveSettings(Settings.Default); + return; + } + + using var file = await OpenSettingsFile(FileAccess.ModeFlags.Read); + + SettingsData = + await Task.Run(() => JsonSerializer.Deserialize(file.GetAsText(), SerializerOptions)); + SettingsChanged?.Invoke(); + } + catch (Exception e) + { + PrintFileError(e); + } + } + + /// + /// open the settings file from the filesystem. + /// + /// a godot FileAccess object. + private static async Task OpenSettingsFile(FileAccess.ModeFlags flag) + { + return await Task.Run(() => FileAccess.Open(SettingsPath, flag)); + } + + internal async Task SaveSettings(Settings settings) + { + try + { + using var file = await OpenSettingsFile(FileAccess.ModeFlags.WriteRead); + var json = await Task.Run(() => JsonSerializer.Serialize(settings, SerializerOptions)); + await Task.Run(() => file.StoreBuffer(json.ToUtf8Buffer())); + SettingsData = settings; + SettingsChanged?.Invoke(); + return true; + } + catch (Exception e) + { + PrintFileError(e); + return false; + } + } + + private static void PrintFileError(Exception e) + { + GD.PrintErr($"a {e.GetType()} has occured while trying to load the settings file: {e}."); + GD.PrintErr("default settings will be used."); + } +} diff --git a/Scripts/Managers/UI/BaseMenu.cs b/Scripts/Managers/UI/BaseMenu.cs new file mode 100644 index 0000000..0639d3a --- /dev/null +++ b/Scripts/Managers/UI/BaseMenu.cs @@ -0,0 +1,43 @@ +using Godot; + +namespace Pong.Scripts.Managers.UI; + +public partial class BaseMenu : VBoxContainer +{ + private SettingsManager _settingsManager; + + public override void _EnterTree() + { + _settingsManager = GetNode("/root/SettingsManager"); + _settingsManager.SettingsChanged += AdaptUiToGameResolution; + GetTree().Root.SizeChanged += AdaptUiToGameResolution; + AdaptUiToGameResolution(); + + EnterTree(); + } + + public override void _ExitTree() + { + GetTree().Root.SizeChanged -= AdaptUiToGameResolution; + _settingsManager.SettingsChanged -= AdaptUiToGameResolution; + ExitTree(); + } + + protected virtual void EnterTree() + { + } + + protected virtual void ExitTree() + { + } + + /// + /// change the size of the UI container to the current resolution of the game. + /// + protected void AdaptUiToGameResolution() + { + var scale = _settingsManager.SettingsData.Scale; + Size = (Vector2)DisplayServer.WindowGetSize() / scale; + Scale = new Vector2(scale, scale); + } +} diff --git a/Scripts/Managers/UI/Menus/MainMenu.cs b/Scripts/Managers/UI/Menus/MainMenu.cs new file mode 100644 index 0000000..b17be61 --- /dev/null +++ b/Scripts/Managers/UI/Menus/MainMenu.cs @@ -0,0 +1,20 @@ +using Godot; + +namespace Pong.Scripts.Managers.UI.Menus; + +public partial class MainMenu : BaseMenu +{ + /// + /// code that executes when the start button is pressed. this loads the Pong scene to the root + /// of the scene tree. + /// + private void OnPlayButtonPressed() + { + GetTree().ChangeSceneToFile("res://Scenes/Pong.tscn"); + } + + private void OnSettingsButtonPressed() + { + GetTree().ChangeSceneToFile("res://Scenes/UI/Menus/Settings.tscn"); + } +} diff --git a/Scripts/Managers/UI/Menus/Settings.cs b/Scripts/Managers/UI/Menus/Settings.cs new file mode 100644 index 0000000..98cb070 --- /dev/null +++ b/Scripts/Managers/UI/Menus/Settings.cs @@ -0,0 +1,52 @@ +using Godot; + +namespace Pong.Scripts.Managers.UI.Menus; + +public partial class Settings : BaseMenu +{ + private SettingsManager _settings; + private SpinBox _scale; + private Button _saveButton; + + private bool _scaleModified; + + protected override void EnterTree() + { + GetNodes(); + + _scale.Value = _settings.SettingsData.Scale; + } + + private void OnBackButtonPressed() + { + GetTree().ChangeSceneToFile("res://Scenes/UI/Menus/MainMenu.tscn"); + } + + private void GetNodes() + { + _settings = GetNode("/root/SettingsManager"); + _scale = GetNode("ScrollContainer/InternalMainContainer/ScaleContainer/Scale"); + _saveButton = GetNode