diff --git a/Documentation/API/Parsers/ParseBpmFromChartSection.md b/Documentation/API/Parsers/ParseBpmFromChartSection.md index c7b87e7..2d80a36 100644 --- a/Documentation/API/Parsers/ParseBpmFromChartSection.md +++ b/Documentation/API/Parsers/ParseBpmFromChartSection.md @@ -12,7 +12,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var bpm = Parsers.ParseBpmFromChartSection(sections[NamedSection.SyncTrack]); -Console.WriteLine(bpm.Count); // 7 +Console.WriteLine(bpm.Length); // 7 ``` ##### C++ diff --git a/Documentation/API/Parsers/ParseLyricsFromChartSection.md b/Documentation/API/Parsers/ParseLyricsFromChartSection.md index 9ad0d27..c830d2f 100644 --- a/Documentation/API/Parsers/ParseLyricsFromChartSection.md +++ b/Documentation/API/Parsers/ParseLyricsFromChartSection.md @@ -12,7 +12,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var lyrics = Parsers.ParseLyricsFromChartSection(sections[NamedSection.Events]); -Console.WriteLine(notes.Count); // 12 +Console.WriteLine(lyrics.Count); // 12 ``` ##### C++ diff --git a/Documentation/API/Parsers/ParseNotesFromChartSection.md b/Documentation/API/Parsers/ParseNotesFromChartSection.md index 83776d5..bd06730 100644 --- a/Documentation/API/Parsers/ParseNotesFromChartSection.md +++ b/Documentation/API/Parsers/ParseNotesFromChartSection.md @@ -12,7 +12,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var notes = Parsers.ParseNotesFromChartSection(sections[$"{Difficulty.Expert}Single"]); -Console.WriteLine(notes.Count); // 8 +Console.WriteLine(notes.Length); // 8 ``` ##### C++ diff --git a/Documentation/API/Parsers/ParseTimeSignaturesFromChartSection.md b/Documentation/API/Parsers/ParseTimeSignaturesFromChartSection.md index e5af772..184fa43 100644 --- a/Documentation/API/Parsers/ParseTimeSignaturesFromChartSection.md +++ b/Documentation/API/Parsers/ParseTimeSignaturesFromChartSection.md @@ -12,7 +12,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var timeSignatures = Parsers.ParseTimeSignaturesFromChartSection(sections[NamedSection.SyncTrack]); -Console.WriteLine(timeSignatures.Count); // 4 +Console.WriteLine(timeSignatures.Length); // 4 ``` ##### C++ diff --git a/Documentation/API/Utilities/CalculateAccuracyRatio.md b/Documentation/API/Utilities/CalculateAccuracyRatio.md index be5cdce..6abd6f8 100644 --- a/Documentation/API/Utilities/CalculateAccuracyRatio.md +++ b/Documentation/API/Utilities/CalculateAccuracyRatio.md @@ -12,10 +12,14 @@ const int seconds = 2; const int resolution = 192; const int positionDelta = 50; -var bpmChanges = new Dictionary { { 0, 120000 } }; +var bpmChanges = new Tempo[] { new() { Position = 0, BPM = 120000 } }; + +var timeSignatureChanges = new TimeSignature[] { new() { Position = 0, Numerator = 4, Denominator = 2 } }; var note = new Note { Position = 750 }; -var currentPosition = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges); + +var currentPosition = + Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges); var value = Utilities.CalculateAccuracyRatio(note.Position, currentPosition, positionDelta); @@ -37,11 +41,12 @@ int main() const int resolution = 192; const int positionDelta = 50; - std::map bpmChanges = {{0, 120000}}; + std::vector bpmChanges = {{0, 120000}}; + std::vector timeSignatureChanges = {{0, 4}}; auto note = new Note{750}; - auto currentPosition = - ConvertSecondsToTicks(seconds, resolution, bpmChanges); + auto currentPosition = ConvertSecondsToTicks( + seconds, resolution, bpmChanges, timeSignatureChanges); auto value = CalculateAccuracyRatio(note->Position, currentPosition, positionDelta); @@ -62,9 +67,15 @@ func _ready() -> void: var resolution = 192 var position_delta = 50 - var bpm_changes = { 0: 120000 } + var bpm_changes = [ + {"position": 0, "bpm": 120000 } + ] + + var time_signature_changes = [ + {"position": 0, "numerator": 4, "denominator": 2 } + ] - var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes, time_signature_changes) var value = rhythm_game_utilities.calculate_accuracy_ratio(750, current_position, position_delta) diff --git a/Documentation/API/Utilities/CalculateBeatBars.md b/Documentation/API/Utilities/CalculateBeatBars.md index a3da1d4..c038e6a 100644 --- a/Documentation/API/Utilities/CalculateBeatBars.md +++ b/Documentation/API/Utilities/CalculateBeatBars.md @@ -5,23 +5,17 @@ ##### C# ```csharp -const int resolution = 192; -const int timeSignature = 4; - -var bpmChanges = new Dictionary +var bpmChanges = new Tempo[] { - { 0, 88000 }, - { 3840, 112000 }, - { 9984, 89600 }, - { 22272, 112000 }, - { 33792, 111500 }, - { 34560, 112000 }, - { 42240, 111980 } + new() { Position = 0, BPM = 88000 }, new() { Position = 3840, BPM = 112000 }, + new() { Position = 9984, BPM = 89600 }, new() { Position = 22272, BPM = 112000 }, + new() { Position = 33792, BPM = 111500 }, new() { Position = 34560, BPM = 112000 }, + new() { Position = 42240, BPM = 111980 } }; -var beatBars = Utilities.CalculateBeatBars(bpmChanges, resolution, timeSignature, true); +var beatBars = Utilities.CalculateBeatBars(bpmChanges); -Console.WriteLine(beatBars.Count); // 440 +Console.WriteLine(beatBars.Length); // 440 ``` ##### C++ @@ -38,7 +32,7 @@ int main() const int resolution = 192; const int timeSignature = 4; - std::map bpmChanges = { + std::vector bpmChanges = { {0, 88000}, {3840, 112000}, {9984, 89600}, {22272, 112000}, {33792, 111500}, {34560, 112000}, {42240, 111980}}; @@ -60,11 +54,15 @@ 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 bpm_changes = [ + {"position": 0, "bpm": 8800 }, + {"position": 3840, "bpm": 112000 }, + {"position": 9984, "bpm": 89600 }, + {"position": 22272, "bpm": 112000 }, + {"position": 33792, "bpm": 111500 }, + {"position": 34560, "bpm": 112000 }, + {"position": 42240, "bpm": 111980 } + ] var beat_bars = rhythm_game_utilities.calculate_beat_bars(bpm_changes, resolution, time_signature, true) diff --git a/Documentation/API/Utilities/ConvertSecondsToTicks.md b/Documentation/API/Utilities/ConvertSecondsToTicks.md index 3252be4..6d05b93 100644 --- a/Documentation/API/Utilities/ConvertSecondsToTicks.md +++ b/Documentation/API/Utilities/ConvertSecondsToTicks.md @@ -11,18 +11,17 @@ using RhythmGameUtilities; const int seconds = 5; const int resolution = 192; -var bpmChanges = new Dictionary +var bpmChanges = new Tempo[] { - { 0, 88000 }, - { 3840, 112000 }, - { 9984, 89600 }, - { 22272, 112000 }, - { 33792, 111500 }, - { 34560, 112000 }, - { 42240, 111980 } + new() { Position = 0, BPM = 88000 }, new() { Position = 3840, BPM = 112000 }, + new() { Position = 9984, BPM = 89600 }, new() { Position = 22272, BPM = 112000 }, + new() { Position = 33792, BPM = 111500 }, new() { Position = 34560, BPM = 112000 }, + new() { Position = 42240, BPM = 111980 } }; -var ticks = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges); +var timeSignatureChanges = new TimeSignature[] { new() { Position = 0, Numerator = 4, Denominator = 2 } }; + +var ticks = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges); Console.WriteLine(ticks); // 1408 ``` @@ -41,11 +40,14 @@ int main() const int seconds = 5; const int resolution = 192; - std::map bpmChanges = { + std::vector bpmChanges = { {0, 88000}, {3840, 112000}, {9984, 89600}, {22272, 112000}, {33792, 111500}, {34560, 112000}, {42240, 111980}}; - auto ticks = ConvertSecondsToTicks(seconds, resolution, bpmChanges); + std::vector timeSignatureChanges = {{0, 4, 2}}; + + auto ticks = ConvertSecondsToTicks(seconds, resolution, bpmChanges, + timeSignatureChanges); std::cout << ticks << std::endl; // 1408 @@ -62,13 +64,21 @@ 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 bpm_changes = [ + {"position": 0, "bpm": 8800 }, + {"position": 3840, "bpm": 112000 }, + {"position": 9984, "bpm": 89600 }, + {"position": 22272, "bpm": 112000 }, + {"position": 33792, "bpm": 111500 }, + {"position": 34560, "bpm": 112000 }, + {"position": 42240, "bpm": 111980 } + ] + + var time_signature_changes = [ + {"position": 0, "numerator": 4, "denominator": 2 } + ] - var ticks = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes, time_signature_changes) - print(ticks) # 1408 + print(current_position) # 1408 ``` diff --git a/Documentation/API/Utilities/ConvertTickToPosition.md b/Documentation/API/Utilities/ConvertTickToPosition.md index d29a6cd..cafc2c8 100644 --- a/Documentation/API/Utilities/ConvertTickToPosition.md +++ b/Documentation/API/Utilities/ConvertTickToPosition.md @@ -8,12 +8,12 @@ using System; using RhythmGameUtilities; -const int tick = 2784; +const int tick = 1056; const int resolution = 192; var position = Utilities.ConvertTickToPosition(tick, resolution); -Console.WriteLine(position); // 14.5 +Console.WriteLine(position); // 5.5 ``` ##### C++ @@ -27,12 +27,12 @@ using namespace RhythmGameUtilities; int main() { - const int tick = 2784; + const int tick = 1056; const int resolution = 192; auto position = ConvertTickToPosition(tick, resolution); - std::cout << position << std::endl; // 14.5 + std::cout << position << std::endl; // 5.5 return 0; } @@ -44,10 +44,10 @@ int main() extends Node func _ready() -> void: - var tick = 2784 + var tick = 1056 var resolution = 192 var position = rhythm_game_utilities.convert_tick_to_position(tick, resolution) - print(position) # 14.5 + print(position) # 5.5 ``` diff --git a/Documentation/API/Utilities/FindPositionNearGivenTick.md b/Documentation/API/Utilities/FindPositionNearGivenTick.md new file mode 100644 index 0000000..7c4ced0 --- /dev/null +++ b/Documentation/API/Utilities/FindPositionNearGivenTick.md @@ -0,0 +1,70 @@ +# FindPositionNearGivenTick + +> Languages: `C#` `C++` `GDScript` + +##### C# + +```csharp +var notes = new Note[] +{ + new() { Position = 768 }, new() { Position = 960 }, new() { Position = 1152 }, + new() { Position = 1536 }, new() { Position = 1728 }, new() { Position = 1920 }, + new() { Position = 2304 }, new() { Position = 2496 }, new() { Position = 2688 }, + new() { Position = 3072 }, new() { Position = 3264 } +}; + +var note = Utilities.FindPositionNearGivenTick(notes, 750); + +if (note != null) +{ + Console.Write(note.Value.Position); // 768 +} +``` + +##### C++ + +```cpp +#include + +#include "RhythmGameUtilities/Utilities.hpp" + +using namespace RhythmGameUtilities; + +int main() +{ + std::vector notes = {{768, 0, 0}, {960, 0, 0}, {1152, 0, 0}, + {1536, 0, 0}, {1728, 0, 0}, {1920, 0, 0}, + {2304, 0, 0}, {2496, 0, 0}, {2688, 0, 0}, + {3072, 0, 0}, {3264, 0, 0}}; + + auto note = FindPositionNearGivenTick(notes, 750); + + if (note) + { + std::cout << note->Position << std::endl; // 768 + } + + return 0; +} +``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var seconds = 5 + var resolution = 192 + + var notes = [ + {"position": 768 }, {"position": 960 }, {"position": 1152 }, + {"position": 1536 }, {"position": 1728 }, {"position": 1920 }, + {"position": 2304 }, {"position": 2496 }, {"position": 2688 }, + {"position": 3072 }, {"position": 3264 } + ] + + var note = rhythm_game_utilities.find_position_near_given_tick(notes, 750); + + print(note["position"]) # 768 +``` diff --git a/Documentation/API/Utilities/IsOnTheBeat.md b/Documentation/API/Utilities/IsOnTheBeat.md index 41bf93d..59a23e2 100644 --- a/Documentation/API/Utilities/IsOnTheBeat.md +++ b/Documentation/API/Utilities/IsOnTheBeat.md @@ -12,10 +12,9 @@ const int bpm = 120; const float currentTime = 10f; const float delta = 0.05f; -if (Utilities.IsOnTheBeat(bpm, currentTime, delta)) -{ - Console.WriteLine("Is on the beat!"); -} +var isOnTheBeat = Utilities.IsOnTheBeat(bpm, currentTime, delta); + +Console.WriteLine(isOnTheBeat ? "Is on the beat!" : "Is not on the beat!"); // "Is on the beat!" ``` ##### C++ @@ -30,13 +29,13 @@ using namespace RhythmGameUtilities; int main() { const int bpm = 120; - const float currentTime = 10f; + const float currentTime = 10; const float delta = 0.05f; - if (IsOnTheBeat(bpm, currentTime, delta)) - { - std::cout << "Is on the beat!" << std::endl; - } + auto isOnTheBeat = IsOnTheBeat(bpm, currentTime, delta); + + std::cout << (isOnTheBeat ? "Is on the beat!" : "Is not on the beat!") + << std::endl; // "Is on the beat!" return 0; } @@ -52,6 +51,10 @@ func _ready() -> void: var current_time = 10 var delta = 0.05 - if rhythm_game_utilities.is_on_the_beat(bpm, current_time, delta): + var isOnTheBeat = rhythm_game_utilities.is_on_the_beat(bpm, current_time, delta) + + if isOnTheBeat: # "Is on the beat!" print("Is on the beat!") + else: + print("Is not on the beat!") ``` diff --git a/Documentation/README.md b/Documentation/README.md index ba99a47..e69338e 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -51,6 +51,7 @@ _Prototype game built using these utilities._ 1. [CalculateBeatBars](#utilitiescalculatebeatbars) 1. [ConvertSecondsToTicks](#utilitiesconvertsecondstoticks) 1. [ConvertTickToPosition](#utilitiesconvertticktoposition) + 1. [FindPositionNearGivenTick](#utilitiesfindpositionneargiventick) 1. [IsOnTheBeat](#utilitiesisonthebeat) 1. [RoundUpToTheNearestMultiplier](#utilitiesrounduptothenearestmultiplier) diff --git a/README.md b/README.md index 8b51cd1..fd4cf09 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ _Prototype game built using these utilities._ 1. [CalculateBeatBars](#utilitiescalculatebeatbars) 1. [ConvertSecondsToTicks](#utilitiesconvertsecondstoticks) 1. [ConvertTickToPosition](#utilitiesconvertticktoposition) + 1. [FindPositionNearGivenTick](#utilitiesfindpositionneargiventick) 1. [IsOnTheBeat](#utilitiesisonthebeat) 1. [RoundUpToTheNearestMultiplier](#utilitiesrounduptothenearestmultiplier) @@ -288,7 +289,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var bpm = Parsers.ParseBpmFromChartSection(sections[NamedSection.SyncTrack]); -Console.WriteLine(bpm.Count); // 7 +Console.WriteLine(bpm.Length); // 7 ``` ##### C++ @@ -346,7 +347,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var lyrics = Parsers.ParseLyricsFromChartSection(sections[NamedSection.Events]); -Console.WriteLine(notes.Count); // 12 +Console.WriteLine(lyrics.Count); // 12 ``` ##### C++ @@ -466,7 +467,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var notes = Parsers.ParseNotesFromChartSection(sections[$"{Difficulty.Expert}Single"]); -Console.WriteLine(notes.Count); // 8 +Console.WriteLine(notes.Length); // 8 ``` ##### C++ @@ -583,7 +584,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var timeSignatures = Parsers.ParseTimeSignaturesFromChartSection(sections[NamedSection.SyncTrack]); -Console.WriteLine(timeSignatures.Count); // 4 +Console.WriteLine(timeSignatures.Length); // 4 ``` ##### C++ @@ -643,10 +644,14 @@ const int seconds = 2; const int resolution = 192; const int positionDelta = 50; -var bpmChanges = new Dictionary { { 0, 120000 } }; +var bpmChanges = new Tempo[] { new() { Position = 0, BPM = 120000 } }; + +var timeSignatureChanges = new TimeSignature[] { new() { Position = 0, Numerator = 4, Denominator = 2 } }; var note = new Note { Position = 750 }; -var currentPosition = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges); + +var currentPosition = + Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges); var value = Utilities.CalculateAccuracyRatio(note.Position, currentPosition, positionDelta); @@ -668,11 +673,12 @@ int main() const int resolution = 192; const int positionDelta = 50; - std::map bpmChanges = {{0, 120000}}; + std::vector bpmChanges = {{0, 120000}}; + std::vector timeSignatureChanges = {{0, 4}}; auto note = new Note{750}; - auto currentPosition = - ConvertSecondsToTicks(seconds, resolution, bpmChanges); + auto currentPosition = ConvertSecondsToTicks( + seconds, resolution, bpmChanges, timeSignatureChanges); auto value = CalculateAccuracyRatio(note->Position, currentPosition, positionDelta); @@ -693,9 +699,15 @@ func _ready() -> void: var resolution = 192 var position_delta = 50 - var bpm_changes = { 0: 120000 } + var bpm_changes = [ + {"position": 0, "bpm": 120000 } + ] - var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + var time_signature_changes = [ + {"position": 0, "numerator": 4, "denominator": 2 } + ] + + var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes, time_signature_changes) var value = rhythm_game_utilities.calculate_accuracy_ratio(750, current_position, position_delta) @@ -709,23 +721,17 @@ func _ready() -> void: ##### C# ```csharp -const int resolution = 192; -const int timeSignature = 4; - -var bpmChanges = new Dictionary +var bpmChanges = new Tempo[] { - { 0, 88000 }, - { 3840, 112000 }, - { 9984, 89600 }, - { 22272, 112000 }, - { 33792, 111500 }, - { 34560, 112000 }, - { 42240, 111980 } + new() { Position = 0, BPM = 88000 }, new() { Position = 3840, BPM = 112000 }, + new() { Position = 9984, BPM = 89600 }, new() { Position = 22272, BPM = 112000 }, + new() { Position = 33792, BPM = 111500 }, new() { Position = 34560, BPM = 112000 }, + new() { Position = 42240, BPM = 111980 } }; -var beatBars = Utilities.CalculateBeatBars(bpmChanges, resolution, timeSignature, true); +var beatBars = Utilities.CalculateBeatBars(bpmChanges); -Console.WriteLine(beatBars.Count); // 440 +Console.WriteLine(beatBars.Length); // 440 ``` ##### C++ @@ -742,7 +748,7 @@ int main() const int resolution = 192; const int timeSignature = 4; - std::map bpmChanges = { + std::vector bpmChanges = { {0, 88000}, {3840, 112000}, {9984, 89600}, {22272, 112000}, {33792, 111500}, {34560, 112000}, {42240, 111980}}; @@ -764,11 +770,15 @@ 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 bpm_changes = [ + {"position": 0, "bpm": 8800 }, + {"position": 3840, "bpm": 112000 }, + {"position": 9984, "bpm": 89600 }, + {"position": 22272, "bpm": 112000 }, + {"position": 33792, "bpm": 111500 }, + {"position": 34560, "bpm": 112000 }, + {"position": 42240, "bpm": 111980 } + ] var beat_bars = rhythm_game_utilities.calculate_beat_bars(bpm_changes, resolution, time_signature, true) @@ -788,18 +798,17 @@ using RhythmGameUtilities; const int seconds = 5; const int resolution = 192; -var bpmChanges = new Dictionary +var bpmChanges = new Tempo[] { - { 0, 88000 }, - { 3840, 112000 }, - { 9984, 89600 }, - { 22272, 112000 }, - { 33792, 111500 }, - { 34560, 112000 }, - { 42240, 111980 } + new() { Position = 0, BPM = 88000 }, new() { Position = 3840, BPM = 112000 }, + new() { Position = 9984, BPM = 89600 }, new() { Position = 22272, BPM = 112000 }, + new() { Position = 33792, BPM = 111500 }, new() { Position = 34560, BPM = 112000 }, + new() { Position = 42240, BPM = 111980 } }; -var ticks = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges); +var timeSignatureChanges = new TimeSignature[] { new() { Position = 0, Numerator = 4, Denominator = 2 } }; + +var ticks = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges); Console.WriteLine(ticks); // 1408 ``` @@ -818,11 +827,14 @@ int main() const int seconds = 5; const int resolution = 192; - std::map bpmChanges = { + std::vector bpmChanges = { {0, 88000}, {3840, 112000}, {9984, 89600}, {22272, 112000}, {33792, 111500}, {34560, 112000}, {42240, 111980}}; - auto ticks = ConvertSecondsToTicks(seconds, resolution, bpmChanges); + std::vector timeSignatureChanges = {{0, 4, 2}}; + + auto ticks = ConvertSecondsToTicks(seconds, resolution, bpmChanges, + timeSignatureChanges); std::cout << ticks << std::endl; // 1408 @@ -839,15 +851,23 @@ 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 bpm_changes = [ + {"position": 0, "bpm": 8800 }, + {"position": 3840, "bpm": 112000 }, + {"position": 9984, "bpm": 89600 }, + {"position": 22272, "bpm": 112000 }, + {"position": 33792, "bpm": 111500 }, + {"position": 34560, "bpm": 112000 }, + {"position": 42240, "bpm": 111980 } + ] + + var time_signature_changes = [ + {"position": 0, "numerator": 4, "denominator": 2 } + ] - var ticks = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes, time_signature_changes) - print(ticks) # 1408 + print(current_position) # 1408 ``` #### `Utilities.ConvertTickToPosition` @@ -860,12 +880,12 @@ func _ready() -> void: using System; using RhythmGameUtilities; -const int tick = 2784; +const int tick = 1056; const int resolution = 192; var position = Utilities.ConvertTickToPosition(tick, resolution); -Console.WriteLine(position); // 14.5 +Console.WriteLine(position); // 5.5 ``` ##### C++ @@ -879,12 +899,12 @@ using namespace RhythmGameUtilities; int main() { - const int tick = 2784; + const int tick = 1056; const int resolution = 192; auto position = ConvertTickToPosition(tick, resolution); - std::cout << position << std::endl; // 14.5 + std::cout << position << std::endl; // 5.5 return 0; } @@ -896,12 +916,83 @@ int main() extends Node func _ready() -> void: - var tick = 2784 + var tick = 1056 var resolution = 192 var position = rhythm_game_utilities.convert_tick_to_position(tick, resolution) - print(position) # 14.5 + print(position) # 5.5 +``` + +# FindPositionNearGivenTick + +> Languages: `C#` `C++` `GDScript` + +##### C# + +```csharp +var notes = new Note[] +{ + new() { Position = 768 }, new() { Position = 960 }, new() { Position = 1152 }, + new() { Position = 1536 }, new() { Position = 1728 }, new() { Position = 1920 }, + new() { Position = 2304 }, new() { Position = 2496 }, new() { Position = 2688 }, + new() { Position = 3072 }, new() { Position = 3264 } +}; + +var note = Utilities.FindPositionNearGivenTick(notes, 750); + +if (note != null) +{ + Console.Write(note.Value.Position); // 768 +} +``` + +##### C++ + +```cpp +#include + +#include "RhythmGameUtilities/Utilities.hpp" + +using namespace RhythmGameUtilities; + +int main() +{ + std::vector notes = {{768, 0, 0}, {960, 0, 0}, {1152, 0, 0}, + {1536, 0, 0}, {1728, 0, 0}, {1920, 0, 0}, + {2304, 0, 0}, {2496, 0, 0}, {2688, 0, 0}, + {3072, 0, 0}, {3264, 0, 0}}; + + auto note = FindPositionNearGivenTick(notes, 750); + + if (note) + { + std::cout << note->Position << std::endl; // 768 + } + + return 0; +} +``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var seconds = 5 + var resolution = 192 + + var notes = [ + {"position": 768 }, {"position": 960 }, {"position": 1152 }, + {"position": 1536 }, {"position": 1728 }, {"position": 1920 }, + {"position": 2304 }, {"position": 2496 }, {"position": 2688 }, + {"position": 3072 }, {"position": 3264 } + ] + + var note = rhythm_game_utilities.find_position_near_given_tick(notes, 750); + + print(note["position"]) # 768 ``` #### `Utilities.IsOnTheBeat` @@ -918,10 +1009,9 @@ const int bpm = 120; const float currentTime = 10f; const float delta = 0.05f; -if (Utilities.IsOnTheBeat(bpm, currentTime, delta)) -{ - Console.WriteLine("Is on the beat!"); -} +var isOnTheBeat = Utilities.IsOnTheBeat(bpm, currentTime, delta); + +Console.WriteLine(isOnTheBeat ? "Is on the beat!" : "Is not on the beat!"); // "Is on the beat!" ``` ##### C++ @@ -936,13 +1026,13 @@ using namespace RhythmGameUtilities; int main() { const int bpm = 120; - const float currentTime = 10f; + const float currentTime = 10; const float delta = 0.05f; - if (IsOnTheBeat(bpm, currentTime, delta)) - { - std::cout << "Is on the beat!" << std::endl; - } + auto isOnTheBeat = IsOnTheBeat(bpm, currentTime, delta); + + std::cout << (isOnTheBeat ? "Is on the beat!" : "Is not on the beat!") + << std::endl; // "Is on the beat!" return 0; } @@ -958,8 +1048,12 @@ func _ready() -> void: var current_time = 10 var delta = 0.05 - if rhythm_game_utilities.is_on_the_beat(bpm, current_time, delta): + var isOnTheBeat = rhythm_game_utilities.is_on_the_beat(bpm, current_time, delta) + + if isOnTheBeat: # "Is on the beat!" print("Is on the beat!") + else: + print("Is not on the beat!") ``` #### `Utilities.RoundUpToTheNearestMultiplier` diff --git a/RhythmGameUtilities.Tests/CommonTest.cs b/RhythmGameUtilities.Tests/CommonTest.cs index 65d88ce..4776a25 100644 --- a/RhythmGameUtilities.Tests/CommonTest.cs +++ b/RhythmGameUtilities.Tests/CommonTest.cs @@ -7,21 +7,37 @@ public class CommonTest { [Test] - public void TestLerp() + public void TestInverseLerp() { - Assert.That(Common.Lerp(0, 10, 0), Is.EqualTo(0)); - Assert.That(Common.Lerp(0, 10, 0.5f), Is.EqualTo(5)); - Assert.That(Common.Lerp(0, 10, 1), Is.EqualTo(10)); + var value = Common.InverseLerp(0, 10, 5); + + Assert.That(value, Is.EqualTo(0.5f)); } [Test] - public void TestInverseLerp() + public void TestInverseLerpContinued() { Assert.That(Common.InverseLerp(0, 10, 0), Is.EqualTo(0)); Assert.That(Common.InverseLerp(0, 10, 5), Is.EqualTo(0.5f)); Assert.That(Common.InverseLerp(0, 10, 10), Is.EqualTo(1)); } + [Test] + public void TestLerp() + { + var value = Common.Lerp(0, 10, 0.5f); + + Assert.That(value, Is.EqualTo(5)); + } + + [Test] + public void TestLerpContinued() + { + Assert.That(Common.Lerp(0, 10, 0), Is.EqualTo(0)); + Assert.That(Common.Lerp(0, 10, 0.5f), Is.EqualTo(5)); + Assert.That(Common.Lerp(0, 10, 1), Is.EqualTo(10)); + } + } } diff --git a/RhythmGameUtilities.Tests/Mocks.cs b/RhythmGameUtilities.Tests/Mocks.cs index 6cb01f8..a66beb1 100644 --- a/RhythmGameUtilities.Tests/Mocks.cs +++ b/RhythmGameUtilities.Tests/Mocks.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RhythmGameUtilities.Tests { diff --git a/RhythmGameUtilities.Tests/ParsersTest.cs b/RhythmGameUtilities.Tests/ParsersTest.cs index 2337dab..a508634 100644 --- a/RhythmGameUtilities.Tests/ParsersTest.cs +++ b/RhythmGameUtilities.Tests/ParsersTest.cs @@ -1,3 +1,4 @@ +using System; using NUnit.Framework; namespace RhythmGameUtilities.Tests @@ -7,32 +8,27 @@ public class ParsersTest { [Test] - public void TestParseSectionsFromChart() + public void TestParseBpmFromChartSection() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - Assert.That(sections.ContainsKey(NamedSection.Song), Is.True); - Assert.That(sections["Song"].Length, Is.EqualTo(12)); - Assert.That(sections.ContainsKey(NamedSection.SyncTrack), Is.True); - Assert.That(sections[NamedSection.SyncTrack].Length, Is.EqualTo(11)); - Assert.That(sections.ContainsKey(NamedSection.Events), Is.True); - Assert.That(sections[NamedSection.Events].Length, Is.EqualTo(16)); - Assert.That(sections.ContainsKey("ExpertSingle"), Is.True); - Assert.That(sections["ExpertSingle"].Length, Is.EqualTo(11)); + var bpm = Parsers.ParseBpmFromChartSection(sections[NamedSection.SyncTrack]); + + Console.WriteLine(bpm.Length); // 7 + + Assert.That(bpm.Length, Is.EqualTo(7)); } [Test] - public void TestParseValuesFromChartSection() + public void TestParseLyricsFromChartSection() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - Assert.That(sections.ContainsKey(NamedSection.Song), Is.True); - Assert.That(sections[NamedSection.Song][0].Key, Is.EqualTo("Name")); - Assert.That(sections[NamedSection.Song][0].Value[0], Is.EqualTo("Example Song")); - Assert.That(sections[NamedSection.Song][6].Key, Is.EqualTo("Resolution")); - Assert.That(sections[NamedSection.Song][6].Value[0], Is.EqualTo("192")); - Assert.That(sections[NamedSection.Song][11].Key, Is.EqualTo("MusicStream")); - Assert.That(sections[NamedSection.Song][11].Value[0], Is.EqualTo("Example Song.ogg")); + var lyrics = Parsers.ParseLyricsFromChartSection(sections[NamedSection.Events]); + + Console.WriteLine(lyrics.Count); // 12 + + Assert.That(lyrics.Count, Is.EqualTo(12)); } [Test] @@ -40,51 +36,68 @@ public void TestParseMetaDataFromChartSection() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - var data = Parsers.ParseMetaDataFromChartSection(sections[NamedSection.Song]); + var metaData = Parsers.ParseMetaDataFromChartSection(sections[NamedSection.Song]); - Assert.That(data["Name"], Is.EqualTo("Example Song")); - Assert.That(data["Resolution"], Is.EqualTo("192")); - Assert.That(data["MusicStream"], Is.EqualTo("Example Song.ogg")); + Console.WriteLine(metaData["Name"]); // Example Song + Console.WriteLine(metaData["Resolution"]); // 192 + Console.WriteLine(metaData["MusicStream"]); // Example Song.ogg + + Assert.That(metaData["Name"], Is.EqualTo("Example Song")); + Assert.That(metaData["Resolution"], Is.EqualTo("192")); + Assert.That(metaData["MusicStream"], Is.EqualTo("Example Song.ogg")); } [Test] - public void TestParseTimeSignaturesFromChartSection() + public void TestParseNotesFromChartSection() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - var timeSignatures = Parsers.ParseTimeSignaturesFromChartSection(sections[NamedSection.SyncTrack]); + var notes = Parsers.ParseNotesFromChartSection(sections[$"{Difficulty.Expert}Single"]); - Assert.That(timeSignatures.Length, Is.EqualTo(4)); + Assert.That(notes.Length, Is.EqualTo(8)); } [Test] - public void TestParseBpmFromChartSection() + public void TestParseSectionsFromChart() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - var bpm = Parsers.ParseBpmFromChartSection(sections[NamedSection.SyncTrack]); + Console.WriteLine(sections.Count); // 4 - Assert.That(bpm.Count, Is.EqualTo(7)); + Assert.That(sections.Count, Is.EqualTo(4)); + + Assert.That(sections.ContainsKey(NamedSection.Song), Is.True); + Assert.That(sections["Song"].Length, Is.EqualTo(12)); + Assert.That(sections.ContainsKey(NamedSection.SyncTrack), Is.True); + Assert.That(sections[NamedSection.SyncTrack].Length, Is.EqualTo(11)); + Assert.That(sections.ContainsKey(NamedSection.Events), Is.True); + Assert.That(sections[NamedSection.Events].Length, Is.EqualTo(16)); + Assert.That(sections.ContainsKey("ExpertSingle"), Is.True); + Assert.That(sections["ExpertSingle"].Length, Is.EqualTo(11)); } [Test] - public void TestParseNotesFromChartSection() + public void TestParseTimeSignaturesFromChartSection() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - var notes = Parsers.ParseNotesFromChartSection(sections[$"{Difficulty.Expert}Single"]); + var timeSignatures = Parsers.ParseTimeSignaturesFromChartSection(sections[NamedSection.SyncTrack]); - Assert.That(notes.Length, Is.EqualTo(8)); + Assert.That(timeSignatures.Length, Is.EqualTo(4)); } [Test] - public void TestParseLyricsFromChartSection() + public void TestParseValuesFromChartSection() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - var lyrics = Parsers.ParseLyricsFromChartSection(sections[NamedSection.Events]); - - Assert.That(lyrics.Count, Is.EqualTo(12)); + Assert.That(sections.ContainsKey(NamedSection.Song), Is.True); + Assert.That(sections[NamedSection.Song][0].Key, Is.EqualTo("Name")); + Assert.That(sections[NamedSection.Song][0].Value[0], Is.EqualTo("Example Song")); + Assert.That(sections[NamedSection.Song][6].Key, Is.EqualTo("Resolution")); + Assert.That(sections[NamedSection.Song][6].Value[0], Is.EqualTo("192")); + Assert.That(sections[NamedSection.Song][11].Key, Is.EqualTo("MusicStream")); + Assert.That(sections[NamedSection.Song][11].Value[0], Is.EqualTo("Example Song.ogg")); } } diff --git a/RhythmGameUtilities.Tests/UtilitiesTest.cs b/RhythmGameUtilities.Tests/UtilitiesTest.cs index 6bf5b7b..c907143 100644 --- a/RhythmGameUtilities.Tests/UtilitiesTest.cs +++ b/RhythmGameUtilities.Tests/UtilitiesTest.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System; using NUnit.Framework; namespace RhythmGameUtilities.Tests @@ -8,54 +8,59 @@ public class UtilitiesTest { [Test] - public void TestConvertTickToPosition() + public void TestCalculateAccuracyRatio() { - const int tick = 1056; + const int seconds = 2; const int resolution = 192; + const int positionDelta = 50; - Assert.That(Utilities.ConvertTickToPosition(tick, resolution), Is.EqualTo(5.5f)); - } + var bpmChanges = new Tempo[] { new() { Position = 0, BPM = 120000 } }; - [Test] - public void TestConvertSecondsToTicks() - { - const int seconds = 5; - const int resolution = 192; + var timeSignatureChanges = new TimeSignature[] { new() { Position = 0, Numerator = 4, Denominator = 2 } }; - var bpmChanges = new[] - { - new Tempo { Position = 0, BPM = 88000 }, new Tempo { Position = 3840, BPM = 112000 }, - new Tempo { Position = 9984, BPM = 89600 }, new Tempo { Position = 22272, BPM = 112000 }, - new Tempo { Position = 33792, BPM = 111500 }, new Tempo { Position = 34560, BPM = 112000 }, - new Tempo { Position = 42240, BPM = 111980 } - }; + var note = new Note { Position = 750 }; - var timeSignatureChanges = new[] { new TimeSignature { Position = 0, Numerator = 4, Denominator = 2 } }; + var currentPosition = + Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges); - Assert.That( - Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges), - Is.EqualTo(1408)); + var value = Utilities.CalculateAccuracyRatio(note.Position, currentPosition, positionDelta); + + Console.WriteLine(value); // 0.64 + + Assert.That(value, Is.EqualTo(0.64).Within(0.01)); } [Test] - public void TestIsOnTheBeat() + public void TestCalculateAccuracyRatioContinued() { - Assert.That(Utilities.IsOnTheBeat(120, 10), Is.True); - Assert.That(Utilities.IsOnTheBeat(60, 1), Is.True); - Assert.That(Utilities.IsOnTheBeat(60, 1.5f), Is.False); + Assert.That(Utilities.CalculateAccuracyRatio(750, 100), Is.EqualTo(0)); + Assert.That(Utilities.CalculateAccuracyRatio(750, 750), Is.EqualTo(1)); + Assert.That(Utilities.CalculateAccuracyRatio(750, 725), Is.EqualTo(0.5f)); } [Test] - public void TestRoundUpToTheNearestMultiplier() + public void TestCalculateBeatBars() { - Assert.That(Utilities.RoundUpToTheNearestMultiplier(12, 10), Is.EqualTo(20)); + var bpmChanges = new Tempo[] + { + new() { Position = 0, BPM = 88000 }, new() { Position = 3840, BPM = 112000 }, + new() { Position = 9984, BPM = 89600 }, new() { Position = 22272, BPM = 112000 }, + new() { Position = 33792, BPM = 111500 }, new() { Position = 34560, BPM = 112000 }, + new() { Position = 42240, BPM = 111980 } + }; + + var beatBars = Utilities.CalculateBeatBars(bpmChanges); + + Console.WriteLine(beatBars.Length); // 440 + + Assert.That(beatBars.Length, Is.EqualTo(440)); } [Test] - public void TestCalculateBeatBars() + public void TestConvertSecondsToTicks() { + const int seconds = 5; const int resolution = 192; - const int timeSignature = 4; var bpmChanges = new Tempo[] { @@ -65,9 +70,26 @@ public void TestCalculateBeatBars() new() { Position = 42240, BPM = 111980 } }; - var beatBars = Utilities.CalculateBeatBars(bpmChanges, resolution, timeSignature, true); + var timeSignatureChanges = new TimeSignature[] { new() { Position = 0, Numerator = 4, Denominator = 2 } }; - Assert.That(beatBars.Length, Is.EqualTo(440)); + var ticks = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges); + + Console.WriteLine(ticks); // 1408 + + Assert.That(ticks, Is.EqualTo(1408)); + } + + [Test] + public void TestConvertTickToPosition() + { + const int tick = 1056; + const int resolution = 192; + + var position = Utilities.ConvertTickToPosition(tick, resolution); + + Console.WriteLine(position); // 5.5 + + Assert.That(position, Is.EqualTo(5.5f)); } [Test] @@ -78,7 +100,28 @@ public void TestFindPositionNearGivenTick() new() { Position = 768 }, new() { Position = 960 }, new() { Position = 1152 }, new() { Position = 1536 }, new() { Position = 1728 }, new() { Position = 1920 }, new() { Position = 2304 }, new() { Position = 2496 }, new() { Position = 2688 }, - new() { Position = 3072 }, new() { Position = 3264 }, + new() { Position = 3072 }, new() { Position = 3264 } + }; + + var note = Utilities.FindPositionNearGivenTick(notes, 750); + + if (note != null) + { + Console.Write(note.Value.Position); // 768 + } + + Assert.That(note, Is.Not.Null); + } + + [Test] + public void TestFindPositionNearGivenTickContinued() + { + var notes = new Note[] + { + new() { Position = 768 }, new() { Position = 960 }, new() { Position = 1152 }, + new() { Position = 1536 }, new() { Position = 1728 }, new() { Position = 1920 }, + new() { Position = 2304 }, new() { Position = 2496 }, new() { Position = 2688 }, + new() { Position = 3072 }, new() { Position = 3264 } }; Assert.That(Utilities.FindPositionNearGivenTick(notes, 100), Is.Null); @@ -88,11 +131,35 @@ public void TestFindPositionNearGivenTick() } [Test] - public void TestCalculateAccuracyRatio() + public void TestIsOnTheBeat() { - Assert.That(Utilities.CalculateAccuracyRatio(750, 100), Is.EqualTo(0)); - Assert.That(Utilities.CalculateAccuracyRatio(750, 750), Is.EqualTo(1)); - Assert.That(Utilities.CalculateAccuracyRatio(750, 725), Is.EqualTo(0.5f)); + const int bpm = 120; + const float currentTime = 10f; + const float delta = 0.05f; + + var isOnTheBeat = Utilities.IsOnTheBeat(bpm, currentTime, delta); + + Console.WriteLine(isOnTheBeat ? "Is on the beat!" : "Is not on the beat!"); // "Is on the beat!" + + Assert.That(isOnTheBeat, Is.True); + } + + [Test] + public void TestIsOnTheBeatContinued() + { + Assert.That(Utilities.IsOnTheBeat(120, 10), Is.True); + Assert.That(Utilities.IsOnTheBeat(60, 1), Is.True); + Assert.That(Utilities.IsOnTheBeat(60, 1.5f), Is.False); + } + + [Test] + public void TestRoundUpToTheNearestMultiplier() + { + var value = Utilities.RoundUpToTheNearestMultiplier(12, 10); + + Console.WriteLine(value); // 20 + + Assert.That(value, Is.EqualTo(20)); } } diff --git a/RhythmGameUtilities/README.md b/RhythmGameUtilities/README.md index 8b51cd1..fd4cf09 100644 --- a/RhythmGameUtilities/README.md +++ b/RhythmGameUtilities/README.md @@ -51,6 +51,7 @@ _Prototype game built using these utilities._ 1. [CalculateBeatBars](#utilitiescalculatebeatbars) 1. [ConvertSecondsToTicks](#utilitiesconvertsecondstoticks) 1. [ConvertTickToPosition](#utilitiesconvertticktoposition) + 1. [FindPositionNearGivenTick](#utilitiesfindpositionneargiventick) 1. [IsOnTheBeat](#utilitiesisonthebeat) 1. [RoundUpToTheNearestMultiplier](#utilitiesrounduptothenearestmultiplier) @@ -288,7 +289,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var bpm = Parsers.ParseBpmFromChartSection(sections[NamedSection.SyncTrack]); -Console.WriteLine(bpm.Count); // 7 +Console.WriteLine(bpm.Length); // 7 ``` ##### C++ @@ -346,7 +347,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var lyrics = Parsers.ParseLyricsFromChartSection(sections[NamedSection.Events]); -Console.WriteLine(notes.Count); // 12 +Console.WriteLine(lyrics.Count); // 12 ``` ##### C++ @@ -466,7 +467,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var notes = Parsers.ParseNotesFromChartSection(sections[$"{Difficulty.Expert}Single"]); -Console.WriteLine(notes.Count); // 8 +Console.WriteLine(notes.Length); // 8 ``` ##### C++ @@ -583,7 +584,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var timeSignatures = Parsers.ParseTimeSignaturesFromChartSection(sections[NamedSection.SyncTrack]); -Console.WriteLine(timeSignatures.Count); // 4 +Console.WriteLine(timeSignatures.Length); // 4 ``` ##### C++ @@ -643,10 +644,14 @@ const int seconds = 2; const int resolution = 192; const int positionDelta = 50; -var bpmChanges = new Dictionary { { 0, 120000 } }; +var bpmChanges = new Tempo[] { new() { Position = 0, BPM = 120000 } }; + +var timeSignatureChanges = new TimeSignature[] { new() { Position = 0, Numerator = 4, Denominator = 2 } }; var note = new Note { Position = 750 }; -var currentPosition = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges); + +var currentPosition = + Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges); var value = Utilities.CalculateAccuracyRatio(note.Position, currentPosition, positionDelta); @@ -668,11 +673,12 @@ int main() const int resolution = 192; const int positionDelta = 50; - std::map bpmChanges = {{0, 120000}}; + std::vector bpmChanges = {{0, 120000}}; + std::vector timeSignatureChanges = {{0, 4}}; auto note = new Note{750}; - auto currentPosition = - ConvertSecondsToTicks(seconds, resolution, bpmChanges); + auto currentPosition = ConvertSecondsToTicks( + seconds, resolution, bpmChanges, timeSignatureChanges); auto value = CalculateAccuracyRatio(note->Position, currentPosition, positionDelta); @@ -693,9 +699,15 @@ func _ready() -> void: var resolution = 192 var position_delta = 50 - var bpm_changes = { 0: 120000 } + var bpm_changes = [ + {"position": 0, "bpm": 120000 } + ] - var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + var time_signature_changes = [ + {"position": 0, "numerator": 4, "denominator": 2 } + ] + + var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes, time_signature_changes) var value = rhythm_game_utilities.calculate_accuracy_ratio(750, current_position, position_delta) @@ -709,23 +721,17 @@ func _ready() -> void: ##### C# ```csharp -const int resolution = 192; -const int timeSignature = 4; - -var bpmChanges = new Dictionary +var bpmChanges = new Tempo[] { - { 0, 88000 }, - { 3840, 112000 }, - { 9984, 89600 }, - { 22272, 112000 }, - { 33792, 111500 }, - { 34560, 112000 }, - { 42240, 111980 } + new() { Position = 0, BPM = 88000 }, new() { Position = 3840, BPM = 112000 }, + new() { Position = 9984, BPM = 89600 }, new() { Position = 22272, BPM = 112000 }, + new() { Position = 33792, BPM = 111500 }, new() { Position = 34560, BPM = 112000 }, + new() { Position = 42240, BPM = 111980 } }; -var beatBars = Utilities.CalculateBeatBars(bpmChanges, resolution, timeSignature, true); +var beatBars = Utilities.CalculateBeatBars(bpmChanges); -Console.WriteLine(beatBars.Count); // 440 +Console.WriteLine(beatBars.Length); // 440 ``` ##### C++ @@ -742,7 +748,7 @@ int main() const int resolution = 192; const int timeSignature = 4; - std::map bpmChanges = { + std::vector bpmChanges = { {0, 88000}, {3840, 112000}, {9984, 89600}, {22272, 112000}, {33792, 111500}, {34560, 112000}, {42240, 111980}}; @@ -764,11 +770,15 @@ 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 bpm_changes = [ + {"position": 0, "bpm": 8800 }, + {"position": 3840, "bpm": 112000 }, + {"position": 9984, "bpm": 89600 }, + {"position": 22272, "bpm": 112000 }, + {"position": 33792, "bpm": 111500 }, + {"position": 34560, "bpm": 112000 }, + {"position": 42240, "bpm": 111980 } + ] var beat_bars = rhythm_game_utilities.calculate_beat_bars(bpm_changes, resolution, time_signature, true) @@ -788,18 +798,17 @@ using RhythmGameUtilities; const int seconds = 5; const int resolution = 192; -var bpmChanges = new Dictionary +var bpmChanges = new Tempo[] { - { 0, 88000 }, - { 3840, 112000 }, - { 9984, 89600 }, - { 22272, 112000 }, - { 33792, 111500 }, - { 34560, 112000 }, - { 42240, 111980 } + new() { Position = 0, BPM = 88000 }, new() { Position = 3840, BPM = 112000 }, + new() { Position = 9984, BPM = 89600 }, new() { Position = 22272, BPM = 112000 }, + new() { Position = 33792, BPM = 111500 }, new() { Position = 34560, BPM = 112000 }, + new() { Position = 42240, BPM = 111980 } }; -var ticks = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges); +var timeSignatureChanges = new TimeSignature[] { new() { Position = 0, Numerator = 4, Denominator = 2 } }; + +var ticks = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges); Console.WriteLine(ticks); // 1408 ``` @@ -818,11 +827,14 @@ int main() const int seconds = 5; const int resolution = 192; - std::map bpmChanges = { + std::vector bpmChanges = { {0, 88000}, {3840, 112000}, {9984, 89600}, {22272, 112000}, {33792, 111500}, {34560, 112000}, {42240, 111980}}; - auto ticks = ConvertSecondsToTicks(seconds, resolution, bpmChanges); + std::vector timeSignatureChanges = {{0, 4, 2}}; + + auto ticks = ConvertSecondsToTicks(seconds, resolution, bpmChanges, + timeSignatureChanges); std::cout << ticks << std::endl; // 1408 @@ -839,15 +851,23 @@ 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 bpm_changes = [ + {"position": 0, "bpm": 8800 }, + {"position": 3840, "bpm": 112000 }, + {"position": 9984, "bpm": 89600 }, + {"position": 22272, "bpm": 112000 }, + {"position": 33792, "bpm": 111500 }, + {"position": 34560, "bpm": 112000 }, + {"position": 42240, "bpm": 111980 } + ] + + var time_signature_changes = [ + {"position": 0, "numerator": 4, "denominator": 2 } + ] - var ticks = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes, time_signature_changes) - print(ticks) # 1408 + print(current_position) # 1408 ``` #### `Utilities.ConvertTickToPosition` @@ -860,12 +880,12 @@ func _ready() -> void: using System; using RhythmGameUtilities; -const int tick = 2784; +const int tick = 1056; const int resolution = 192; var position = Utilities.ConvertTickToPosition(tick, resolution); -Console.WriteLine(position); // 14.5 +Console.WriteLine(position); // 5.5 ``` ##### C++ @@ -879,12 +899,12 @@ using namespace RhythmGameUtilities; int main() { - const int tick = 2784; + const int tick = 1056; const int resolution = 192; auto position = ConvertTickToPosition(tick, resolution); - std::cout << position << std::endl; // 14.5 + std::cout << position << std::endl; // 5.5 return 0; } @@ -896,12 +916,83 @@ int main() extends Node func _ready() -> void: - var tick = 2784 + var tick = 1056 var resolution = 192 var position = rhythm_game_utilities.convert_tick_to_position(tick, resolution) - print(position) # 14.5 + print(position) # 5.5 +``` + +# FindPositionNearGivenTick + +> Languages: `C#` `C++` `GDScript` + +##### C# + +```csharp +var notes = new Note[] +{ + new() { Position = 768 }, new() { Position = 960 }, new() { Position = 1152 }, + new() { Position = 1536 }, new() { Position = 1728 }, new() { Position = 1920 }, + new() { Position = 2304 }, new() { Position = 2496 }, new() { Position = 2688 }, + new() { Position = 3072 }, new() { Position = 3264 } +}; + +var note = Utilities.FindPositionNearGivenTick(notes, 750); + +if (note != null) +{ + Console.Write(note.Value.Position); // 768 +} +``` + +##### C++ + +```cpp +#include + +#include "RhythmGameUtilities/Utilities.hpp" + +using namespace RhythmGameUtilities; + +int main() +{ + std::vector notes = {{768, 0, 0}, {960, 0, 0}, {1152, 0, 0}, + {1536, 0, 0}, {1728, 0, 0}, {1920, 0, 0}, + {2304, 0, 0}, {2496, 0, 0}, {2688, 0, 0}, + {3072, 0, 0}, {3264, 0, 0}}; + + auto note = FindPositionNearGivenTick(notes, 750); + + if (note) + { + std::cout << note->Position << std::endl; // 768 + } + + return 0; +} +``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var seconds = 5 + var resolution = 192 + + var notes = [ + {"position": 768 }, {"position": 960 }, {"position": 1152 }, + {"position": 1536 }, {"position": 1728 }, {"position": 1920 }, + {"position": 2304 }, {"position": 2496 }, {"position": 2688 }, + {"position": 3072 }, {"position": 3264 } + ] + + var note = rhythm_game_utilities.find_position_near_given_tick(notes, 750); + + print(note["position"]) # 768 ``` #### `Utilities.IsOnTheBeat` @@ -918,10 +1009,9 @@ const int bpm = 120; const float currentTime = 10f; const float delta = 0.05f; -if (Utilities.IsOnTheBeat(bpm, currentTime, delta)) -{ - Console.WriteLine("Is on the beat!"); -} +var isOnTheBeat = Utilities.IsOnTheBeat(bpm, currentTime, delta); + +Console.WriteLine(isOnTheBeat ? "Is on the beat!" : "Is not on the beat!"); // "Is on the beat!" ``` ##### C++ @@ -936,13 +1026,13 @@ using namespace RhythmGameUtilities; int main() { const int bpm = 120; - const float currentTime = 10f; + const float currentTime = 10; const float delta = 0.05f; - if (IsOnTheBeat(bpm, currentTime, delta)) - { - std::cout << "Is on the beat!" << std::endl; - } + auto isOnTheBeat = IsOnTheBeat(bpm, currentTime, delta); + + std::cout << (isOnTheBeat ? "Is on the beat!" : "Is not on the beat!") + << std::endl; // "Is on the beat!" return 0; } @@ -958,8 +1048,12 @@ func _ready() -> void: var current_time = 10 var delta = 0.05 - if rhythm_game_utilities.is_on_the_beat(bpm, current_time, delta): + var isOnTheBeat = rhythm_game_utilities.is_on_the_beat(bpm, current_time, delta) + + if isOnTheBeat: # "Is on the beat!" print("Is on the beat!") + else: + print("Is not on the beat!") ``` #### `Utilities.RoundUpToTheNearestMultiplier` diff --git a/RhythmGameUtilities/Scripts/Audio.cs b/RhythmGameUtilities/Scripts/Audio.cs index f6a6191..2a64592 100644 --- a/RhythmGameUtilities/Scripts/Audio.cs +++ b/RhythmGameUtilities/Scripts/Audio.cs @@ -19,9 +19,8 @@ public static class Audio { /// - /// Converts samples from an audio file into data used to display a waveform. + /// Converts samples from an audio file into data used to display a waveform. /// - /// /// Array of sample data from an audio file. /// Width of the waveform. /// Height of the waveform. diff --git a/RhythmGameUtilities/Scripts/Common.cs b/RhythmGameUtilities/Scripts/Common.cs index c529e1a..fdb88cf 100644 --- a/RhythmGameUtilities/Scripts/Common.cs +++ b/RhythmGameUtilities/Scripts/Common.cs @@ -18,9 +18,8 @@ public static class Common { /// - /// Calculates the linear interpolation between two values. + /// Calculates the linear interpolation between two values. /// - /// /// The start value. /// The end value. /// The value used for interpolation. @@ -30,9 +29,8 @@ public static float Lerp(float a, float b, float t) } /// - /// Calculates the fraction, based on a value between two values. + /// Calculates the fraction, based on a value between two values. /// - /// /// The start value. /// The end value. /// The value in the middle. diff --git a/RhythmGameUtilities/Scripts/Parsers.cs b/RhythmGameUtilities/Scripts/Parsers.cs index ff4503b..3b7c2ff 100644 --- a/RhythmGameUtilities/Scripts/Parsers.cs +++ b/RhythmGameUtilities/Scripts/Parsers.cs @@ -106,14 +106,16 @@ public static Dictionary[]> ParseSections public static Dictionary ParseMetaDataFromChartSection( KeyValuePair[] section) { - return section.ToDictionary(item => item.Key, x => x.Value.First()); + return section.Where(item => item.Value.Length >= 1) + .ToDictionary(item => item.Key, x => x.Value[0]); } public static TimeSignature[] ParseTimeSignaturesFromChartSection( KeyValuePair[] section) { return section - .Where(item => item.Value.Length >= 2 && item.Value.First() == TypeCode.TimeSignatureMarker).Select( + .Where(item => item.Value.Length >= 2 && item.Value[0] == TypeCode.TimeSignatureMarker) + .OrderBy(item => int.Parse(item.Key)).Select( item => new TimeSignature { Position = int.Parse(item.Key), @@ -122,20 +124,20 @@ public static TimeSignature[] ParseTimeSignaturesFromChartSection( }).ToArray(); } - public static Dictionary ParseBpmFromChartSection( + public static Tempo[] ParseBpmFromChartSection( KeyValuePair[] section) { return section .Where(item => item.Value[0] == TypeCode.BPM_Marker) .Select(item => new KeyValuePair(int.Parse(item.Key), int.Parse(item.Value.Skip(1).First()))) - .OrderBy(item => item.Key) - .ToDictionary(item => item.Key, x => x.Value); + .OrderBy(item => item.Key).Select( + item => new Tempo { Position = item.Key, BPM = item.Value }).ToArray(); } public static Note[] ParseNotesFromChartSection(KeyValuePair[] section) { return section - .Where(item => item.Value.Length == 3 && item.Value.First() == TypeCode.NoteMarker).Select( + .Where(item => item.Value.Length == 3 && item.Value[0] == TypeCode.NoteMarker).Select( item => new Note { Position = int.Parse(item.Key), @@ -148,7 +150,7 @@ public static Dictionary ParseLyricsFromChartSection( KeyValuePair[] section) { return section - .Where(item => item.Value.First() == TypeCode.EventMarker) + .Where(item => item.Value[0] == TypeCode.EventMarker) .Select( item => new KeyValuePair(int.Parse(item.Key), JSON_VALUE_PATTERN.Matches(item.Value.Skip(1).First()).Select(part => part.Value.Trim('"')) diff --git a/RhythmGameUtilities/Scripts/Utilities.cs b/RhythmGameUtilities/Scripts/Utilities.cs index d93127d..c546f57 100644 --- a/RhythmGameUtilities/Scripts/Utilities.cs +++ b/RhythmGameUtilities/Scripts/Utilities.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; namespace RhythmGameUtilities @@ -38,9 +36,8 @@ public static class Utilities public const float SECONDS_PER_MINUTE = 60.0f; /// - /// Convert a tick to a 2D/3D position. + /// Convert a tick to a 2D/3D position. /// - /// /// The tick. /// The resolution of the song. public static float ConvertTickToPosition(int tick, int resolution) @@ -49,9 +46,8 @@ public static float ConvertTickToPosition(int tick, int resolution) } /// - /// Convert seconds to ticks. + /// Convert seconds to ticks. /// - /// /// The seconds to generate ticks with. /// The resolution of the song. /// All BPM changes within the song. @@ -64,9 +60,8 @@ public static int ConvertSecondsToTicks(float seconds, int resolution, Tempo[] b } /// - /// Checks to see if the current time of a game or audio file is on the beat. + /// Checks to see if the current time of a game or audio file is on the beat. /// - /// /// The base BPM for a song. /// A timestamp to compare to the BPM. /// The plus/minus delta to test the current time against. @@ -76,9 +71,8 @@ public static bool IsOnTheBeat(int bpm, float currentTime, float delta = 0.05f) } /// - /// Rounds a value up the nearest multiplier. + /// Rounds a value up the nearest multiplier. /// - /// /// The value to round. /// The multiplier to round using. public static int RoundUpToTheNearestMultiplier(int value, int multiplier) @@ -139,9 +133,8 @@ public static BeatBar[] CalculateBeatBars(Tempo[] bpmChanges, int resolution = 1 } /// - /// Calculated the accuracy ratio of the current position against a static position. + /// Calculated the accuracy ratio of the current position against a static position. /// - /// /// The position to test against. /// The current position. /// The plus/minus delta to test the current position against. diff --git a/RhythmGameUtilities/Structs/Note.cs b/RhythmGameUtilities/Structs/Note.cs index a1d4f15..dc803be 100644 --- a/RhythmGameUtilities/Structs/Note.cs +++ b/RhythmGameUtilities/Structs/Note.cs @@ -14,7 +14,10 @@ public struct Note : IEquatable public int Length; - public override int GetHashCode() => (Position, HandPosition, Length).GetHashCode(); + public override int GetHashCode() + { + return (Position, HandPosition, Length).GetHashCode(); + } public bool Equals(Note other) { diff --git a/UnityPackage/Editor/Tests/CommonTest.cs b/UnityPackage/Editor/Tests/CommonTest.cs index 65d88ce..4776a25 100644 --- a/UnityPackage/Editor/Tests/CommonTest.cs +++ b/UnityPackage/Editor/Tests/CommonTest.cs @@ -7,21 +7,37 @@ public class CommonTest { [Test] - public void TestLerp() + public void TestInverseLerp() { - Assert.That(Common.Lerp(0, 10, 0), Is.EqualTo(0)); - Assert.That(Common.Lerp(0, 10, 0.5f), Is.EqualTo(5)); - Assert.That(Common.Lerp(0, 10, 1), Is.EqualTo(10)); + var value = Common.InverseLerp(0, 10, 5); + + Assert.That(value, Is.EqualTo(0.5f)); } [Test] - public void TestInverseLerp() + public void TestInverseLerpContinued() { Assert.That(Common.InverseLerp(0, 10, 0), Is.EqualTo(0)); Assert.That(Common.InverseLerp(0, 10, 5), Is.EqualTo(0.5f)); Assert.That(Common.InverseLerp(0, 10, 10), Is.EqualTo(1)); } + [Test] + public void TestLerp() + { + var value = Common.Lerp(0, 10, 0.5f); + + Assert.That(value, Is.EqualTo(5)); + } + + [Test] + public void TestLerpContinued() + { + Assert.That(Common.Lerp(0, 10, 0), Is.EqualTo(0)); + Assert.That(Common.Lerp(0, 10, 0.5f), Is.EqualTo(5)); + Assert.That(Common.Lerp(0, 10, 1), Is.EqualTo(10)); + } + } } diff --git a/UnityPackage/Editor/Tests/Mocks.cs b/UnityPackage/Editor/Tests/Mocks.cs index 6cb01f8..a66beb1 100644 --- a/UnityPackage/Editor/Tests/Mocks.cs +++ b/UnityPackage/Editor/Tests/Mocks.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RhythmGameUtilities.Tests { diff --git a/UnityPackage/Editor/Tests/ParsersTest.cs b/UnityPackage/Editor/Tests/ParsersTest.cs index 2337dab..a508634 100644 --- a/UnityPackage/Editor/Tests/ParsersTest.cs +++ b/UnityPackage/Editor/Tests/ParsersTest.cs @@ -1,3 +1,4 @@ +using System; using NUnit.Framework; namespace RhythmGameUtilities.Tests @@ -7,32 +8,27 @@ public class ParsersTest { [Test] - public void TestParseSectionsFromChart() + public void TestParseBpmFromChartSection() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - Assert.That(sections.ContainsKey(NamedSection.Song), Is.True); - Assert.That(sections["Song"].Length, Is.EqualTo(12)); - Assert.That(sections.ContainsKey(NamedSection.SyncTrack), Is.True); - Assert.That(sections[NamedSection.SyncTrack].Length, Is.EqualTo(11)); - Assert.That(sections.ContainsKey(NamedSection.Events), Is.True); - Assert.That(sections[NamedSection.Events].Length, Is.EqualTo(16)); - Assert.That(sections.ContainsKey("ExpertSingle"), Is.True); - Assert.That(sections["ExpertSingle"].Length, Is.EqualTo(11)); + var bpm = Parsers.ParseBpmFromChartSection(sections[NamedSection.SyncTrack]); + + Console.WriteLine(bpm.Length); // 7 + + Assert.That(bpm.Length, Is.EqualTo(7)); } [Test] - public void TestParseValuesFromChartSection() + public void TestParseLyricsFromChartSection() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - Assert.That(sections.ContainsKey(NamedSection.Song), Is.True); - Assert.That(sections[NamedSection.Song][0].Key, Is.EqualTo("Name")); - Assert.That(sections[NamedSection.Song][0].Value[0], Is.EqualTo("Example Song")); - Assert.That(sections[NamedSection.Song][6].Key, Is.EqualTo("Resolution")); - Assert.That(sections[NamedSection.Song][6].Value[0], Is.EqualTo("192")); - Assert.That(sections[NamedSection.Song][11].Key, Is.EqualTo("MusicStream")); - Assert.That(sections[NamedSection.Song][11].Value[0], Is.EqualTo("Example Song.ogg")); + var lyrics = Parsers.ParseLyricsFromChartSection(sections[NamedSection.Events]); + + Console.WriteLine(lyrics.Count); // 12 + + Assert.That(lyrics.Count, Is.EqualTo(12)); } [Test] @@ -40,51 +36,68 @@ public void TestParseMetaDataFromChartSection() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - var data = Parsers.ParseMetaDataFromChartSection(sections[NamedSection.Song]); + var metaData = Parsers.ParseMetaDataFromChartSection(sections[NamedSection.Song]); - Assert.That(data["Name"], Is.EqualTo("Example Song")); - Assert.That(data["Resolution"], Is.EqualTo("192")); - Assert.That(data["MusicStream"], Is.EqualTo("Example Song.ogg")); + Console.WriteLine(metaData["Name"]); // Example Song + Console.WriteLine(metaData["Resolution"]); // 192 + Console.WriteLine(metaData["MusicStream"]); // Example Song.ogg + + Assert.That(metaData["Name"], Is.EqualTo("Example Song")); + Assert.That(metaData["Resolution"], Is.EqualTo("192")); + Assert.That(metaData["MusicStream"], Is.EqualTo("Example Song.ogg")); } [Test] - public void TestParseTimeSignaturesFromChartSection() + public void TestParseNotesFromChartSection() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - var timeSignatures = Parsers.ParseTimeSignaturesFromChartSection(sections[NamedSection.SyncTrack]); + var notes = Parsers.ParseNotesFromChartSection(sections[$"{Difficulty.Expert}Single"]); - Assert.That(timeSignatures.Length, Is.EqualTo(4)); + Assert.That(notes.Length, Is.EqualTo(8)); } [Test] - public void TestParseBpmFromChartSection() + public void TestParseSectionsFromChart() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - var bpm = Parsers.ParseBpmFromChartSection(sections[NamedSection.SyncTrack]); + Console.WriteLine(sections.Count); // 4 - Assert.That(bpm.Count, Is.EqualTo(7)); + Assert.That(sections.Count, Is.EqualTo(4)); + + Assert.That(sections.ContainsKey(NamedSection.Song), Is.True); + Assert.That(sections["Song"].Length, Is.EqualTo(12)); + Assert.That(sections.ContainsKey(NamedSection.SyncTrack), Is.True); + Assert.That(sections[NamedSection.SyncTrack].Length, Is.EqualTo(11)); + Assert.That(sections.ContainsKey(NamedSection.Events), Is.True); + Assert.That(sections[NamedSection.Events].Length, Is.EqualTo(16)); + Assert.That(sections.ContainsKey("ExpertSingle"), Is.True); + Assert.That(sections["ExpertSingle"].Length, Is.EqualTo(11)); } [Test] - public void TestParseNotesFromChartSection() + public void TestParseTimeSignaturesFromChartSection() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - var notes = Parsers.ParseNotesFromChartSection(sections[$"{Difficulty.Expert}Single"]); + var timeSignatures = Parsers.ParseTimeSignaturesFromChartSection(sections[NamedSection.SyncTrack]); - Assert.That(notes.Length, Is.EqualTo(8)); + Assert.That(timeSignatures.Length, Is.EqualTo(4)); } [Test] - public void TestParseLyricsFromChartSection() + public void TestParseValuesFromChartSection() { var sections = Parsers.ParseSectionsFromChart(Mocks.SONG_CHART); - var lyrics = Parsers.ParseLyricsFromChartSection(sections[NamedSection.Events]); - - Assert.That(lyrics.Count, Is.EqualTo(12)); + Assert.That(sections.ContainsKey(NamedSection.Song), Is.True); + Assert.That(sections[NamedSection.Song][0].Key, Is.EqualTo("Name")); + Assert.That(sections[NamedSection.Song][0].Value[0], Is.EqualTo("Example Song")); + Assert.That(sections[NamedSection.Song][6].Key, Is.EqualTo("Resolution")); + Assert.That(sections[NamedSection.Song][6].Value[0], Is.EqualTo("192")); + Assert.That(sections[NamedSection.Song][11].Key, Is.EqualTo("MusicStream")); + Assert.That(sections[NamedSection.Song][11].Value[0], Is.EqualTo("Example Song.ogg")); } } diff --git a/UnityPackage/Editor/Tests/UtilitiesTest.cs b/UnityPackage/Editor/Tests/UtilitiesTest.cs index 6bf5b7b..c907143 100644 --- a/UnityPackage/Editor/Tests/UtilitiesTest.cs +++ b/UnityPackage/Editor/Tests/UtilitiesTest.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System; using NUnit.Framework; namespace RhythmGameUtilities.Tests @@ -8,54 +8,59 @@ public class UtilitiesTest { [Test] - public void TestConvertTickToPosition() + public void TestCalculateAccuracyRatio() { - const int tick = 1056; + const int seconds = 2; const int resolution = 192; + const int positionDelta = 50; - Assert.That(Utilities.ConvertTickToPosition(tick, resolution), Is.EqualTo(5.5f)); - } + var bpmChanges = new Tempo[] { new() { Position = 0, BPM = 120000 } }; - [Test] - public void TestConvertSecondsToTicks() - { - const int seconds = 5; - const int resolution = 192; + var timeSignatureChanges = new TimeSignature[] { new() { Position = 0, Numerator = 4, Denominator = 2 } }; - var bpmChanges = new[] - { - new Tempo { Position = 0, BPM = 88000 }, new Tempo { Position = 3840, BPM = 112000 }, - new Tempo { Position = 9984, BPM = 89600 }, new Tempo { Position = 22272, BPM = 112000 }, - new Tempo { Position = 33792, BPM = 111500 }, new Tempo { Position = 34560, BPM = 112000 }, - new Tempo { Position = 42240, BPM = 111980 } - }; + var note = new Note { Position = 750 }; - var timeSignatureChanges = new[] { new TimeSignature { Position = 0, Numerator = 4, Denominator = 2 } }; + var currentPosition = + Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges); - Assert.That( - Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges), - Is.EqualTo(1408)); + var value = Utilities.CalculateAccuracyRatio(note.Position, currentPosition, positionDelta); + + Console.WriteLine(value); // 0.64 + + Assert.That(value, Is.EqualTo(0.64).Within(0.01)); } [Test] - public void TestIsOnTheBeat() + public void TestCalculateAccuracyRatioContinued() { - Assert.That(Utilities.IsOnTheBeat(120, 10), Is.True); - Assert.That(Utilities.IsOnTheBeat(60, 1), Is.True); - Assert.That(Utilities.IsOnTheBeat(60, 1.5f), Is.False); + Assert.That(Utilities.CalculateAccuracyRatio(750, 100), Is.EqualTo(0)); + Assert.That(Utilities.CalculateAccuracyRatio(750, 750), Is.EqualTo(1)); + Assert.That(Utilities.CalculateAccuracyRatio(750, 725), Is.EqualTo(0.5f)); } [Test] - public void TestRoundUpToTheNearestMultiplier() + public void TestCalculateBeatBars() { - Assert.That(Utilities.RoundUpToTheNearestMultiplier(12, 10), Is.EqualTo(20)); + var bpmChanges = new Tempo[] + { + new() { Position = 0, BPM = 88000 }, new() { Position = 3840, BPM = 112000 }, + new() { Position = 9984, BPM = 89600 }, new() { Position = 22272, BPM = 112000 }, + new() { Position = 33792, BPM = 111500 }, new() { Position = 34560, BPM = 112000 }, + new() { Position = 42240, BPM = 111980 } + }; + + var beatBars = Utilities.CalculateBeatBars(bpmChanges); + + Console.WriteLine(beatBars.Length); // 440 + + Assert.That(beatBars.Length, Is.EqualTo(440)); } [Test] - public void TestCalculateBeatBars() + public void TestConvertSecondsToTicks() { + const int seconds = 5; const int resolution = 192; - const int timeSignature = 4; var bpmChanges = new Tempo[] { @@ -65,9 +70,26 @@ public void TestCalculateBeatBars() new() { Position = 42240, BPM = 111980 } }; - var beatBars = Utilities.CalculateBeatBars(bpmChanges, resolution, timeSignature, true); + var timeSignatureChanges = new TimeSignature[] { new() { Position = 0, Numerator = 4, Denominator = 2 } }; - Assert.That(beatBars.Length, Is.EqualTo(440)); + var ticks = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges); + + Console.WriteLine(ticks); // 1408 + + Assert.That(ticks, Is.EqualTo(1408)); + } + + [Test] + public void TestConvertTickToPosition() + { + const int tick = 1056; + const int resolution = 192; + + var position = Utilities.ConvertTickToPosition(tick, resolution); + + Console.WriteLine(position); // 5.5 + + Assert.That(position, Is.EqualTo(5.5f)); } [Test] @@ -78,7 +100,28 @@ public void TestFindPositionNearGivenTick() new() { Position = 768 }, new() { Position = 960 }, new() { Position = 1152 }, new() { Position = 1536 }, new() { Position = 1728 }, new() { Position = 1920 }, new() { Position = 2304 }, new() { Position = 2496 }, new() { Position = 2688 }, - new() { Position = 3072 }, new() { Position = 3264 }, + new() { Position = 3072 }, new() { Position = 3264 } + }; + + var note = Utilities.FindPositionNearGivenTick(notes, 750); + + if (note != null) + { + Console.Write(note.Value.Position); // 768 + } + + Assert.That(note, Is.Not.Null); + } + + [Test] + public void TestFindPositionNearGivenTickContinued() + { + var notes = new Note[] + { + new() { Position = 768 }, new() { Position = 960 }, new() { Position = 1152 }, + new() { Position = 1536 }, new() { Position = 1728 }, new() { Position = 1920 }, + new() { Position = 2304 }, new() { Position = 2496 }, new() { Position = 2688 }, + new() { Position = 3072 }, new() { Position = 3264 } }; Assert.That(Utilities.FindPositionNearGivenTick(notes, 100), Is.Null); @@ -88,11 +131,35 @@ public void TestFindPositionNearGivenTick() } [Test] - public void TestCalculateAccuracyRatio() + public void TestIsOnTheBeat() { - Assert.That(Utilities.CalculateAccuracyRatio(750, 100), Is.EqualTo(0)); - Assert.That(Utilities.CalculateAccuracyRatio(750, 750), Is.EqualTo(1)); - Assert.That(Utilities.CalculateAccuracyRatio(750, 725), Is.EqualTo(0.5f)); + const int bpm = 120; + const float currentTime = 10f; + const float delta = 0.05f; + + var isOnTheBeat = Utilities.IsOnTheBeat(bpm, currentTime, delta); + + Console.WriteLine(isOnTheBeat ? "Is on the beat!" : "Is not on the beat!"); // "Is on the beat!" + + Assert.That(isOnTheBeat, Is.True); + } + + [Test] + public void TestIsOnTheBeatContinued() + { + Assert.That(Utilities.IsOnTheBeat(120, 10), Is.True); + Assert.That(Utilities.IsOnTheBeat(60, 1), Is.True); + Assert.That(Utilities.IsOnTheBeat(60, 1.5f), Is.False); + } + + [Test] + public void TestRoundUpToTheNearestMultiplier() + { + var value = Utilities.RoundUpToTheNearestMultiplier(12, 10); + + Console.WriteLine(value); // 20 + + Assert.That(value, Is.EqualTo(20)); } } diff --git a/UnityPackage/README.md b/UnityPackage/README.md index 8b51cd1..fd4cf09 100644 --- a/UnityPackage/README.md +++ b/UnityPackage/README.md @@ -51,6 +51,7 @@ _Prototype game built using these utilities._ 1. [CalculateBeatBars](#utilitiescalculatebeatbars) 1. [ConvertSecondsToTicks](#utilitiesconvertsecondstoticks) 1. [ConvertTickToPosition](#utilitiesconvertticktoposition) + 1. [FindPositionNearGivenTick](#utilitiesfindpositionneargiventick) 1. [IsOnTheBeat](#utilitiesisonthebeat) 1. [RoundUpToTheNearestMultiplier](#utilitiesrounduptothenearestmultiplier) @@ -288,7 +289,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var bpm = Parsers.ParseBpmFromChartSection(sections[NamedSection.SyncTrack]); -Console.WriteLine(bpm.Count); // 7 +Console.WriteLine(bpm.Length); // 7 ``` ##### C++ @@ -346,7 +347,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var lyrics = Parsers.ParseLyricsFromChartSection(sections[NamedSection.Events]); -Console.WriteLine(notes.Count); // 12 +Console.WriteLine(lyrics.Count); // 12 ``` ##### C++ @@ -466,7 +467,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var notes = Parsers.ParseNotesFromChartSection(sections[$"{Difficulty.Expert}Single"]); -Console.WriteLine(notes.Count); // 8 +Console.WriteLine(notes.Length); // 8 ``` ##### C++ @@ -583,7 +584,7 @@ var sections = Parsers.ParseSectionsFromChart(contents); var timeSignatures = Parsers.ParseTimeSignaturesFromChartSection(sections[NamedSection.SyncTrack]); -Console.WriteLine(timeSignatures.Count); // 4 +Console.WriteLine(timeSignatures.Length); // 4 ``` ##### C++ @@ -643,10 +644,14 @@ const int seconds = 2; const int resolution = 192; const int positionDelta = 50; -var bpmChanges = new Dictionary { { 0, 120000 } }; +var bpmChanges = new Tempo[] { new() { Position = 0, BPM = 120000 } }; + +var timeSignatureChanges = new TimeSignature[] { new() { Position = 0, Numerator = 4, Denominator = 2 } }; var note = new Note { Position = 750 }; -var currentPosition = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges); + +var currentPosition = + Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges); var value = Utilities.CalculateAccuracyRatio(note.Position, currentPosition, positionDelta); @@ -668,11 +673,12 @@ int main() const int resolution = 192; const int positionDelta = 50; - std::map bpmChanges = {{0, 120000}}; + std::vector bpmChanges = {{0, 120000}}; + std::vector timeSignatureChanges = {{0, 4}}; auto note = new Note{750}; - auto currentPosition = - ConvertSecondsToTicks(seconds, resolution, bpmChanges); + auto currentPosition = ConvertSecondsToTicks( + seconds, resolution, bpmChanges, timeSignatureChanges); auto value = CalculateAccuracyRatio(note->Position, currentPosition, positionDelta); @@ -693,9 +699,15 @@ func _ready() -> void: var resolution = 192 var position_delta = 50 - var bpm_changes = { 0: 120000 } + var bpm_changes = [ + {"position": 0, "bpm": 120000 } + ] - var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + var time_signature_changes = [ + {"position": 0, "numerator": 4, "denominator": 2 } + ] + + var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes, time_signature_changes) var value = rhythm_game_utilities.calculate_accuracy_ratio(750, current_position, position_delta) @@ -709,23 +721,17 @@ func _ready() -> void: ##### C# ```csharp -const int resolution = 192; -const int timeSignature = 4; - -var bpmChanges = new Dictionary +var bpmChanges = new Tempo[] { - { 0, 88000 }, - { 3840, 112000 }, - { 9984, 89600 }, - { 22272, 112000 }, - { 33792, 111500 }, - { 34560, 112000 }, - { 42240, 111980 } + new() { Position = 0, BPM = 88000 }, new() { Position = 3840, BPM = 112000 }, + new() { Position = 9984, BPM = 89600 }, new() { Position = 22272, BPM = 112000 }, + new() { Position = 33792, BPM = 111500 }, new() { Position = 34560, BPM = 112000 }, + new() { Position = 42240, BPM = 111980 } }; -var beatBars = Utilities.CalculateBeatBars(bpmChanges, resolution, timeSignature, true); +var beatBars = Utilities.CalculateBeatBars(bpmChanges); -Console.WriteLine(beatBars.Count); // 440 +Console.WriteLine(beatBars.Length); // 440 ``` ##### C++ @@ -742,7 +748,7 @@ int main() const int resolution = 192; const int timeSignature = 4; - std::map bpmChanges = { + std::vector bpmChanges = { {0, 88000}, {3840, 112000}, {9984, 89600}, {22272, 112000}, {33792, 111500}, {34560, 112000}, {42240, 111980}}; @@ -764,11 +770,15 @@ 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 bpm_changes = [ + {"position": 0, "bpm": 8800 }, + {"position": 3840, "bpm": 112000 }, + {"position": 9984, "bpm": 89600 }, + {"position": 22272, "bpm": 112000 }, + {"position": 33792, "bpm": 111500 }, + {"position": 34560, "bpm": 112000 }, + {"position": 42240, "bpm": 111980 } + ] var beat_bars = rhythm_game_utilities.calculate_beat_bars(bpm_changes, resolution, time_signature, true) @@ -788,18 +798,17 @@ using RhythmGameUtilities; const int seconds = 5; const int resolution = 192; -var bpmChanges = new Dictionary +var bpmChanges = new Tempo[] { - { 0, 88000 }, - { 3840, 112000 }, - { 9984, 89600 }, - { 22272, 112000 }, - { 33792, 111500 }, - { 34560, 112000 }, - { 42240, 111980 } + new() { Position = 0, BPM = 88000 }, new() { Position = 3840, BPM = 112000 }, + new() { Position = 9984, BPM = 89600 }, new() { Position = 22272, BPM = 112000 }, + new() { Position = 33792, BPM = 111500 }, new() { Position = 34560, BPM = 112000 }, + new() { Position = 42240, BPM = 111980 } }; -var ticks = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges); +var timeSignatureChanges = new TimeSignature[] { new() { Position = 0, Numerator = 4, Denominator = 2 } }; + +var ticks = Utilities.ConvertSecondsToTicks(seconds, resolution, bpmChanges, timeSignatureChanges); Console.WriteLine(ticks); // 1408 ``` @@ -818,11 +827,14 @@ int main() const int seconds = 5; const int resolution = 192; - std::map bpmChanges = { + std::vector bpmChanges = { {0, 88000}, {3840, 112000}, {9984, 89600}, {22272, 112000}, {33792, 111500}, {34560, 112000}, {42240, 111980}}; - auto ticks = ConvertSecondsToTicks(seconds, resolution, bpmChanges); + std::vector timeSignatureChanges = {{0, 4, 2}}; + + auto ticks = ConvertSecondsToTicks(seconds, resolution, bpmChanges, + timeSignatureChanges); std::cout << ticks << std::endl; // 1408 @@ -839,15 +851,23 @@ 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 bpm_changes = [ + {"position": 0, "bpm": 8800 }, + {"position": 3840, "bpm": 112000 }, + {"position": 9984, "bpm": 89600 }, + {"position": 22272, "bpm": 112000 }, + {"position": 33792, "bpm": 111500 }, + {"position": 34560, "bpm": 112000 }, + {"position": 42240, "bpm": 111980 } + ] + + var time_signature_changes = [ + {"position": 0, "numerator": 4, "denominator": 2 } + ] - var ticks = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes) + var current_position = rhythm_game_utilities.convert_seconds_to_ticks(seconds, resolution, bpm_changes, time_signature_changes) - print(ticks) # 1408 + print(current_position) # 1408 ``` #### `Utilities.ConvertTickToPosition` @@ -860,12 +880,12 @@ func _ready() -> void: using System; using RhythmGameUtilities; -const int tick = 2784; +const int tick = 1056; const int resolution = 192; var position = Utilities.ConvertTickToPosition(tick, resolution); -Console.WriteLine(position); // 14.5 +Console.WriteLine(position); // 5.5 ``` ##### C++ @@ -879,12 +899,12 @@ using namespace RhythmGameUtilities; int main() { - const int tick = 2784; + const int tick = 1056; const int resolution = 192; auto position = ConvertTickToPosition(tick, resolution); - std::cout << position << std::endl; // 14.5 + std::cout << position << std::endl; // 5.5 return 0; } @@ -896,12 +916,83 @@ int main() extends Node func _ready() -> void: - var tick = 2784 + var tick = 1056 var resolution = 192 var position = rhythm_game_utilities.convert_tick_to_position(tick, resolution) - print(position) # 14.5 + print(position) # 5.5 +``` + +# FindPositionNearGivenTick + +> Languages: `C#` `C++` `GDScript` + +##### C# + +```csharp +var notes = new Note[] +{ + new() { Position = 768 }, new() { Position = 960 }, new() { Position = 1152 }, + new() { Position = 1536 }, new() { Position = 1728 }, new() { Position = 1920 }, + new() { Position = 2304 }, new() { Position = 2496 }, new() { Position = 2688 }, + new() { Position = 3072 }, new() { Position = 3264 } +}; + +var note = Utilities.FindPositionNearGivenTick(notes, 750); + +if (note != null) +{ + Console.Write(note.Value.Position); // 768 +} +``` + +##### C++ + +```cpp +#include + +#include "RhythmGameUtilities/Utilities.hpp" + +using namespace RhythmGameUtilities; + +int main() +{ + std::vector notes = {{768, 0, 0}, {960, 0, 0}, {1152, 0, 0}, + {1536, 0, 0}, {1728, 0, 0}, {1920, 0, 0}, + {2304, 0, 0}, {2496, 0, 0}, {2688, 0, 0}, + {3072, 0, 0}, {3264, 0, 0}}; + + auto note = FindPositionNearGivenTick(notes, 750); + + if (note) + { + std::cout << note->Position << std::endl; // 768 + } + + return 0; +} +``` + +##### GDScript + +```gdscript +extends Node + +func _ready() -> void: + var seconds = 5 + var resolution = 192 + + var notes = [ + {"position": 768 }, {"position": 960 }, {"position": 1152 }, + {"position": 1536 }, {"position": 1728 }, {"position": 1920 }, + {"position": 2304 }, {"position": 2496 }, {"position": 2688 }, + {"position": 3072 }, {"position": 3264 } + ] + + var note = rhythm_game_utilities.find_position_near_given_tick(notes, 750); + + print(note["position"]) # 768 ``` #### `Utilities.IsOnTheBeat` @@ -918,10 +1009,9 @@ const int bpm = 120; const float currentTime = 10f; const float delta = 0.05f; -if (Utilities.IsOnTheBeat(bpm, currentTime, delta)) -{ - Console.WriteLine("Is on the beat!"); -} +var isOnTheBeat = Utilities.IsOnTheBeat(bpm, currentTime, delta); + +Console.WriteLine(isOnTheBeat ? "Is on the beat!" : "Is not on the beat!"); // "Is on the beat!" ``` ##### C++ @@ -936,13 +1026,13 @@ using namespace RhythmGameUtilities; int main() { const int bpm = 120; - const float currentTime = 10f; + const float currentTime = 10; const float delta = 0.05f; - if (IsOnTheBeat(bpm, currentTime, delta)) - { - std::cout << "Is on the beat!" << std::endl; - } + auto isOnTheBeat = IsOnTheBeat(bpm, currentTime, delta); + + std::cout << (isOnTheBeat ? "Is on the beat!" : "Is not on the beat!") + << std::endl; // "Is on the beat!" return 0; } @@ -958,8 +1048,12 @@ func _ready() -> void: var current_time = 10 var delta = 0.05 - if rhythm_game_utilities.is_on_the_beat(bpm, current_time, delta): + var isOnTheBeat = rhythm_game_utilities.is_on_the_beat(bpm, current_time, delta) + + if isOnTheBeat: # "Is on the beat!" print("Is on the beat!") + else: + print("Is not on the beat!") ``` #### `Utilities.RoundUpToTheNearestMultiplier` diff --git a/UnityPackage/Scripts/Audio.cs b/UnityPackage/Scripts/Audio.cs index f6a6191..2a64592 100644 --- a/UnityPackage/Scripts/Audio.cs +++ b/UnityPackage/Scripts/Audio.cs @@ -19,9 +19,8 @@ public static class Audio { /// - /// Converts samples from an audio file into data used to display a waveform. + /// Converts samples from an audio file into data used to display a waveform. /// - /// /// Array of sample data from an audio file. /// Width of the waveform. /// Height of the waveform. diff --git a/UnityPackage/Scripts/Common.cs b/UnityPackage/Scripts/Common.cs index c529e1a..fdb88cf 100644 --- a/UnityPackage/Scripts/Common.cs +++ b/UnityPackage/Scripts/Common.cs @@ -18,9 +18,8 @@ public static class Common { /// - /// Calculates the linear interpolation between two values. + /// Calculates the linear interpolation between two values. /// - /// /// The start value. /// The end value. /// The value used for interpolation. @@ -30,9 +29,8 @@ public static float Lerp(float a, float b, float t) } /// - /// Calculates the fraction, based on a value between two values. + /// Calculates the fraction, based on a value between two values. /// - /// /// The start value. /// The end value. /// The value in the middle. diff --git a/UnityPackage/Scripts/Parsers.cs b/UnityPackage/Scripts/Parsers.cs index ff4503b..3b7c2ff 100644 --- a/UnityPackage/Scripts/Parsers.cs +++ b/UnityPackage/Scripts/Parsers.cs @@ -106,14 +106,16 @@ public static Dictionary[]> ParseSections public static Dictionary ParseMetaDataFromChartSection( KeyValuePair[] section) { - return section.ToDictionary(item => item.Key, x => x.Value.First()); + return section.Where(item => item.Value.Length >= 1) + .ToDictionary(item => item.Key, x => x.Value[0]); } public static TimeSignature[] ParseTimeSignaturesFromChartSection( KeyValuePair[] section) { return section - .Where(item => item.Value.Length >= 2 && item.Value.First() == TypeCode.TimeSignatureMarker).Select( + .Where(item => item.Value.Length >= 2 && item.Value[0] == TypeCode.TimeSignatureMarker) + .OrderBy(item => int.Parse(item.Key)).Select( item => new TimeSignature { Position = int.Parse(item.Key), @@ -122,20 +124,20 @@ public static TimeSignature[] ParseTimeSignaturesFromChartSection( }).ToArray(); } - public static Dictionary ParseBpmFromChartSection( + public static Tempo[] ParseBpmFromChartSection( KeyValuePair[] section) { return section .Where(item => item.Value[0] == TypeCode.BPM_Marker) .Select(item => new KeyValuePair(int.Parse(item.Key), int.Parse(item.Value.Skip(1).First()))) - .OrderBy(item => item.Key) - .ToDictionary(item => item.Key, x => x.Value); + .OrderBy(item => item.Key).Select( + item => new Tempo { Position = item.Key, BPM = item.Value }).ToArray(); } public static Note[] ParseNotesFromChartSection(KeyValuePair[] section) { return section - .Where(item => item.Value.Length == 3 && item.Value.First() == TypeCode.NoteMarker).Select( + .Where(item => item.Value.Length == 3 && item.Value[0] == TypeCode.NoteMarker).Select( item => new Note { Position = int.Parse(item.Key), @@ -148,7 +150,7 @@ public static Dictionary ParseLyricsFromChartSection( KeyValuePair[] section) { return section - .Where(item => item.Value.First() == TypeCode.EventMarker) + .Where(item => item.Value[0] == TypeCode.EventMarker) .Select( item => new KeyValuePair(int.Parse(item.Key), JSON_VALUE_PATTERN.Matches(item.Value.Skip(1).First()).Select(part => part.Value.Trim('"')) diff --git a/UnityPackage/Scripts/Utilities.cs b/UnityPackage/Scripts/Utilities.cs index d93127d..c546f57 100644 --- a/UnityPackage/Scripts/Utilities.cs +++ b/UnityPackage/Scripts/Utilities.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; namespace RhythmGameUtilities @@ -38,9 +36,8 @@ public static class Utilities public const float SECONDS_PER_MINUTE = 60.0f; /// - /// Convert a tick to a 2D/3D position. + /// Convert a tick to a 2D/3D position. /// - /// /// The tick. /// The resolution of the song. public static float ConvertTickToPosition(int tick, int resolution) @@ -49,9 +46,8 @@ public static float ConvertTickToPosition(int tick, int resolution) } /// - /// Convert seconds to ticks. + /// Convert seconds to ticks. /// - /// /// The seconds to generate ticks with. /// The resolution of the song. /// All BPM changes within the song. @@ -64,9 +60,8 @@ public static int ConvertSecondsToTicks(float seconds, int resolution, Tempo[] b } /// - /// Checks to see if the current time of a game or audio file is on the beat. + /// Checks to see if the current time of a game or audio file is on the beat. /// - /// /// The base BPM for a song. /// A timestamp to compare to the BPM. /// The plus/minus delta to test the current time against. @@ -76,9 +71,8 @@ public static bool IsOnTheBeat(int bpm, float currentTime, float delta = 0.05f) } /// - /// Rounds a value up the nearest multiplier. + /// Rounds a value up the nearest multiplier. /// - /// /// The value to round. /// The multiplier to round using. public static int RoundUpToTheNearestMultiplier(int value, int multiplier) @@ -139,9 +133,8 @@ public static BeatBar[] CalculateBeatBars(Tempo[] bpmChanges, int resolution = 1 } /// - /// Calculated the accuracy ratio of the current position against a static position. + /// Calculated the accuracy ratio of the current position against a static position. /// - /// /// The position to test against. /// The current position. /// The plus/minus delta to test the current position against. diff --git a/UnityPackage/Structs/Note.cs b/UnityPackage/Structs/Note.cs index a1d4f15..dc803be 100644 --- a/UnityPackage/Structs/Note.cs +++ b/UnityPackage/Structs/Note.cs @@ -14,7 +14,10 @@ public struct Note : IEquatable public int Length; - public override int GetHashCode() => (Position, HandPosition, Length).GetHashCode(); + public override int GetHashCode() + { + return (Position, HandPosition, Length).GetHashCode(); + } public bool Equals(Note other) { diff --git a/tests/RhythmGameUtilities/Common.cpp b/tests/RhythmGameUtilities/Common.cpp index 7e4b47d..216e920 100644 --- a/tests/RhythmGameUtilities/Common.cpp +++ b/tests/RhythmGameUtilities/Common.cpp @@ -7,6 +7,24 @@ using namespace RhythmGameUtilities; +void testInverseLerp() +{ + auto value = InverseLerp(0, 10, 5); + + assert(abs(0.5 - value) < 0.01); + + std::cout << "."; +} + +void testLerp() +{ + auto value = Lerp(0, 10, 0.5f); + + assert(5 == value); + + std::cout << "."; +} + void testTrim() { assert(Trim(" test ") == "test"); @@ -49,6 +67,8 @@ void testFindMatchGroups() int main() { + testInverseLerp(); + testLerp(); testTrim(); testSplit(); testFindAllMatches(); diff --git a/tests/RhythmGameUtilities/Parsers.cpp b/tests/RhythmGameUtilities/Parsers.cpp index da0e6f8..33e5103 100644 --- a/tests/RhythmGameUtilities/Parsers.cpp +++ b/tests/RhythmGameUtilities/Parsers.cpp @@ -69,36 +69,26 @@ auto contents = R"([Song] 1248 = E soloend })"; -void testParseSectionsFromChart() +void testParseBpmFromChartSection() { auto sections = ParseSectionsFromChart(contents); - assert(sections.size() == 4); + auto bpm = ParseBpmFromChartSection( + sections.at(ToString(NamedSection::SyncTrack))); - assert(sections.find(ToString(NamedSection::Song)) != sections.end()); - assert(sections.find(ToString(NamedSection::SyncTrack)) != sections.end()); - assert(sections.find(ToString(Difficulty::Expert) + "Single") != - sections.end()); + assert(bpm.size() == 7); std::cout << "."; } -void testParseValuesFromChartSection() +void testParseLyricsFromChartSection() { auto sections = ParseSectionsFromChart(contents); - auto values = sections.at(ToString(NamedSection::Song)); - - assert(values.size() == 12); - - assert(values[0].first == "Name"); - assert(values[0].second[0] == "Example Song"); - - assert(values[6].first == "Resolution"); - assert(values[6].second[0] == "192"); + auto lyrics = ParseLyricsFromChartSection( + sections.at(ToString(NamedSection::Events))); - assert(values[11].first == "MusicStream"); - assert(values[11].second[0] == "Example Song.ogg"); + assert(lyrics.size() == 12); std::cout << "."; } @@ -107,76 +97,88 @@ void testParseMetaDataFromChartSection() { auto sections = ParseSectionsFromChart(contents); - auto lines = ParseMetaDataFromChartSection( + auto metaData = ParseMetaDataFromChartSection( sections.at(ToString(NamedSection::Song))); - assert(lines.size() == 12); + assert(metaData.size() == 12); - assert(lines.at("Name") == "Example Song"); - assert(lines.at("Resolution") == "192"); - assert(lines.at("MusicStream") == "Example Song.ogg"); + assert(metaData.at("Name") == "Example Song"); + assert(metaData.at("Resolution") == "192"); + assert(metaData.at("MusicStream") == "Example Song.ogg"); std::cout << "."; } -void testParseTimeSignaturesFromChartSection() +void testParseNotesFromChartSection() { auto sections = ParseSectionsFromChart(contents); - auto lines = ParseTimeSignaturesFromChartSection( - sections.at(ToString(NamedSection::SyncTrack))); + auto notes = ParseNotesFromChartSection( + sections.at(ToString(Difficulty::Expert) + "Single")); - assert(lines.size() == 4); + assert(notes.size() == 8); std::cout << "."; } -void testParseBpmFromChartSection() +void testParseSectionsFromChart() { auto sections = ParseSectionsFromChart(contents); - auto lines = ParseBpmFromChartSection( - sections.at(ToString(NamedSection::SyncTrack))); + assert(sections.size() == 4); - assert(lines.size() == 7); + assert(sections.find(ToString(NamedSection::Song)) != sections.end()); + assert(sections.find(ToString(NamedSection::SyncTrack)) != sections.end()); + assert(sections.find(ToString(Difficulty::Expert) + "Single") != + sections.end()); std::cout << "."; } -void testParseNotesFromChartSection() +void testParseTimeSignaturesFromChartSection() { auto sections = ParseSectionsFromChart(contents); - auto lines = ParseNotesFromChartSection( - sections.at(ToString(Difficulty::Expert) + "Single")); + auto timeSignatures = ParseTimeSignaturesFromChartSection( + sections.at(ToString(NamedSection::SyncTrack))); - assert(lines.size() == 8); + assert(timeSignatures.size() == 4); std::cout << "."; } -void testParseLyricsFromChartSection() +void testParseValuesFromChartSection() { auto sections = ParseSectionsFromChart(contents); - auto lines = ParseLyricsFromChartSection( - sections.at(ToString(NamedSection::Events))); + assert(sections.size() == 4); - assert(lines.size() == 12); + auto values = sections.at(ToString(NamedSection::Song)); + + assert(values.size() == 12); + + assert(values[0].first == "Name"); + assert(values[0].second[0] == "Example Song"); + + assert(values[6].first == "Resolution"); + assert(values[6].second[0] == "192"); + + assert(values[11].first == "MusicStream"); + assert(values[11].second[0] == "Example Song.ogg"); std::cout << "."; } int main() { - testParseSectionsFromChart(); - testParseValuesFromChartSection(); - - testParseMetaDataFromChartSection(); - testParseTimeSignaturesFromChartSection(); testParseBpmFromChartSection(); - testParseNotesFromChartSection(); testParseLyricsFromChartSection(); + testParseMetaDataFromChartSection(); + testParseNotesFromChartSection(); + testParseSectionsFromChart(); + testParseTimeSignaturesFromChartSection(); + + testParseValuesFromChartSection(); return 0; } diff --git a/tests/RhythmGameUtilities/Utilities.cpp b/tests/RhythmGameUtilities/Utilities.cpp index de41221..0e32145 100644 --- a/tests/RhythmGameUtilities/Utilities.cpp +++ b/tests/RhythmGameUtilities/Utilities.cpp @@ -10,32 +10,71 @@ using namespace RhythmGameUtilities; -void testConvertTickToPosition() +void testCalculateAccuracyRatio() { - assert(4 == ConvertTickToPosition(768, 192)); + const int seconds = 2; + const int resolution = 192; + const int positionDelta = 50; + + std::vector bpmChanges = {{0, 120000}}; + std::vector timeSignatureChanges = {{0, 4}}; + + auto note = new Note{750}; + auto currentPosition = ConvertSecondsToTicks( + seconds, resolution, bpmChanges, timeSignatureChanges); + + auto value = + CalculateAccuracyRatio(note->Position, currentPosition, positionDelta); + + assert(abs(0.64 - value) < 0.01); + + std::cout << "."; +} + +void testCalculateBeatBars() +{ + const int resolution = 192; + const int timeSignature = 4; + + std::vector bpmChanges = { + {0, 88000}, {3840, 112000}, {9984, 89600}, {22272, 112000}, + {33792, 111500}, {34560, 112000}, {42240, 111980}}; + + auto beatBars = + CalculateBeatBars(bpmChanges, resolution, timeSignature, true); + + assert(beatBars.size() == 440); std::cout << "."; } void testConvertSecondsToTicks() { + const int seconds = 5; + const int resolution = 192; + std::vector bpmChanges = { {0, 88000}, {3840, 112000}, {9984, 89600}, {22272, 112000}, {33792, 111500}, {34560, 112000}, {42240, 111980}}; std::vector timeSignatureChanges = {{0, 4, 2}}; - assert(1408 == - ConvertSecondsToTicks(5, 192, bpmChanges, timeSignatureChanges)); + auto ticks = ConvertSecondsToTicks(seconds, resolution, bpmChanges, + timeSignatureChanges); + + assert(1408 == ticks); std::cout << "."; } -void testIsOnTheBeat() +void testConvertTickToPosition() { - assert(true == IsOnTheBeat(120, 10)); - assert(true == IsOnTheBeat(60, 1)); - assert(false == IsOnTheBeat(60, 1.5f)); + const int tick = 1056; + const int resolution = 192; + + auto position = ConvertTickToPosition(tick, resolution); + + assert(abs(5.5 - position) < 0.01); std::cout << "."; } @@ -47,6 +86,10 @@ void testFindPositionNearGivenTick() {2304, 0, 0}, {2496, 0, 0}, {2688, 0, 0}, {3072, 0, 0}, {3264, 0, 0}}; + auto note = FindPositionNearGivenTick(notes, 750); + + assert(768 == note->Position); + assert(std::nullopt == FindPositionNearGivenTick(notes, 100)); assert(768 == FindPositionNearGivenTick(notes, 750)->Position); assert(1536 == FindPositionNearGivenTick(notes, 1500)->Position); @@ -55,18 +98,24 @@ void testFindPositionNearGivenTick() std::cout << "."; } -void testCalculateAccuracyRatio() +void testIsOnTheBeat() { - assert(0 == CalculateAccuracyRatio(750, 100)); - assert(1 == CalculateAccuracyRatio(750, 750)); - assert(0.5f == CalculateAccuracyRatio(750, 725)); + const int bpm = 120; + const float currentTime = 10; + const float delta = 0.05f; + + auto isOnTheBeat = IsOnTheBeat(bpm, currentTime, delta); + + assert(true == isOnTheBeat); std::cout << "."; } void testRoundUpToTheNearestMultiplier() { - assert(20 == RoundUpToTheNearestMultiplier(12, 10)); + auto value = RoundUpToTheNearestMultiplier(12, 10); + + assert(20 == value); std::cout << "."; } @@ -86,29 +135,16 @@ void testGenerateAdjacentKeyPairs() std::cout << "."; } -void testCalculateBeatBars() -{ - std::vector bpmChanges = { - {0, 88000}, {3840, 112000}, {9984, 89600}, {22272, 112000}, - {33792, 111500}, {34560, 112000}, {42240, 111980}}; - - auto beatBars = CalculateBeatBars(bpmChanges, 192, 4, true); - - assert(beatBars.size() == 440); - - std::cout << "."; -} - int main() { - testConvertTickToPosition(); + testCalculateAccuracyRatio(); + testCalculateBeatBars(); testConvertSecondsToTicks(); - testIsOnTheBeat(); + testConvertTickToPosition(); testFindPositionNearGivenTick(); - testCalculateAccuracyRatio(); + testIsOnTheBeat(); testRoundUpToTheNearestMultiplier(); testGenerateAdjacentKeyPairs(); - testCalculateBeatBars(); return 0; }