Compare commits

...

11 Commits

Author SHA1 Message Date
56bc5d1ba5 Configure rhythm game scene. 2026-02-04 00:07:58 +08:00
501ff6a7dc Add nodes for creating note arrays in-editor. 2026-02-04 00:07:13 +08:00
dbf6a710a3 Fix note pool. 2026-02-04 00:05:33 +08:00
06a0424fd0 Implement lane update. 2026-02-04 00:04:55 +08:00
176465e067 Complete note spawner implementation. 2026-02-04 00:04:17 +08:00
fa0c53ac3e Implement note visuals update method. 2026-02-04 00:03:12 +08:00
6ac9be6c79 Fix swapped tap and hold note scenes. 2026-02-04 00:01:27 +08:00
753a2b1030 Add global scroll speed setting. 2026-02-04 00:00:52 +08:00
7bc4246375 Implement note spawner. 2026-02-01 23:17:51 +08:00
3a9d3046b2 Update note visuals. 2026-02-01 23:14:52 +08:00
72f9b8eea5 Make NoteScenes class abstract. 2026-02-01 23:07:05 +08:00
20 changed files with 362 additions and 92 deletions

View File

@@ -4,6 +4,7 @@ class_name Lane extends Node2D
func get_hit_pos() -> Vector2: func get_hit_pos() -> Vector2:
return position return position
## Should be connected to [signal NoteSpawner.notes_spawned].
func update(_beat: BeatUpdate) -> void: func update(beat: float) -> void:
pass for note: NoteVisual in get_children():
note.update(beat)

View File

@@ -0,0 +1,5 @@
class_name EditorNote extends Node
@export var hit_beat: float = 0.0
@export var type: Note.TYPE = Note.TYPE.TAP
@export var lane: int = 0

View File

@@ -0,0 +1 @@
uid://cgif6nuped1v1

View File

@@ -0,0 +1,10 @@
class_name EditorNoteArray extends NoteArray
func _ready() -> void:
for note: EditorNote in get_children():
if note == null:
return
# TODO: Make this insert sorted.
_beats.append(note.hit_beat)
_types.append(note.type)
_lanes.append(note.lane)

View File

@@ -0,0 +1 @@
uid://b1x25i77v5nag

View File

@@ -8,9 +8,9 @@ class_name LaneView extends NoteView
func get_data() -> NoteSubset: func get_data() -> NoteSubset:
return _lane_notes return _lane_notes
func update_current_beat(beat: float) -> void: func update(beat: float) -> void:
_current_beat = beat _update_view_relative_to_notes(_lane_notes, beat)
_update_view_relative_to_notes(_lane_notes) _previous_beat = beat
# ======== Implementation ======== # # ======== Implementation ======== #
var _lane_notes: NoteSubset var _lane_notes: NoteSubset
@@ -18,4 +18,4 @@ var _lane_notes: NoteSubset
func _set_data(p_notes: NoteArray) -> void: func _set_data(p_notes: NoteArray) -> void:
notes = p_notes notes = p_notes
_lane_notes = NoteSubset.get_notes_in_lane(p_notes, lane) _lane_notes = NoteSubset.get_notes_in_lane(p_notes, lane)
_reset_view() reset_view()

View File

@@ -8,12 +8,41 @@ class_name NoteView extends View
func get_data() -> NoteArray: func get_data() -> NoteArray:
return notes return notes
func update_current_beat(beat: float) -> void: func update(beat: float) -> void:
_current_beat = beat _update_view_relative_to_notes(notes, beat)
_update_view_relative_to_notes(notes) _previous_beat = beat
func reset_view() -> void:
super.reset_view()
_previous_beat = -999.0
# ======= IMPLEMENTATION ======= # # ======= IMPLEMENTATION ======= #
var _previous_beat: float = -999.0
func _set_notes(p_notes: NoteArray) -> void: func _set_notes(p_notes: NoteArray) -> void:
notes = p_notes notes = p_notes
_reset_view() reset_view()
## Update the view to match _current_beat.
func _update_view_relative_to_notes(p_notes: NoteArray, new_beat: float) -> void:
var new_begin = _begin
var new_end = _end
if new_beat > _previous_beat:
# Update forward.
while(new_begin < p_notes.size()) and (p_notes.beat_at(new_begin) < new_beat + offset_begin):
new_begin += 1
while(new_end < p_notes.size()) and (p_notes.beat_at(new_end) <= new_beat + offset_end):
new_end += 1
elif new_beat < _previous_beat:
# Update backward.
while(new_begin >= 0) and (p_notes.beat_at(new_begin) >= new_beat + offset_begin):
_begin -= 1
while(new_end >= 0) and (p_notes.beat_at(new_end) > new_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

View File

@@ -7,7 +7,12 @@
## Update the view to be relative to beat. ## Update the view to be relative to beat.
## Can be connected to a beat update signal. ## Can be connected to a beat update signal.
@abstract func update_current_beat(beat: float) -> void @abstract func update(beat: float) -> void
## Reset the view.
func reset_view() -> void:
_begin = -1
_end = -1
## Beginning of the range relative to te current beat where notes will be visible. ## 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. ## Any notes where (hit_beat < current_beat + offset_begin) will NOT be visible.
@@ -32,37 +37,3 @@ func size() -> int:
# ======= IMPLEMENETATION ======= # # ======= IMPLEMENETATION ======= #
var _begin: int = -1 var _begin: int = -1
var _end: 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

View File

@@ -1,4 +1,41 @@
class_name HoldNote extends NoteVisual class_name HoldNote extends NoteVisual
@export var start: Node2D
@export var end: Node2D
var start_id: int var start_id: int
var end_id: int var end_id: int
var start_beat: float
var end_beat: float
func reset() -> void:
start_id = -1
end_id = -1
start_beat = -999.0
end_beat = -999.0
func in_use() -> bool:
return start_id >= 0 or end_id >= 0
func update(beat: float) -> void:
var start_scroll: float
if start_beat:
start_scroll = _calculate_scroll(start_beat, beat)
else:
start_scroll = 999.0 # Decently up. (TODO Replace with offset_begin).
_set_start_scroll(start_scroll)
var end_scroll: float
if end_beat:
end_scroll = _calculate_scroll(end_beat, beat)
else:
end_scroll = -999.0 # Decently down. (TODO Replace with offset_end).
_set_end_scroll(end_scroll)
# ======== IMPLEMENTATION ======== #
func _set_start_scroll(scroll: float) -> void:
start.y = scroll
func _set_end_scroll(scroll: float) -> void:
end.y = scroll

View File

@@ -1,3 +1,41 @@
[gd_scene format=3 uid="uid://dq5ocf0272tet"] [gd_scene format=3 uid="uid://dq5ocf0272tet"]
[node name="HoldNote" type="Node2D" unique_id=495232990] [ext_resource type="Script" uid="uid://cbsnb8bdby0d" path="res://rhythm_game/note/visual/hold/hold_note.gd" id="1_5cv34"]
[node name="HoldNote" type="Node2D" unique_id=320259211 node_paths=PackedStringArray("start", "end")]
script = ExtResource("1_5cv34")
start = NodePath("NoteStart")
end = NodePath("NoteEnd")
metadata/_custom_type_script = "uid://cbsnb8bdby0d"
[node name="NoteStart" type="Node2D" parent="." unique_id=1911676927]
[node name="ColorRect" type="ColorRect" parent="NoteStart" unique_id=1232591431]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -29.0
offset_top = -6.0
offset_right = 29.0
offset_bottom = 6.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.93, 0, 0, 1)
[node name="NoteEnd" type="Node2D" parent="." unique_id=2007025807]
[node name="ColorRect" type="ColorRect" parent="NoteEnd" unique_id=1179831333]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -29.0
offset_top = -6.0
offset_right = 29.0
offset_bottom = 6.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.93, 0, 0, 1)

View File

@@ -1,27 +0,0 @@
class_name NoteAnimator extends NoteView
@export var pool: NotePool
## TODO WARNING: This is currently only implemented for beat updates
## going forward in time.
func update_current_beat(beat: float) -> void:
var old_begin: int = _begin
var old_end: int = _end
super.update_current_beat(beat) # Update _begin and _end.
_update_note_visuals(old_begin, _begin)
_update_note_visuals(old_end, _end)
# ======== IMPLEMENTATION ======== #
var _note_map: Dictionary[int, NoteVisual]
func _update_note_visuals(index_begin: int, index_end: int) -> void:
for i: int in range(index_begin, index_end):
# TODO: Either get or return node from pool,
# depending on if index_begin > or < than index_end.
pass
func _set_notes(p_notes: NoteArray) -> void:
notes = p_notes
_reset_view()

View File

@@ -15,9 +15,21 @@ class_name NotePool extends Node
@export var hold_skin: StringName = "default" @export var hold_skin: StringName = "default"
func get_note(type: NoteVisual.TYPE) -> NoteVisual: func get_note(type: NoteVisual.TYPE) -> NoteVisual:
var note: NoteVisual match type:
# TODO: Implement. NoteVisual.TYPE.TAP:
return note return get_tap()
NoteVisual.TYPE.HOLD:
return get_hold()
_:
return null
func return_note(note: NoteVisual) -> void:
if note is TapNote:
note.reset()
return_tap(note as TapNote)
elif note is HoldNote:
note.reset()
return_hold(note as HoldNote)
## Get a tap note. Instantiates one if no free nodes are available. ## Get a tap note. Instantiates one if no free nodes are available.
func get_tap() -> TapNote: func get_tap() -> TapNote:
@@ -25,6 +37,7 @@ func get_tap() -> TapNote:
if tap == null: if tap == null:
tap = _instantiate_tap() tap = _instantiate_tap()
_used_taps[tap] = true _used_taps[tap] = true
tap.reset()
return tap return tap
## Return a tap note. ## Return a tap note.
@@ -38,6 +51,7 @@ func return_tap(tap: TapNote) -> void:
"Returning tap note ", tap, "Returning tap note ", tap,
" that was not spawned by pool ", self, "." " that was not spawned by pool ", self, "."
) )
tap.reset()
_free_taps.append(tap) _free_taps.append(tap)
## Get a hold note. Instantiates one if no free nodes are available. ## Get a hold note. Instantiates one if no free nodes are available.
@@ -46,6 +60,7 @@ func get_hold() -> HoldNote:
if hold == null: if hold == null:
hold = _instantiate_hold() hold = _instantiate_hold()
_used_holds[hold] = true _used_holds[hold] = true
hold.reset()
return hold return hold
## Return a hold note. ## Return a hold note.
@@ -59,6 +74,7 @@ func return_hold(hold: HoldNote) -> void:
"Returning hold note ", hold, "Returning hold note ", hold,
" that was not spawned by pool ", self, "." " that was not spawned by pool ", self, "."
) )
hold.reset()
_free_holds.append(hold) _free_holds.append(hold)
## Spawn the given number of notes. ## Spawn the given number of notes.
@@ -77,8 +93,11 @@ var _used_taps: Dictionary[TapNote, bool]
var _free_holds: Array[HoldNote] var _free_holds: Array[HoldNote]
var _used_holds: Dictionary[HoldNote, bool] var _used_holds: Dictionary[HoldNote, bool]
func _ready() -> void:
reserve(tap_size, hold_size)
func _instantiate_tap() -> TapNote: func _instantiate_tap() -> TapNote:
return NoteScenes.get_tap(tap_skin).instantiate() as TapNote return NoteScenes.get_tap(tap_skin).instantiate() as TapNote
func _instantiate_hold() -> HoldNote: func _instantiate_hold() -> HoldNote:
return NoteScenes.get_tap(tap_skin).instantiate() as HoldNote return NoteScenes.get_hold(tap_skin).instantiate() as HoldNote

View File

@@ -1,5 +1,5 @@
## Static class that stores references to note scenes. ## Static class that stores references to note scenes.
class_name NoteScenes extends Object @abstract class_name NoteScenes extends Object
static func get_tap(skin: StringName = "default") -> PackedScene: static func get_tap(skin: StringName = "default") -> PackedScene:
if _tap_scenes.has(skin): if _tap_scenes.has(skin):
@@ -18,9 +18,9 @@ static func get_hold(skin: StringName = "default") -> PackedScene:
# ======== IMPLEMENTATION ======== # # ======== IMPLEMENTATION ======== #
static var _tap_scenes: Dictionary[StringName, PackedScene] = { static var _tap_scenes: Dictionary[StringName, PackedScene] = {
"default": load("uid://dq5ocf0272tet") "default": load("uid://jbgxbhfpj806")
} }
static var _hold_scenes: Dictionary[StringName, PackedScene] = { static var _hold_scenes: Dictionary[StringName, PackedScene] = {
"default": load("uid://jbgxbhfpj806") "default": load("uid://dq5ocf0272tet")
} }

View File

@@ -0,0 +1,108 @@
class_name NoteSpawner extends NoteView
signal notes_spawned(beat: float)
@export var lanes: Dictionary[int, Lane]
@export var pool: NotePool
## Spawn all the notes that should exist on this beat.
func update(beat: float) -> void:
var old_begin: int = _begin
var old_end: int = _end
_update_view_relative_to_notes(notes, beat) # Updates _begin and _end.
if _previous_beat <= beat:
_set_note_visuals(old_end, _end)
_remove_note_visuals(old_begin, _begin)
else:
_remove_note_visuals(_end, old_end)
_set_note_visuals(_begin, old_begin)
_previous_beat = beat
notes_spawned.emit(beat)
# ======== IMPLEMENTATION ======== #
var _note_visuals: Dictionary[int, NoteVisual] # [Note ID, Note Visual]
var _last_lane_holds: Dictionary[int, HoldNote]
func _set_notes(p_notes: NoteArray) -> void:
super._set_notes(p_notes)
for note in _note_visuals.values():
if note == null:
continue
pool.return_note(note)
# TODO Finish implementation.
# Spawn or delete nodes in the range.
func _set_note_visuals(index_begin: int, index_end: int) -> void:
for note_id: int in range(index_begin, index_end):
match notes.type_at(note_id):
Note.TYPE.TAP:
_set_visual_for_tap_at(note_id)
Note.TYPE.HOLD_START:
_set_visual_for_hold_start_at(note_id)
Note.TYPE.HOLD_END:
_set_visual_for_hold_end_at(note_id)
func _remove_note_visuals(index_begin: int, index_end: int) -> void:
for note_id: int in range(index_begin, index_end):
match notes.type_at(note_id):
Note.TYPE.TAP:
_remove_visual_for_tap_at(note_id)
Note.TYPE.HOLD_START:
_remove_visual_for_hold_start_at(note_id)
Note.TYPE.HOLD_END:
_remove_visual_for_hold_end_at(note_id)
func _set_visual_for_tap_at(note_id: int) -> void:
var lane_id: int = notes.lane_at(note_id)
var lane: Lane = lanes[lane_id]
var note: TapNote = pool.get_tap()
note.id = note_id
note.hit_beat = notes.beat_at(note_id)
lane.add_child(note)
_note_visuals[note_id] = note
func _set_visual_for_hold_start_at(note_id: int) -> void:
var lane_id: int = notes.lane_at(note_id)
var lane: Lane = lanes[lane_id]
var note: HoldNote = pool.get_hold()
note.start_id = note_id
note.hit_beat = notes.beat_at(note_id)
lane.add_child(note)
_note_visuals[note_id] = note
_last_lane_holds[lane_id] = note
func _set_visual_for_hold_end_at(note_id: int) -> void:
var lane_id: int = notes.lane_at(note_id)
var lane: Lane = lanes[lane_id]
var note: HoldNote = _last_lane_holds[lane_id]
if note == null:
note = pool.get_hold()
lane.add_child(note)
_note_visuals[note_id] = note
_last_lane_holds[lane_id] = null
func _remove_visual_for_tap_at(note_id: int) -> void:
var note: TapNote = _note_visuals.get(note_id) as TapNote
if note == null:
return
pool.return_tap(note)
func _remove_visual_for_hold_start_at(note_id: int) -> void:
var note: HoldNote = _note_visuals.get(note_id) as HoldNote
if note == null:
return
note.start_id = -1
if note.end_id < 0:
pool.return_hold(note)
func _remove_visual_for_hold_end_at(note_id: int) -> void:
var note: HoldNote = _note_visuals.get(note_id) as HoldNote
if note == null:
return
note.end_id = -1
if note.start_id < 0:
pool.return_hold(note)

View File

@@ -8,3 +8,18 @@ enum TYPE {
@abstract func reset() -> void @abstract func reset() -> void
@abstract func in_use() -> bool @abstract func in_use() -> bool
@abstract func update(beat: float) -> void
# ======= IMPLEMENTATION ======= #
var _lane: Lane = null
func _notification(what: int) -> void:
match what:
NOTIFICATION_PARENTED:
_lane = get_parent() as Lane
NOTIFICATION_UNPARENTED:
_lane = null
static func _calculate_scroll(target_beat: float, current_beat: float) -> float:
return Settings.get_note_scroll_speed() * (current_beat - target_beat)

View File

@@ -1,9 +1,16 @@
class_name TapNote extends NoteVisual class_name TapNote extends NoteVisual
var hit_beat: float = -999.0
var id: int = -1 var id: int = -1
func reset() -> void: func reset() -> void:
id = -1 id = -1
hit_beat = -999.0
position = Vector2(0, 9999.0)
func in_use() -> bool: func in_use() -> bool:
return id >= 0 return id >= 0
func update(beat: float) -> void:
position.y = _calculate_scroll(hit_beat, beat)

View File

@@ -2,14 +2,17 @@
[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://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="Script" uid="uid://s16dt0bu0jrg" path="res://rhythm_game/music_sync/conductor.gd" id="2_62aw1"]
[ext_resource type="Script" uid="uid://dpu6645p40p43" path="res://rhythm_game/note/visual/note_spawner.gd" id="2_nuoj2"]
[ext_resource type="AudioStream" uid="uid://btmy8ffph5gn3" path="res://chart/test_nibelungen/audio.mp3" id="3_txi6k"] [ext_resource type="AudioStream" uid="uid://btmy8ffph5gn3" path="res://chart/test_nibelungen/audio.mp3" id="3_txi6k"]
[ext_resource type="Script" uid="uid://u42y08gn6bi7" path="res://rhythm_game/lane/lane.gd" id="4_obbjr"]
[ext_resource type="Resource" uid="uid://b34uhnkvwfyc1" path="res://chart/test_nibelungen/tempo.tres" id="5_10cpq"] [ext_resource type="Resource" uid="uid://b34uhnkvwfyc1" path="res://chart/test_nibelungen/tempo.tres" id="5_10cpq"]
[ext_resource type="Script" uid="uid://rg6orh6kutai" path="res://resource_type/music.gd" id="5_nsyv8"] [ext_resource type="Script" uid="uid://rg6orh6kutai" path="res://resource_type/music.gd" id="5_nsyv8"]
[ext_resource type="Script" uid="uid://cgif6nuped1v1" path="res://rhythm_game/note/editor/editor_note.gd" id="5_wq11w"]
[ext_resource type="AudioStream" uid="uid://be8dyt7nfpffw" path="res://sfx/sfx_cowbell.ogg" id="6_ecbku"] [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://bbpyym0kgujev" path="res://rhythm_game/metronome.gd" id="7_nsyv8"]
[ext_resource type="Script" uid="uid://8isyo4pxyj1r" path="res://rhythm_game/note/array/note_array.gd" id="9_10cpq"]
[ext_resource type="Script" uid="uid://c7h7ue6kjgoha" path="res://rhythm_game/note/view/note_view.gd" id="9_74aio"] [ext_resource type="Script" uid="uid://c7h7ue6kjgoha" path="res://rhythm_game/note/view/note_view.gd" id="9_74aio"]
[ext_resource type="Script" uid="uid://clog51kfg2rsb" path="res://rhythm_game/note/visual/note_pool.gd" id="10_74aio"] [ext_resource type="Script" uid="uid://clog51kfg2rsb" path="res://rhythm_game/note/visual/note_pool.gd" id="10_74aio"]
[ext_resource type="Script" uid="uid://b1x25i77v5nag" path="res://rhythm_game/note/editor/editor_note_array.gd" id="12_obbjr"]
[sub_resource type="Resource" id="Resource_10cpq"] [sub_resource type="Resource" id="Resource_10cpq"]
script = ExtResource("5_nsyv8") script = ExtResource("5_nsyv8")
@@ -21,6 +24,57 @@ metadata/_custom_type_script = "uid://rg6orh6kutai"
[node name="RhythmGame" type="Node2D" unique_id=1479619523] [node name="RhythmGame" type="Node2D" unique_id=1479619523]
script = ExtResource("1_jnfl3") script = ExtResource("1_jnfl3")
[node name="NoteSpawner" type="Node" parent="." unique_id=885666554 node_paths=PackedStringArray("lanes", "pool", "notes")]
script = ExtResource("2_nuoj2")
lanes = {
0: NodePath("../Lane0"),
1: NodePath("../Lane1"),
2: NodePath("../Lane2"),
3: NodePath("../Lane3")
}
pool = NodePath("NotePool")
notes = NodePath("Notes")
metadata/_custom_type_script = "uid://dpu6645p40p43"
[node name="NotePool" type="Node" parent="NoteSpawner" unique_id=621311008]
script = ExtResource("10_74aio")
metadata/_custom_type_script = "uid://clog51kfg2rsb"
[node name="Notes" type="Node" parent="NoteSpawner" unique_id=936018806]
script = ExtResource("12_obbjr")
[node name="Note1" type="Node" parent="NoteSpawner/Notes" unique_id=577206015]
script = ExtResource("5_wq11w")
[node name="Note2" type="Node" parent="NoteSpawner/Notes" unique_id=1051280322]
script = ExtResource("5_wq11w")
hit_beat = 5.0
[node name="Note3" type="Node" parent="NoteSpawner/Notes" unique_id=1001463669]
script = ExtResource("5_wq11w")
hit_beat = 6.0
lane = 2
[node name="Lane0" type="Node2D" parent="." unique_id=178768598]
position = Vector2(394, 308)
script = ExtResource("4_obbjr")
metadata/_custom_type_script = "uid://u42y08gn6bi7"
[node name="Lane1" type="Node2D" parent="." unique_id=1477988197]
position = Vector2(516, 312)
script = ExtResource("4_obbjr")
metadata/_custom_type_script = "uid://u42y08gn6bi7"
[node name="Lane2" type="Node2D" parent="." unique_id=1586197762]
position = Vector2(634, 311)
script = ExtResource("4_obbjr")
metadata/_custom_type_script = "uid://u42y08gn6bi7"
[node name="Lane3" type="Node2D" parent="." unique_id=1689399295]
position = Vector2(778, 313)
script = ExtResource("4_obbjr")
metadata/_custom_type_script = "uid://u42y08gn6bi7"
[node name="Conductor" type="Node" parent="." unique_id=1726392948] [node name="Conductor" type="Node" parent="." unique_id=1726392948]
script = ExtResource("2_62aw1") script = ExtResource("2_62aw1")
autostart = true autostart = true
@@ -32,17 +86,8 @@ stream = ExtResource("6_ecbku")
script = ExtResource("7_nsyv8") script = ExtResource("7_nsyv8")
metadata/_custom_type_script = "uid://bbpyym0kgujev" metadata/_custom_type_script = "uid://bbpyym0kgujev"
[node name="AllChartNotes" type="Node" parent="." unique_id=2044386757] [node name="VisibleNotes" type="Node" parent="." unique_id=149671562]
script = ExtResource("9_10cpq")
metadata/_custom_type_script = "uid://8isyo4pxyj1r"
[node name="VisibleNotes" type="Node" parent="." unique_id=149671562 node_paths=PackedStringArray("notes")]
script = ExtResource("9_74aio") script = ExtResource("9_74aio")
notes = NodePath("../AllChartNotes")
metadata/_custom_type_script = "uid://c7h7ue6kjgoha" metadata/_custom_type_script = "uid://c7h7ue6kjgoha"
[node name="NotePool" type="Node" parent="." unique_id=621311008]
script = ExtResource("10_74aio")
metadata/_custom_type_script = "uid://clog51kfg2rsb"
[connection signal="ticked" from="Conductor" to="Metronome" method="on_conductor_ticked"] [connection signal="ticked" from="Conductor" to="Metronome" method="on_conductor_ticked"]

9
rhythm_game/settings.gd Normal file
View File

@@ -0,0 +1,9 @@
@abstract class_name Settings extends Object
## World coordinate units (pixels) per beat.
static func get_note_scroll_speed() -> float:
return _note_scroll_speed
# ======== IMPLEMENTATION ======== #
static var _note_scroll_speed: float = 20.0

View File

@@ -0,0 +1 @@
uid://k0no5veq8xco