diff --git a/resource_type/music.gd b/resource_type/music.gd index a9d8ce8..33547af 100644 --- a/resource_type/music.gd +++ b/resource_type/music.gd @@ -1,3 +1,5 @@ +## Class that packs an [AudioStream] with additional information +## to sync beats to it. class_name Music extends Resource ## Music AudioStream. diff --git a/rhythm_game/note/lane_view.gd b/rhythm_game/note/lane_view.gd new file mode 100644 index 0000000..b6f07db --- /dev/null +++ b/rhythm_game/note/lane_view.gd @@ -0,0 +1,21 @@ +## A NoteView that stores notes that exist on a lane. +class_name LaneView extends NoteView + +## The lane notes will be filtered by. +@export var lane: int + +## Get the array this LaneView refers to. +func get_data() -> NoteSubset: + return _lane_notes + +func update_current_beat(beat: float) -> void: + _current_beat = beat + _update_view_relative_to_notes(_lane_notes) + +# ======== Implementation ======== # +var _lane_notes: NoteSubset + +func _set_data(p_notes: NoteArray) -> void: + notes = p_notes + _lane_notes = NoteSubset.get_notes_in_lane(p_notes, lane) + _reset_view() diff --git a/rhythm_game/note/lane_view.gd.uid b/rhythm_game/note/lane_view.gd.uid new file mode 100644 index 0000000..79fa2f9 --- /dev/null +++ b/rhythm_game/note/lane_view.gd.uid @@ -0,0 +1 @@ +uid://cq818ut4pmunn diff --git a/rhythm_game/note.gd b/rhythm_game/note/note.gd similarity index 100% rename from rhythm_game/note.gd rename to rhythm_game/note/note.gd diff --git a/rhythm_game/note.gd.uid b/rhythm_game/note/note.gd.uid similarity index 100% rename from rhythm_game/note.gd.uid rename to rhythm_game/note/note.gd.uid diff --git a/rhythm_game/note/note_array.gd b/rhythm_game/note/note_array.gd new file mode 100644 index 0000000..22af288 --- /dev/null +++ b/rhythm_game/note/note_array.gd @@ -0,0 +1,44 @@ +## An array of notes, stored as arrays of data. +## Notes are assumed to be sorted in ascending order by beat. +class_name NoteArray extends Node + +## The ID of the note at [param index]. +func id_at(index: int) -> int: + return index + +## The beat of the note at [param index]. +func beat_at(index: int) -> float: + return _beats.get(index) + +## The type of the note at [param index]. +func type_at(index: int) -> Note.TYPE: + return _types.get(index) + +## The lane of the note at [param index]. +func lane_at(index: int) -> int: + return _lanes.get(index) + +## The number of notes in the array. +func size() -> int: + return _beats.size() + +## Remove all notes in the array. +func clear() -> void: + _beats = [] + _types = [] + _lanes = [] + +# ======= IMPLEMENTATION ======= # +var _beats: Array[float] = [] +var _types: Array[Note.TYPE] = [] +var _lanes: Array[int] = [] + +func _append_note(note: Note) -> void: + _beats.append(note.hit_beat) + _types.append(note.type) + _lanes.append(note.lane) + +func _append_note_data(beat: float, type: Note.TYPE, lane: int) -> void: + _beats.append(beat) + _types.append(type) + _lanes.append(lane) diff --git a/rhythm_game/note/note_array.gd.uid b/rhythm_game/note/note_array.gd.uid new file mode 100644 index 0000000..5413325 --- /dev/null +++ b/rhythm_game/note/note_array.gd.uid @@ -0,0 +1 @@ +uid://8isyo4pxyj1r diff --git a/rhythm_game/note/note_subset.gd b/rhythm_game/note/note_subset.gd new file mode 100644 index 0000000..9c1a22d --- /dev/null +++ b/rhythm_game/note/note_subset.gd @@ -0,0 +1,25 @@ +## Contain a subset of notes in a [NoteArray]. +## Notes are expected to be sorted in ascending order by beat. +## The original index of a note can be found with [method id_at]. +class_name NoteSubset extends NoteArray + +## The array where notes in this subset come from. +var full_set: NoteArray + +## The ID (index of note in full_set) of the note at [param index]. +func id_at(index) -> int: + return _ids.get(index) + +## Parse the given [param notes] and return a [class NoteSubset] +## containing only notes in [param lane]. +static func get_notes_in_lane(notes: NoteArray, lane: int) -> NoteSubset: + var lane_notes: NoteSubset = NoteSubset.new() + lane_notes.full_set = notes + for i in range(notes.size( )): + if notes.lane_at(i) == lane: + lane_notes._ids.append(i) + lane_notes.append_note_data(notes.beat_at(i), notes.type_at(i), lane) + return lane_notes + +## ====== IMPLEMENTATION ====== # +var _ids: Array[int] = [] diff --git a/rhythm_game/note/note_subset.gd.uid b/rhythm_game/note/note_subset.gd.uid new file mode 100644 index 0000000..8346727 --- /dev/null +++ b/rhythm_game/note/note_subset.gd.uid @@ -0,0 +1 @@ +uid://dhaxabkmyajjv diff --git a/rhythm_game/note/note_view.gd b/rhythm_game/note/note_view.gd new file mode 100644 index 0000000..09d8661 --- /dev/null +++ b/rhythm_game/note/note_view.gd @@ -0,0 +1,19 @@ +## Gets a range of notes in a NoteData relative to a current beat. +class_name NoteView extends View + +## The full set of notes. +@export var notes: NoteArray = null: set = _set_notes + +## Get the array this NoteView refers to. +func get_data() -> NoteArray: + return notes + +func update_current_beat(beat: float) -> void: + _current_beat = beat + _update_view_relative_to_notes(notes) + +# ======= IMPLEMENTATION ======= # + +func _set_notes(p_notes: NoteArray) -> void: + notes = p_notes + _reset_view() diff --git a/rhythm_game/note/note_view.gd.uid b/rhythm_game/note/note_view.gd.uid new file mode 100644 index 0000000..94967b4 --- /dev/null +++ b/rhythm_game/note/note_view.gd.uid @@ -0,0 +1 @@ +uid://c7h7ue6kjgoha diff --git a/rhythm_game/note/view.gd b/rhythm_game/note/view.gd new file mode 100644 index 0000000..d5dfcb4 --- /dev/null +++ b/rhythm_game/note/view.gd @@ -0,0 +1,68 @@ +## Abstract class representing a range of notes in some kind of array, +## expressed by a beginning index and an ending index: [begin, end). +@abstract class_name View extends Node + +## Get the array this View refers to. +@abstract func get_data() -> Variant + +## Update the view to be relative to beat. +## Can be connected to a beat update signal. +@abstract func update_current_beat(beat: float) -> void + +## Beginning of the range relative to te current beat where notes will be visible. +## Any notes where (hit_beat < current_beat + offset_begin) will NOT be visible. +@export var offset_begin: float = -4.0 + +## End of the range relative to the current beat where notes will be visible. +## Any notes where (hit_beat > current_beat + offset_end) will NOT be visible. +@export var offset_end: float = 4.0 + +## Return the index to the first element. +func begin() -> int: + return _begin + +## Return the index after the last element (equal to last index + 1). +func end() -> int: + return _end + +## Size of the range encompassed by begin() and end(). +func size() -> int: + return int(_begin >= 0) * _end - _begin + +# ======= IMPLEMENETATION ======= # +var _begin: int = -1 +var _end: int = -1 + +var _current_beat: float = -999 +var _previous_beat: float = -999 + +func _reset_view() -> void: + _begin = -1 + _end = -1 + _current_beat = -999 + _previous_beat = -999 + +## Update the view to match _current_beat. +func _update_view_relative_to_notes(notes: NoteArray) -> void: + var new_begin = _begin + var new_end = _end + if _current_beat > _previous_beat: + # Update forward. + while(new_begin < notes.size()) and (notes.beat_at(new_begin) < _current_beat + offset_begin): + new_begin += 1 + while(new_end < notes.size()) and (notes.beat_at(new_end) <= _current_beat + offset_end): + new_end += 1 + elif _current_beat < _previous_beat: + # Update backward. + while(new_begin >= 0) and (notes.beat_at(new_begin) >= _current_beat + offset_begin): + _begin -= 1 + while(new_end >= 0) and (notes.beat_at(new_end) > _current_beat + offset_end): + new_end -= 1 + if(new_begin != _begin): + new_begin += 1 + if(new_end != _end): + new_end += 1 + + _begin = new_begin + _end = new_end + _previous_beat = _current_beat diff --git a/rhythm_game/note/view.gd.uid b/rhythm_game/note/view.gd.uid new file mode 100644 index 0000000..61aefed --- /dev/null +++ b/rhythm_game/note/view.gd.uid @@ -0,0 +1 @@ +uid://drrwatuw1yur diff --git a/rhythm_game/note_layout.gd b/rhythm_game/note_layout.gd deleted file mode 100644 index a33f0a0..0000000 --- a/rhythm_game/note_layout.gd +++ /dev/null @@ -1,84 +0,0 @@ -#TODO Split class into two. -# One should be responsible for storing arrays of data, like a NoteData class? -# Another represents a slice of the data, NoteView. -# Before that, figure out how the part of a hold note between the hold start -# and hold end will be implemented. -class_name NoteLayout extends Node - -## Controls at what beat notes are visible. -@export_group("Note View Offset") -## At what beat offset from the current beat will notes will be visible. -## Example, spawn = 4.0 means notes are spawned 4 beats before -## they are meant to be hit. -@export var spawn: float = 4.0 - -## At what beat offset from the current beat will notes be invisible. -## Example, despawn = 4.0 means notes are despawned 4 beats after -## they are meant to be hit. -@export var despawn: float = 4.0 - - -## The index in notes of the first active note. -func start() -> int: - return _start - - -## The index in notes after the last active note. -func end() -> int: - return _end - - -func size() -> int: - return _beat.size() - - -func beat(index: int) -> float: - return _beat[index] - - -func lane(index: int) -> int: - return _lane[index] - - -func type(index: int) -> Note.TYPE: - return _type[index] - - -# ======= IMPLEMENTATION ======== # - -# All notes in the chart, arranged by ascending beat. -var _beat: Array[float] = [] -var _lane: Array[int] = [] -var _type: Array[Note.TYPE] = [] - -var _start: int = 0 -var _end: int = 0 - - -func _push_note(_note: Note) -> void: - pass - - -# TODO FIX THESE -func _update_forward(new_beat: float) -> void: - var spawn_beat = new_beat + spawn - - while _end < size() and _beat[_end] <= spawn_beat: - _end += 1 - - var despawn_beat = new_beat - despawn - - while _start < size() and _beat[_start] < despawn_beat: - _start += 1 - -# TODO FIX THESE -#func _update_backward(new_beat: float) -> void: - #var spawn_beat = new_beat + note_spawn_offset - # - #while _end >= 0 and _beat[_end] > spawn_beat: - #_end -= 1 - # - #var despawn_beat = new_beat - note_despawn_offset - # - #while _start >= 0 and _beat[_start] < despawn_beat: - #_start -= 1 diff --git a/rhythm_game/note_layout.gd.uid b/rhythm_game/note_layout.gd.uid deleted file mode 100644 index 9fa00b2..0000000 --- a/rhythm_game/note_layout.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dlnnbx2wvn66t diff --git a/rhythm_game/rhythm_game.tscn b/rhythm_game/rhythm_game.tscn index 9d485e8..2578998 100644 --- a/rhythm_game/rhythm_game.tscn +++ b/rhythm_game/rhythm_game.tscn @@ -1,6 +1,5 @@ -[gd_scene load_steps=10 format=3 uid="uid://dwro2d0482v0p"] +[gd_scene load_steps=11 format=3 uid="uid://dwro2d0482v0p"] -[ext_resource type="Script" uid="uid://dlnnbx2wvn66t" path="res://rhythm_game/note_layout.gd" id="1_cr5rn"] [ext_resource type="Script" uid="uid://102cl75cfpgw" path="res://rhythm_game/music_sync/event_layout.gd" id="1_jnfl3"] [ext_resource type="Script" uid="uid://s16dt0bu0jrg" path="res://rhythm_game/music_sync/conductor.gd" id="2_62aw1"] [ext_resource type="AudioStream" uid="uid://btmy8ffph5gn3" path="res://chart/test_nibelungen/audio.mp3" id="3_txi6k"] @@ -8,6 +7,8 @@ [ext_resource type="Script" uid="uid://rg6orh6kutai" path="res://resource_type/music.gd" id="5_nsyv8"] [ext_resource type="AudioStream" uid="uid://be8dyt7nfpffw" path="res://sfx/sfx_cowbell.ogg" id="6_ecbku"] [ext_resource type="Script" uid="uid://bbpyym0kgujev" path="res://rhythm_game/metronome.gd" id="7_nsyv8"] +[ext_resource type="Script" uid="uid://8isyo4pxyj1r" path="res://rhythm_game/note/note_array.gd" id="9_10cpq"] +[ext_resource type="Script" uid="uid://c7h7ue6kjgoha" path="res://rhythm_game/note/note_view.gd" id="9_74aio"] [sub_resource type="Resource" id="Resource_10cpq"] script = ExtResource("5_nsyv8") @@ -25,13 +26,18 @@ autostart = true music = SubResource("Resource_10cpq") metadata/_custom_type_script = "uid://s16dt0bu0jrg" -[node name="NoteLayout" type="Node" parent="."] -script = ExtResource("1_cr5rn") -metadata/_custom_type_script = "uid://dlnnbx2wvn66t" - [node name="Metronome" type="AudioStreamPlayer" parent="."] stream = ExtResource("6_ecbku") script = ExtResource("7_nsyv8") metadata/_custom_type_script = "uid://bbpyym0kgujev" +[node name="AllChartNotes" type="Node" parent="."] +script = ExtResource("9_10cpq") +metadata/_custom_type_script = "uid://8isyo4pxyj1r" + +[node name="VisibleNotes" type="Node" parent="." node_paths=PackedStringArray("notes")] +script = ExtResource("9_74aio") +notes = NodePath("../AllChartNotes") +metadata/_custom_type_script = "uid://c7h7ue6kjgoha" + [connection signal="ticked" from="Conductor" to="Metronome" method="on_conductor_ticked"]