Implement note spawner.

This commit is contained in:
2026-02-01 23:17:51 +08:00
parent 3a9d3046b2
commit 7bc4246375
8 changed files with 151 additions and 75 deletions

View File

@@ -4,6 +4,6 @@ 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 pass

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,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:

View File

@@ -0,0 +1,91 @@
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()
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()
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:
pass
func _remove_visual_for_hold_start_at(note_id: int) -> void:
pass
func _remove_visual_for_hold_end_at(note_id: int) -> void:
pass