diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e0603cd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "GodotPlugin/godot-cpp"] + path = GodotPlugin/godot-cpp + url = https://github.com/godotengine/godot-cpp.git diff --git a/Documentation/API/Common/InverseLerp.md b/Documentation/API/Common/InverseLerp.md index 0bfeffc..638d53e 100644 --- a/Documentation/API/Common/InverseLerp.md +++ b/Documentation/API/Common/InverseLerp.md @@ -1,6 +1,6 @@ #### `Common.InverseLerp` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -31,3 +31,14 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var value = rhythm_game_utilities.inverse_lerp(0, 10, 5) + + print(value) # 0.5 +``` diff --git a/Documentation/API/Common/Lerp.md b/Documentation/API/Common/Lerp.md index 08785a0..6d001f7 100644 --- a/Documentation/API/Common/Lerp.md +++ b/Documentation/API/Common/Lerp.md @@ -1,6 +1,6 @@ #### `Common.Lerp` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -31,3 +31,14 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var value = rhythm_game_utilities.lerp(0, 10, 0.5) + + print(value) # 5 +``` diff --git a/Documentation/API/Parsers/ParseBpmFromChartSection.md b/Documentation/API/Parsers/ParseBpmFromChartSection.md index 160db28..cdb6249 100644 --- a/Documentation/API/Parsers/ParseBpmFromChartSection.md +++ b/Documentation/API/Parsers/ParseBpmFromChartSection.md @@ -1,6 +1,6 @@ #### `Parsers.ParseBpmFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -39,3 +39,19 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var bpm = rhythm_game_utilities.parse_bpm_from_chart_section(sections["SyncTrack"]) + + print(bpm) +``` diff --git a/Documentation/API/Parsers/ParseLyricsFromChartSection.md b/Documentation/API/Parsers/ParseLyricsFromChartSection.md index 31e8746..f47a744 100644 --- a/Documentation/API/Parsers/ParseLyricsFromChartSection.md +++ b/Documentation/API/Parsers/ParseLyricsFromChartSection.md @@ -1,6 +1,6 @@ #### `Parsers.ParseLyricsFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -39,3 +39,19 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var lyrics = rhythm_game_utilities.parse_lyrics_from_chart_section(sections["Events"]) + + print(lyrics) +``` diff --git a/Documentation/API/Parsers/ParseMetaDataFromChartSection.md b/Documentation/API/Parsers/ParseMetaDataFromChartSection.md index 52470c7..b2cad17 100644 --- a/Documentation/API/Parsers/ParseMetaDataFromChartSection.md +++ b/Documentation/API/Parsers/ParseMetaDataFromChartSection.md @@ -1,6 +1,6 @@ #### `Parsers.ParseMetaDataFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -43,3 +43,19 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var meta_data = rhythm_game_utilities.parse_meta_data_from_chart_section(sections["Song"]) + + print(meta_data) +``` diff --git a/Documentation/API/Parsers/ParseNotesFromChartSection.md b/Documentation/API/Parsers/ParseNotesFromChartSection.md index 7988351..35f6020 100644 --- a/Documentation/API/Parsers/ParseNotesFromChartSection.md +++ b/Documentation/API/Parsers/ParseNotesFromChartSection.md @@ -1,6 +1,6 @@ #### `Parsers.ParseNotesFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -47,3 +47,19 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var notes = rhythm_game_utilities.parse_notes_from_chart_section(sections["ExpertSingle"]) + + print(notes) +``` diff --git a/Documentation/API/Parsers/ParseSectionsFromChart.md b/Documentation/API/Parsers/ParseSectionsFromChart.md index e51ea16..8705ad3 100644 --- a/Documentation/API/Parsers/ParseSectionsFromChart.md +++ b/Documentation/API/Parsers/ParseSectionsFromChart.md @@ -1,6 +1,6 @@ #### `Parsers.ParseSectionsFromChart` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -34,3 +34,17 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + print(sections) +``` diff --git a/Documentation/API/Parsers/ParseTimeSignaturesFromChartSection.md b/Documentation/API/Parsers/ParseTimeSignaturesFromChartSection.md index b91ef84..936caba 100644 --- a/Documentation/API/Parsers/ParseTimeSignaturesFromChartSection.md +++ b/Documentation/API/Parsers/ParseTimeSignaturesFromChartSection.md @@ -1,6 +1,6 @@ #### `Parsers.ParseTimeSignaturesFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -39,3 +39,19 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var time_signatures = rhythm_game_utilities.parse_time_signatures_from_chart_section(sections["SyncTrack"]) + + print(time_signatures) +``` diff --git a/Documentation/API/Utilities/CalculateAccuracyRatio.md b/Documentation/API/Utilities/CalculateAccuracyRatio.md index 5ddbe56..be5cdce 100644 --- a/Documentation/API/Utilities/CalculateAccuracyRatio.md +++ b/Documentation/API/Utilities/CalculateAccuracyRatio.md @@ -1,6 +1,6 @@ #### `Utilities.CalculateAccuracyRatio` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -51,3 +51,22 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var seconds = 2 + var resolution = 192 + var position_delta = 50 + + var bpm_changes = { 0: 120000 } + + var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + + var value = rhythm_game_utilities.calculate_accuracy_ratio(750, current_position, position_delta) + + print(round(value * 100) / 100.0) # 0.64 +``` diff --git a/Documentation/API/Utilities/CalculateBeatBars.md b/Documentation/API/Utilities/CalculateBeatBars.md index 0553abe..a3da1d4 100644 --- a/Documentation/API/Utilities/CalculateBeatBars.md +++ b/Documentation/API/Utilities/CalculateBeatBars.md @@ -1,6 +1,6 @@ #### `Utilities.CalculateBeatBars` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -50,3 +50,23 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var resolution = 192 + var time_signature = 4 + + var bpm_changes = { + 0: 88000, 3840: 112000, 9984: 89600, + 22272: 112000, 33792: 111500, 34560: 112000, + 42240: 111980 + } + + var beat_bars = rhythm_game_utilities.calculate_beat_bars(bpm_changes, resolution, time_signature, true) + + print(beat_bars) +``` diff --git a/Documentation/API/Utilities/ConvertSecondsToTicks.md b/Documentation/API/Utilities/ConvertSecondsToTicks.md index 188e835..3252be4 100644 --- a/Documentation/API/Utilities/ConvertSecondsToTicks.md +++ b/Documentation/API/Utilities/ConvertSecondsToTicks.md @@ -1,6 +1,6 @@ #### `Utilities.ConvertSecondsToTicks` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -52,3 +52,23 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var seconds = 5 + var resolution = 192 + + var bpm_changes = { + 0: 88000, 3840: 112000, 9984: 89600, + 22272: 112000, 33792: 111500, 34560: 112000, + 42240: 111980 + } + + var ticks = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + + print(ticks) # 1408 +``` diff --git a/Documentation/API/Utilities/ConvertTickToPosition.md b/Documentation/API/Utilities/ConvertTickToPosition.md index df36ec9..d29a6cd 100644 --- a/Documentation/API/Utilities/ConvertTickToPosition.md +++ b/Documentation/API/Utilities/ConvertTickToPosition.md @@ -1,6 +1,6 @@ #### `Utilities.ConvertTickToPosition` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -37,3 +37,17 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var tick = 2784 + var resolution = 192 + + var position = rhythm_game_utilities.convert_tick_to_position(tick, resolution) + + print(position) # 14.5 +``` diff --git a/Documentation/API/Utilities/IsOnTheBeat.md b/Documentation/API/Utilities/IsOnTheBeat.md index 005a42f..41bf93d 100644 --- a/Documentation/API/Utilities/IsOnTheBeat.md +++ b/Documentation/API/Utilities/IsOnTheBeat.md @@ -1,6 +1,6 @@ #### `Utilities.IsOnTheBeat` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -41,3 +41,17 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var bpm = 120 + var current_time = 10 + var delta = 0.05 + + if rhythm_game_utilities.is_on_the_beat(bpm, current_time, delta): + print("Is on the beat!") +``` diff --git a/Documentation/API/Utilities/RoundUpToTheNearestMultiplier.md b/Documentation/API/Utilities/RoundUpToTheNearestMultiplier.md index a6a1f58..877fe49 100644 --- a/Documentation/API/Utilities/RoundUpToTheNearestMultiplier.md +++ b/Documentation/API/Utilities/RoundUpToTheNearestMultiplier.md @@ -1,6 +1,6 @@ #### `Utilities.RoundUpToTheNearestMultiplier` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -31,3 +31,14 @@ int main() return 0; } ``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var value = rhythm_game_utilities.round_up_to_the_nearest_multiplier(12, 10) + + print(value) # 20 +``` diff --git a/GodotPlugin/.gitignore b/GodotPlugin/.gitignore new file mode 100644 index 0000000..1e7ad28 --- /dev/null +++ b/GodotPlugin/.gitignore @@ -0,0 +1,4 @@ +addons/ + +*.os +*.dblite diff --git a/GodotPlugin/Makefile b/GodotPlugin/Makefile new file mode 100644 index 0000000..eaa45a0 --- /dev/null +++ b/GodotPlugin/Makefile @@ -0,0 +1,16 @@ +help: + @fgrep -h "##" $(MAKEFILE_LIST) | sed -e 's/##//' | tail -n +2 + +build-debug: ## Build debug Godot plugin + scons platform=macos arch=universal target=template_debug + scons platform=windows arch=x86_32 target=template_debug + scons platform=windows arch=x86_64 target=template_debug + +build-release: ## Build release Godot plugin + scons platform=macos arch=universal target=template_release + scons platform=windows arch=x86_32 target=template_release + scons platform=windows arch=x86_64 target=template_release + +install-dependencies: ## Install Dependencies + brew install scons + brew install mingw-w64 diff --git a/GodotPlugin/RhythmGameUtilities.gdextension b/GodotPlugin/RhythmGameUtilities.gdextension new file mode 100644 index 0000000..90522ce --- /dev/null +++ b/GodotPlugin/RhythmGameUtilities.gdextension @@ -0,0 +1,14 @@ +[configuration] + +entry_symbol = "rhythm_game_utilities_plugin" +compatibility_minimum = 4.3 +reloadable = true + +[libraries] + +macos.debug = "res://addons/RhythmGameUtilities/libRhythmGameUtilities.macos.template_debug.framework/libRhythmGameUtilities.macos.template_debug" +macos.release = "res://addons/RhythmGameUtilities/libRhythmGameUtilities.macos.template_release.framework/libRhythmGameUtilities.macos.template_release" +windows.debug.x86_32 = "res://addons/RhythmGameUtilities/libRhythmGameUtilities.windows.template_debug.x86_32.dll" +windows.release.x86_32 = "res://addons/RhythmGameUtilities/libRhythmGameUtilities.windows.template_release.x86_32.dll" +windows.debug.x86_64 = "res://addons/RhythmGameUtilities/libRhythmGameUtilities.windows.template_debug.x86_64.dll" +windows.release.x86_64 = "res://addons/RhythmGameUtilities/libRhythmGameUtilities.windows.template_release.x86_64.dll" diff --git a/GodotPlugin/SConstruct.py b/GodotPlugin/SConstruct.py new file mode 100644 index 0000000..f88d54a --- /dev/null +++ b/GodotPlugin/SConstruct.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +import os +import sys + +env = SConscript("godot-cpp/SConstruct") + +env.Append(CPPPATH=["include/", "../include"]) +sources = Glob("include/*.cpp") + +if env["platform"] == "macos": + file_name = "libRhythmGameUtilities.{}.{}".format(env["platform"], env["target"]) + + library = env.SharedLibrary( + "build/addons/RhythmGameUtilities/{}.framework/{}".format(file_name, file_name), + source=sources + ) +else: + library = env.SharedLibrary( + "build/addons/RhythmGameUtilities/libRhythmGameUtilities{}{}" + .format(env["suffix"], env["SHLIBSUFFIX"]), + source=sources, + ) + +gdextension_copy = env.Command( + target="build/addons/RhythmGameUtilities/RhythmGameUtilities.gdextension", + source="RhythmGameUtilities.gdextension", + action=Copy("$TARGET", "$SOURCE") +) + +env.Depends(gdextension_copy, library) + +Default(library) + +Default(gdextension_copy) diff --git a/GodotPlugin/godot-cpp b/GodotPlugin/godot-cpp new file mode 160000 index 0000000..fbbf9ec --- /dev/null +++ b/GodotPlugin/godot-cpp @@ -0,0 +1 @@ +Subproject commit fbbf9ec4efd8f1055d00edb8d926eef8ba4c2cce diff --git a/GodotPlugin/include/register_types.cpp b/GodotPlugin/include/register_types.cpp new file mode 100644 index 0000000..78ef967 --- /dev/null +++ b/GodotPlugin/include/register_types.cpp @@ -0,0 +1,45 @@ +#include "register_types.h" + +#include "rhythm_game_utilities.h" + +#include +#include +#include +#include + +using namespace godot; + +void initialize_rhythm_game_utilities(ModuleInitializationLevel p_level) +{ + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) + { + return; + } + + ClassDB::register_class(true); +} + +void terminate_rhythm_game_utilities(ModuleInitializationLevel p_level) +{ + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) + { + return; + } +} + +extern "C" +{ + GDExtensionBool GDE_EXPORT rhythm_game_utilities_plugin( + GDExtensionInterfaceGetProcAddress p_get_proc_address, + const GDExtensionClassLibraryPtr p_library, + GDExtensionInitialization *r_initialization) + { + godot::GDExtensionBinding::InitObject init_obj( + p_get_proc_address, p_library, r_initialization); + + init_obj.register_initializer(initialize_rhythm_game_utilities); + init_obj.register_terminator(terminate_rhythm_game_utilities); + + return init_obj.init(); + } +} diff --git a/GodotPlugin/include/register_types.h b/GodotPlugin/include/register_types.h new file mode 100644 index 0000000..bffff7b --- /dev/null +++ b/GodotPlugin/include/register_types.h @@ -0,0 +1,4 @@ +#pragma once + +void initialize_rhythm_game_utilities(); +void terminate_rhythm_game_utilities(); diff --git a/GodotPlugin/include/rhythm_game_utilities.cpp b/GodotPlugin/include/rhythm_game_utilities.cpp new file mode 100644 index 0000000..fa93695 --- /dev/null +++ b/GodotPlugin/include/rhythm_game_utilities.cpp @@ -0,0 +1,286 @@ +#include "rhythm_game_utilities.h" + +#include "utilities.hpp" + +#include +#include +#include + +void rhythm_game_utilities::_bind_methods() +{ + // Common + + ClassDB::bind_static_method("rhythm_game_utilities", + D_METHOD("lerp", "a", "b", "t"), + &rhythm_game_utilities::lerp); + + ClassDB::bind_static_method("rhythm_game_utilities", + D_METHOD("inverse_lerp", "a", "b", "v"), + &rhythm_game_utilities::inverse_lerp); + + // Parsers + + ClassDB::bind_static_method( + "rhythm_game_utilities", + D_METHOD("parse_sections_from_chart", "contents"), + &rhythm_game_utilities::parse_sections_from_chart); + + ClassDB::bind_static_method( + "rhythm_game_utilities", + D_METHOD("parse_bpm_from_chart_section", "section"), + &rhythm_game_utilities::parse_bpm_from_chart_section); + + ClassDB::bind_static_method( + "rhythm_game_utilities", + D_METHOD("parse_lyrics_from_chart_section", "section"), + &rhythm_game_utilities::parse_lyrics_from_chart_section); + + ClassDB::bind_static_method( + "rhythm_game_utilities", + D_METHOD("parse_meta_data_from_chart_section", "section"), + &rhythm_game_utilities::parse_meta_data_from_chart_section); + + ClassDB::bind_static_method( + "rhythm_game_utilities", + D_METHOD("parse_notes_from_chart_section", "section"), + &rhythm_game_utilities::parse_notes_from_chart_section); + + ClassDB::bind_static_method( + "rhythm_game_utilities", + D_METHOD("parse_time_signatures_from_chart_section", "section"), + &rhythm_game_utilities::parse_time_signatures_from_chart_section); + + // Utilities + + ClassDB::bind_static_method( + "rhythm_game_utilities", + D_METHOD("convert_seconds_to_ticks", "seconds", "resolution", + "bpm_changes"), + &rhythm_game_utilities::convert_seconds_to_ticks); + + ClassDB::bind_static_method( + "rhythm_game_utilities", + D_METHOD("convert_tick_to_position", "tick", "resolution"), + &rhythm_game_utilities::convert_tick_to_position); + + ClassDB::bind_static_method( + "rhythm_game_utilities", + D_METHOD("is_on_the_beat", "bpm", "current_time", "delta"), + &rhythm_game_utilities::is_on_the_beat); + + ClassDB::bind_static_method( + "rhythm_game_utilities", + D_METHOD("round_up_to_the_nearest_multiplier", "value", "multiplier"), + &rhythm_game_utilities::round_up_to_the_nearest_multiplier); + + ClassDB::bind_static_method( + "rhythm_game_utilities", + D_METHOD("calculate_accuracy_ratio", "position", "current_position", + "delta"), + &rhythm_game_utilities::calculate_accuracy_ratio); + + ClassDB::bind_static_method("rhythm_game_utilities", + D_METHOD("calculate_beat_bars", "bpm_changes", + "resolution", "ts", + "include_half_notes"), + &rhythm_game_utilities::calculate_beat_bars); +} + +// Common + +float rhythm_game_utilities::lerp(float a, float b, float t) +{ + return RhythmGameUtilities::Lerp(a, b, t); +} + +float rhythm_game_utilities::inverse_lerp(float a, float b, float v) +{ + return RhythmGameUtilities::InverseLerp(a, b, v); +} + +// Parsers + +Dictionary rhythm_game_utilities::parse_sections_from_chart(String contents) +{ + Dictionary sections; + + auto sections_internal = + RhythmGameUtilities::ParseSectionsFromChart(contents.utf8().get_data()); + + for (auto section_internal = sections_internal.begin(); + section_internal != sections_internal.end(); section_internal++) + { + auto section_key = godot::String(section_internal->first.c_str()); + + Array section_items; + + for (auto i = 0; i < section_internal->second.size(); i += 1) + { + Dictionary section_item; + + auto temp = section_internal->second[i]; + + auto key = godot::Variant(temp.first.c_str()); + + Array values; + + for (auto j = 0; j < temp.second.size(); j += 1) + { + values.append(godot::Variant(temp.second[j].c_str())); + } + + section_item[key] = values; + + section_items.append(section_item); + } + + sections[section_key] = section_items; + } + + return sections; +} + +Dictionary rhythm_game_utilities::parse_bpm_from_chart_section(Array section) +{ + auto bpm_internal = RhythmGameUtilities::ParseBpmFromChartSection( + convert_section_to_section_internal(section)); + + Dictionary bpm; + + for (auto const &[key, val] : bpm_internal) + { + bpm[key] = bpm_internal[key]; + } + + return bpm; +} + +Dictionary rhythm_game_utilities::parse_lyrics_from_chart_section(Array section) +{ + auto lyrics_internal = RhythmGameUtilities::ParseLyricsFromChartSection( + convert_section_to_section_internal(section)); + + Dictionary lyrics; + + for (auto const &[key, val] : lyrics_internal) + { + lyrics[key] = godot::String(lyrics_internal[key].c_str()); + } + + return lyrics; +} + +Dictionary +rhythm_game_utilities::parse_meta_data_from_chart_section(Array section) +{ + auto meta_data_internal = + RhythmGameUtilities::ParseMetaDataFromChartSection( + convert_section_to_section_internal(section)); + + Dictionary meta_data; + + for (auto const &[key, val] : meta_data_internal) + { + meta_data[godot::String(key.c_str())] = + godot::String(meta_data_internal[key].c_str()); + } + + return meta_data; +} + +Array rhythm_game_utilities::parse_notes_from_chart_section(Array section) +{ + auto notes_internal = RhythmGameUtilities::ParseNotesFromChartSection( + convert_section_to_section_internal(section)); + + Array notes; + + for (auto ¬e_internal : notes_internal) + { + Dictionary note; + + note["hand_position"] = note_internal.HandPosition; + note["length"] = note_internal.Length; + note["position"] = note_internal.Position; + + notes.append(note); + } + + return notes; +} + +Dictionary +rhythm_game_utilities::parse_time_signatures_from_chart_section(Array section) +{ + auto time_signatures_internal = + RhythmGameUtilities::ParseTimeSignaturesFromChartSection( + convert_section_to_section_internal(section)); + + Dictionary time_signatures; + + for (auto const &[key, val] : time_signatures_internal) + { + time_signatures[key] = time_signatures_internal[key]; + } + + return time_signatures; +} + +// Utilities + +int rhythm_game_utilities::convert_seconds_to_ticks(float seconds, + int resolution, + Dictionary bpm_changes) +{ + return RhythmGameUtilities::ConvertSecondsToTicks( + seconds, resolution, convert_dictionary_to_map(bpm_changes)); +} + +float rhythm_game_utilities::convert_tick_to_position(int tick, int resolution) +{ + return RhythmGameUtilities::ConvertTickToPosition(tick, resolution); +} + +bool rhythm_game_utilities::is_on_the_beat(int bpm, float current_time, + float delta) +{ + return RhythmGameUtilities::IsOnTheBeat(bpm, current_time, delta); +} + +int rhythm_game_utilities::round_up_to_the_nearest_multiplier(int value, + int multiplier) +{ + return RhythmGameUtilities::RoundUpToTheNearestMultiplier(value, + multiplier); +} + +float rhythm_game_utilities::calculate_accuracy_ratio(int position, + int current_position, + int delta) +{ + return RhythmGameUtilities::CalculateAccuracyRatio(position, + current_position, delta); +} + +Array rhythm_game_utilities::calculate_beat_bars(Dictionary bpm_changes, + int resolution, int ts, + bool include_half_notes) +{ + auto beat_bars = RhythmGameUtilities::CalculateBeatBars( + convert_dictionary_to_map(bpm_changes), resolution, ts, + include_half_notes); + + Array beat_bars_dictionary_array; + + for (auto &beat_bar : beat_bars) + { + Dictionary beat_bar_dictionary; + + beat_bar_dictionary["bpm"] = beat_bar.BPM; + beat_bar_dictionary["position"] = beat_bar.Position; + + beat_bars_dictionary_array.append(beat_bar_dictionary); + } + + return beat_bars_dictionary_array; +} diff --git a/GodotPlugin/include/rhythm_game_utilities.h b/GodotPlugin/include/rhythm_game_utilities.h new file mode 100644 index 0000000..3901c1e --- /dev/null +++ b/GodotPlugin/include/rhythm_game_utilities.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include + +using namespace godot; + +class rhythm_game_utilities : public Object +{ + GDCLASS(rhythm_game_utilities, Object) + + protected: + static void _bind_methods(); + + public: + // Common + + static float lerp(float a, float b, float t); + + static float inverse_lerp(float a, float b, float v); + + // Parsers + + static Dictionary parse_sections_from_chart(String contents); + + static Dictionary parse_bpm_from_chart_section(Array section); + + static Dictionary parse_lyrics_from_chart_section(Array section); + + static Dictionary parse_meta_data_from_chart_section(Array section); + + static Array parse_notes_from_chart_section(Array section); + + static Dictionary parse_time_signatures_from_chart_section(Array section); + + // Utilities + + static int convert_seconds_to_ticks(float seconds, int resolution, + Dictionary bpm_changes); + + static float convert_tick_to_position(int tick, int resolution); + + static bool is_on_the_beat(int bpm, float current_time, + float delta = 0.05f); + + static int round_up_to_the_nearest_multiplier(int value, int multiplier); + + static float calculate_accuracy_ratio(int position, int current_position, + int delta = 50); + + static Array calculate_beat_bars(Dictionary bpm_changes, int resolution, + int ts, bool include_half_notes); +}; diff --git a/GodotPlugin/include/utilities.hpp b/GodotPlugin/include/utilities.hpp new file mode 100644 index 0000000..d088064 --- /dev/null +++ b/GodotPlugin/include/utilities.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include + +#include + +using namespace godot; + +template +std::map convert_dictionary_to_map(Dictionary input) +{ + std::map output; + + auto keys = input.keys(); + + for (auto i = 0; i < keys.size(); i += 1) + { + auto key = keys[i]; + output[key] = input[key]; + } + + return output; +} + +std::vector>> +convert_section_to_section_internal(Array section) +{ + std::vector>> + section_internal; + + for (auto i = 0; i < section.size(); i += 1) + { + if (section[i].get_type() == Variant::DICTIONARY) + { + Dictionary variant = section[i]; + + Array keys = variant.keys(); + + for (auto j = 0; j < keys.size(); j += 1) + { + String key = keys[j]; + Array values = variant[key]; + + std::vector values_internal; + + for (auto k = 0; k < values.size(); k += 1) + { + if (values[k].get_type() == Variant::Type::STRING) + { + String value = values[k]; + + values_internal.push_back(value.utf8().get_data()); + } + } + + section_internal.push_back( + std::make_pair(key.utf8().get_data(), values_internal)); + } + } + } + + return section_internal; +} diff --git a/README.md b/README.md index c9a7a52..3601337 100644 --- a/README.md +++ b/README.md @@ -231,7 +231,7 @@ _texture2D.Apply(); #### `Common.InverseLerp` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -263,9 +263,20 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var value = rhythm_game_utilities.inverse_lerp(0, 10, 5) + + print(value) # 0.5 +``` + #### `Common.Lerp` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -297,13 +308,24 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var value = rhythm_game_utilities.lerp(0, 10, 0.5) + + print(value) # 5 +``` + ### `Parsers` Read more about `.chart` files: #### `Parsers.ParseBpmFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -343,9 +365,25 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var bpm = rhythm_game_utilities.parse_bpm_from_chart_section(sections["SyncTrack"]) + + print(bpm) +``` + #### `Parsers.ParseLyricsFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -385,9 +423,25 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var lyrics = rhythm_game_utilities.parse_lyrics_from_chart_section(sections["Events"]) + + print(lyrics) +``` + #### `Parsers.ParseMetaDataFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -431,9 +485,25 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var meta_data = rhythm_game_utilities.parse_meta_data_from_chart_section(sections["Song"]) + + print(meta_data) +``` + #### `Parsers.ParseNotesFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -481,9 +551,25 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var notes = rhythm_game_utilities.parse_notes_from_chart_section(sections["ExpertSingle"]) + + print(notes) +``` + #### `Parsers.ParseSectionsFromChart` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -518,9 +604,23 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + print(sections) +``` + #### `Parsers.ParseTimeSignaturesFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -560,11 +660,27 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var time_signatures = rhythm_game_utilities.parse_time_signatures_from_chart_section(sections["SyncTrack"]) + + print(time_signatures) +``` + ### Utilities #### `Utilities.CalculateAccuracyRatio` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -616,9 +732,28 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var seconds = 2 + var resolution = 192 + var position_delta = 50 + + var bpm_changes = { 0: 120000 } + + var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + + var value = rhythm_game_utilities.calculate_accuracy_ratio(750, current_position, position_delta) + + print(round(value * 100) / 100.0) # 0.64 +``` + #### `Utilities.CalculateBeatBars` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -669,9 +804,29 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var resolution = 192 + var time_signature = 4 + + var bpm_changes = { + 0: 88000, 3840: 112000, 9984: 89600, + 22272: 112000, 33792: 111500, 34560: 112000, + 42240: 111980 + } + + var beat_bars = rhythm_game_utilities.calculate_beat_bars(bpm_changes, resolution, time_signature, true) + + print(beat_bars) +``` + #### `Utilities.ConvertSecondsToTicks` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -724,9 +879,29 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var seconds = 5 + var resolution = 192 + + var bpm_changes = { + 0: 88000, 3840: 112000, 9984: 89600, + 22272: 112000, 33792: 111500, 34560: 112000, + 42240: 111980 + } + + var ticks = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + + print(ticks) # 1408 +``` + #### `Utilities.ConvertTickToPosition` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -764,9 +939,23 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var tick = 2784 + var resolution = 192 + + var position = rhythm_game_utilities.convert_tick_to_position(tick, resolution) + + print(position) # 14.5 +``` + #### `Utilities.IsOnTheBeat` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -808,9 +997,23 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var bpm = 120 + var current_time = 10 + var delta = 0.05 + + if rhythm_game_utilities.is_on_the_beat(bpm, current_time, delta): + print("Is on the beat!") +``` + #### `Utilities.RoundUpToTheNearestMultiplier` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -842,6 +1045,17 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var value = rhythm_game_utilities.round_up_to_the_nearest_multiplier(12, 10) + + print(value) # 20 +``` + ## Architecture The current architecture for this project looks like this: diff --git a/RhythmGameUtilities/README.md b/RhythmGameUtilities/README.md index c9a7a52..3601337 100644 --- a/RhythmGameUtilities/README.md +++ b/RhythmGameUtilities/README.md @@ -231,7 +231,7 @@ _texture2D.Apply(); #### `Common.InverseLerp` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -263,9 +263,20 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var value = rhythm_game_utilities.inverse_lerp(0, 10, 5) + + print(value) # 0.5 +``` + #### `Common.Lerp` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -297,13 +308,24 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var value = rhythm_game_utilities.lerp(0, 10, 0.5) + + print(value) # 5 +``` + ### `Parsers` Read more about `.chart` files: #### `Parsers.ParseBpmFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -343,9 +365,25 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var bpm = rhythm_game_utilities.parse_bpm_from_chart_section(sections["SyncTrack"]) + + print(bpm) +``` + #### `Parsers.ParseLyricsFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -385,9 +423,25 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var lyrics = rhythm_game_utilities.parse_lyrics_from_chart_section(sections["Events"]) + + print(lyrics) +``` + #### `Parsers.ParseMetaDataFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -431,9 +485,25 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var meta_data = rhythm_game_utilities.parse_meta_data_from_chart_section(sections["Song"]) + + print(meta_data) +``` + #### `Parsers.ParseNotesFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -481,9 +551,25 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var notes = rhythm_game_utilities.parse_notes_from_chart_section(sections["ExpertSingle"]) + + print(notes) +``` + #### `Parsers.ParseSectionsFromChart` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -518,9 +604,23 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + print(sections) +``` + #### `Parsers.ParseTimeSignaturesFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -560,11 +660,27 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var time_signatures = rhythm_game_utilities.parse_time_signatures_from_chart_section(sections["SyncTrack"]) + + print(time_signatures) +``` + ### Utilities #### `Utilities.CalculateAccuracyRatio` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -616,9 +732,28 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var seconds = 2 + var resolution = 192 + var position_delta = 50 + + var bpm_changes = { 0: 120000 } + + var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + + var value = rhythm_game_utilities.calculate_accuracy_ratio(750, current_position, position_delta) + + print(round(value * 100) / 100.0) # 0.64 +``` + #### `Utilities.CalculateBeatBars` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -669,9 +804,29 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var resolution = 192 + var time_signature = 4 + + var bpm_changes = { + 0: 88000, 3840: 112000, 9984: 89600, + 22272: 112000, 33792: 111500, 34560: 112000, + 42240: 111980 + } + + var beat_bars = rhythm_game_utilities.calculate_beat_bars(bpm_changes, resolution, time_signature, true) + + print(beat_bars) +``` + #### `Utilities.ConvertSecondsToTicks` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -724,9 +879,29 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var seconds = 5 + var resolution = 192 + + var bpm_changes = { + 0: 88000, 3840: 112000, 9984: 89600, + 22272: 112000, 33792: 111500, 34560: 112000, + 42240: 111980 + } + + var ticks = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + + print(ticks) # 1408 +``` + #### `Utilities.ConvertTickToPosition` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -764,9 +939,23 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var tick = 2784 + var resolution = 192 + + var position = rhythm_game_utilities.convert_tick_to_position(tick, resolution) + + print(position) # 14.5 +``` + #### `Utilities.IsOnTheBeat` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -808,9 +997,23 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var bpm = 120 + var current_time = 10 + var delta = 0.05 + + if rhythm_game_utilities.is_on_the_beat(bpm, current_time, delta): + print("Is on the beat!") +``` + #### `Utilities.RoundUpToTheNearestMultiplier` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -842,6 +1045,17 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var value = rhythm_game_utilities.round_up_to_the_nearest_multiplier(12, 10) + + print(value) # 20 +``` + ## Architecture The current architecture for this project looks like this: diff --git a/UnityPackage/README.md b/UnityPackage/README.md index c9a7a52..3601337 100644 --- a/UnityPackage/README.md +++ b/UnityPackage/README.md @@ -231,7 +231,7 @@ _texture2D.Apply(); #### `Common.InverseLerp` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -263,9 +263,20 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var value = rhythm_game_utilities.inverse_lerp(0, 10, 5) + + print(value) # 0.5 +``` + #### `Common.Lerp` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -297,13 +308,24 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var value = rhythm_game_utilities.lerp(0, 10, 0.5) + + print(value) # 5 +``` + ### `Parsers` Read more about `.chart` files: #### `Parsers.ParseBpmFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -343,9 +365,25 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var bpm = rhythm_game_utilities.parse_bpm_from_chart_section(sections["SyncTrack"]) + + print(bpm) +``` + #### `Parsers.ParseLyricsFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -385,9 +423,25 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var lyrics = rhythm_game_utilities.parse_lyrics_from_chart_section(sections["Events"]) + + print(lyrics) +``` + #### `Parsers.ParseMetaDataFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -431,9 +485,25 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var meta_data = rhythm_game_utilities.parse_meta_data_from_chart_section(sections["Song"]) + + print(meta_data) +``` + #### `Parsers.ParseNotesFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -481,9 +551,25 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var notes = rhythm_game_utilities.parse_notes_from_chart_section(sections["ExpertSingle"]) + + print(notes) +``` + #### `Parsers.ParseSectionsFromChart` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -518,9 +604,23 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + print(sections) +``` + #### `Parsers.ParseTimeSignaturesFromChartSection` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -560,11 +660,27 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var file = FileAccess.open("res://song.txt", FileAccess.READ) + var content = file.get_as_text() + + var sections = rhythm_game_utilities.parse_sections_from_chart(content) + + var time_signatures = rhythm_game_utilities.parse_time_signatures_from_chart_section(sections["SyncTrack"]) + + print(time_signatures) +``` + ### Utilities #### `Utilities.CalculateAccuracyRatio` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -616,9 +732,28 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var seconds = 2 + var resolution = 192 + var position_delta = 50 + + var bpm_changes = { 0: 120000 } + + var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + + var value = rhythm_game_utilities.calculate_accuracy_ratio(750, current_position, position_delta) + + print(round(value * 100) / 100.0) # 0.64 +``` + #### `Utilities.CalculateBeatBars` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -669,9 +804,29 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var resolution = 192 + var time_signature = 4 + + var bpm_changes = { + 0: 88000, 3840: 112000, 9984: 89600, + 22272: 112000, 33792: 111500, 34560: 112000, + 42240: 111980 + } + + var beat_bars = rhythm_game_utilities.calculate_beat_bars(bpm_changes, resolution, time_signature, true) + + print(beat_bars) +``` + #### `Utilities.ConvertSecondsToTicks` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -724,9 +879,29 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var seconds = 5 + var resolution = 192 + + var bpm_changes = { + 0: 88000, 3840: 112000, 9984: 89600, + 22272: 112000, 33792: 111500, 34560: 112000, + 42240: 111980 + } + + var ticks = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + + print(ticks) # 1408 +``` + #### `Utilities.ConvertTickToPosition` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -764,9 +939,23 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var tick = 2784 + var resolution = 192 + + var position = rhythm_game_utilities.convert_tick_to_position(tick, resolution) + + print(position) # 14.5 +``` + #### `Utilities.IsOnTheBeat` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -808,9 +997,23 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var bpm = 120 + var current_time = 10 + var delta = 0.05 + + if rhythm_game_utilities.is_on_the_beat(bpm, current_time, delta): + print("Is on the beat!") +``` + #### `Utilities.RoundUpToTheNearestMultiplier` -> Languages: `C#` `C++` +> Languages: `C#` `C++` `GDScript` ##### C# @@ -842,6 +1045,17 @@ int main() } ``` +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var value = rhythm_game_utilities.round_up_to_the_nearest_multiplier(12, 10) + + print(value) # 20 +``` + ## Architecture The current architecture for this project looks like this: