Last active
June 25, 2024 21:01
-
-
Save amirrajan/e80e0645b7079259f8a4c7c5ec591449 to your computer and use it in GitHub Desktop.
DragonRuby Game Toolkit - Rhythm Based Game
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def tick args | |
defaults args | |
tick_audio args | |
tick_calibration args | |
if args.state.tick_count > args.state.start_playing_on_tick | |
args.state.beat_accumulator += args.state.beats_per_tick | |
args.state.quarter_beat = args.state.beat_accumulator.to_i | |
args.state.previous_quarter_beat ||= args.state.quarter_beat | |
end | |
if args.state.previous_quarter_beat != args.state.quarter_beat | |
args.state.previous_quarter_beat_at = args.state.quarter_beat | |
args.state.quarter_beat_occurred_at = args.state.tick_count | |
end | |
if (Kernel.tick_count - args.state.quarter_beat_occurred_at + args.state.calibration_ticks).abs == 0 | |
args.state.fx_queue << { x: 640, | |
y: 360, | |
w: 100, | |
h: 100, | |
r: 255, | |
anchor_x: 0.5, | |
anchor_y: 0.5, | |
g: 0, | |
b: 0, | |
a: 255, | |
path: :solid } | |
args.state.fx_queue << { x: 640, | |
y: 360, | |
w: 100, | |
h: 100, | |
r: 255, | |
anchor_x: 0.5, | |
anchor_y: 0.5, | |
g: 0, | |
b: 0, | |
a: 255, | |
d_size: 20, | |
path: :solid } | |
end | |
if args.inputs.keyboard.key_down.space | |
input_diff = (Kernel.tick_count - args.state.quarter_beat_occurred_at + args.state.calibration_ticks) | |
if input_diff.abs <= 1 | |
args.state.label_fx_queue << { x: 640, | |
y: 360, | |
anchor_x: 0.5, | |
anchor_y: 0.5, | |
text: "perfect! (#{input_diff})" } | |
elsif input_diff.abs <= 3 | |
args.state.label_fx_queue << { x: 640, | |
y: 360, | |
anchor_x: 0.5, | |
anchor_y: 0.5, | |
text: "great! (#{input_diff})" } | |
elsif input_diff.abs <= 5 | |
args.state.label_fx_queue << { x: 640, | |
y: 360, | |
anchor_x: 0.5, | |
anchor_y: 0.5, | |
text: "okay... (#{input_diff})" } | |
else | |
args.state.label_fx_queue << { x: 640, | |
y: 360, | |
anchor_x: 0.5, | |
anchor_y: 0.5, | |
text: "bad :-( (#{input_diff})" } | |
end | |
end | |
calc_fx_queues args | |
render args | |
end | |
def defaults args | |
args.state.track_length_in_ticks ||= 2057 | |
args.state.main_track ||= :track_1 | |
args.state.other_track ||= :track_2 | |
args.state.fx_queue ||= [] | |
args.state.label_fx_queue ||= [] | |
args.state.play_head ||= 0 | |
args.state.start_playing_on_tick ||= 180 | |
args.state.beats_per_minute ||= 140 | |
args.state.beats_per_second ||= args.state.beats_per_minute / 60.0 | |
args.state.beats_per_tick ||= args.state.beats_per_second / 60.0 | |
args.state.beat_accumulator ||= 0 | |
args.state.quarter_beat ||= 0 | |
args.state.calibration_ticks ||= 0 | |
args.state.quarter_beat_interval ||= 1.fdiv(args.state.beats_per_tick).to_i | |
args.state.quarter_beat_inputs ||= 0 | |
args.state.quarter_beat_diff_history ||= [] | |
end | |
def tick_audio args | |
return if args.state.tick_count < args.state.start_playing_on_tick | |
# start up audio | |
args.audio[:track_1] ||= { | |
input: "sounds/music.ogg", | |
gain: 1.0, | |
looping: false | |
} | |
args.audio[:track_2] ||= { | |
input: "sounds/music.ogg", | |
looping: false, | |
gain: 0.0 | |
} | |
# play head increment every tick | |
args.state.play_head += 1 | |
args.state.play_head = args.state.play_head % args.state.track_length_in_ticks | |
# every 10 seconds, cross fade | |
if args.state.play_head.zmod?(600) && args.state.tick_count > args.state.start_playing_on_tick | |
if args.state.main_track == :track_1 | |
args.state.main_track = :track_2 | |
args.state.other_track = :track_1 | |
else | |
args.state.main_track = :track_1 | |
args.state.other_track = :track_2 | |
end | |
if args.audio[args.state.main_track] | |
args.audio[args.state.main_track].playtime = args.state.play_head.idiv(60) | |
end | |
end | |
# perform cross fade | |
if args.audio[args.state.main_track] | |
args.audio[args.state.main_track].gain += 0.1 | |
args.audio[args.state.main_track].gain = 1.0 if args.audio[args.state.main_track].gain > 1.0 | |
end | |
if args.audio[args.state.other_track] | |
args.audio[args.state.other_track].gain -= 0.1 | |
args.audio[args.state.other_track].gain = 0.0 if args.audio[args.state.other_track].gain < 0.0 | |
end | |
end | |
def tick_calibration args | |
if args.inputs.keyboard.key_down.up | |
args.state.calibration_ticks += 1 | |
elsif args.inputs.keyboard.key_down.down | |
args.state.calibration_ticks -= 1 | |
end | |
if args.inputs.keyboard.key_down.m | |
args.state.player_beat_at = Kernel.tick_count | |
else | |
args.state.player_beat_at = nil | |
end | |
if args.state.player_beat_at && args.state.quarter_beat_occurred_at | |
diff = args.state.player_beat_at - args.state.quarter_beat_occurred_at | |
description = if (diff + args.state.calibration_ticks) < 0 | |
"early: increase calibration value" | |
elsif (diff + args.state.calibration_ticks) > 0 | |
"late: decrease calibration value" | |
else | |
"perfect" | |
end | |
quarter_beat_diff = { diff: (diff + args.state.calibration_ticks), description: description } | |
args.state.quarter_beat_diff_history.unshift quarter_beat_diff.copy | |
if args.state.quarter_beat_diff_history.length > 20 | |
args.state.quarter_beat_diff_history = args.state.quarter_beat_diff_history.take 20 | |
end | |
end | |
end | |
def calc_fx_queues args | |
args.state.fx_queue.each do |fx| | |
fx.at ||= args.state.tick_count | |
fx.d_size ||= 0 | |
fx.w += fx.d_size | |
fx.h += fx.d_size | |
end | |
args.state.fx_queue.reject! { |fx| fx.at.elapsed_time > 5 } | |
args.state.label_fx_queue.each do |fx| | |
fx.at ||= args.state.tick_count | |
fx.a ||= 255 | |
fx.y = fx.y.lerp(540, 0.1) | |
fx.a -= 5 | |
end | |
args.state.label_fx_queue.reject! { |fx| fx.a <= 0 } | |
end | |
def render args | |
if args.state.tick_count < args.state.start_playing_on_tick | |
args.outputs.labels << { x: 640, | |
y: 360, | |
text: "Count down: #{(args.state.start_playing_on_tick - args.state.tick_count).idiv(60) + 1}", | |
anchor_x: 0.5, | |
anchor_y: 0.5 } | |
end | |
args.outputs.borders << { x: 640, y: 360, w: 100, h: 100, | |
anchor_x: 0.5, anchor_y: 0.5, | |
r: 255, g: 0, b: 0, a: 255 } | |
args.outputs.primitives << args.state.fx_queue | |
args.outputs.primitives << args.state.label_fx_queue | |
args.state.previous_quarter_beat = args.state.quarter_beat | |
args.outputs.debug.watch "instructions: close your eyes and listen to the beat and press 'M' on quarter beats. Press 'UP' or 'DOWN' to adjust calibration_ticks." | |
args.outputs.debug.watch " press 'SPACE' on quarter beats to test calibration." | |
if args.audio[:track_1] && args.audio[:track_2] | |
args.outputs.debug.watch "track_1 gain: #{args.audio[:track_1].gain.to_sf}" | |
args.outputs.debug.watch "track_2 gain: #{args.audio[:track_2].gain.to_sf}" | |
end | |
args.outputs.debug.watch "beat accumulator: #{args.state.beat_accumulator.to_sf}" | |
args.outputs.debug.watch "quarter beat: #{args.state.quarter_beat}" | |
args.outputs.debug.watch "calibration_ticks: #{args.state.calibration_ticks.to_i}" | |
args.state.quarter_beat_diff_history.each do |item| | |
if item.diff >= 0 | |
args.outputs.debug.watch "+#{item.diff.to_sf} #{item.description}" | |
elsif item.diff < 0 | |
args.outputs.debug.watch "#{item.diff.to_sf} #{item.description}" | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# THIS IS FOR DEBUGGING PURPOSES TO MAKE SURE THE AUDIO THAT'S BEING USED | |
# IS BEING CROSS-FADED TO YOUR EXPECTATIONS | |
# step 1. create a looping audio and make sure it's seemless | |
# we are explictly setting the audio looping to true because | |
# we want to verify that the audio playback works independent | |
# of the simulation loop | |
def tick args | |
args.audio[:track_1] ||= { | |
input: "sounds/music.ogg", | |
gain: 1.0, | |
looping: true | |
} | |
end | |
# step 2. create a looping audio and make sure the loop of the | |
# audio is acceptable with the looping being controlled by the | |
# simulation | |
def tick args | |
args.audio[:track_1] ||= { | |
input: "sounds/music.ogg", | |
gain: 1.0, | |
looping: false | |
} | |
end | |
# step 3. cross fading audio with play head controlled by the simulation loop | |
def tick args | |
if Kernel.tick_count == 0 | |
puts "Step 3." | |
end | |
args.state.track_length_in_ticks ||= 2057 | |
args.state.main_track ||= :track_1 | |
args.state.other_track ||= :track_2 | |
args.state.play_head ||= 0 | |
args.state.start_playing_on_tick ||= 180 | |
if args.state.tick_count >= args.state.start_playing_on_tick | |
args.audio[:track_1] ||= { | |
input: "sounds/music.ogg", | |
gain: 1.0, | |
looping: false | |
} | |
args.audio[:track_2] ||= { | |
input: "sounds/music.ogg", | |
looping: false, | |
gain: 0.0 | |
} | |
end | |
if args.state.tick_count > args.state.start_playing_on_tick | |
args.state.play_head += 1 | |
args.state.play_head = args.state.play_head % args.state.track_length_in_ticks | |
else | |
args.outputs.labels << { x: 640, | |
y: 360, | |
text: "Count down: #{(args.state.start_playing_on_tick - args.state.tick_count).idiv(60) + 1}", | |
anchor_x: 0.5, | |
anchor_y: 0.5 } | |
end | |
if args.state.play_head.zmod?(600) && args.state.tick_count > args.state.start_playing_on_tick | |
if args.state.main_track == :track_1 | |
args.state.main_track = :track_2 | |
args.state.other_track = :track_1 | |
else | |
args.state.main_track = :track_1 | |
args.state.other_track = :track_2 | |
end | |
if args.audio[args.state.main_track] | |
args.audio[args.state.main_track].playtime = args.state.play_head.idiv(60) | |
end | |
end | |
if args.audio[args.state.main_track] | |
args.audio[args.state.main_track].gain += 0.1 | |
args.audio[args.state.main_track].gain = 1.0 if args.audio[args.state.main_track].gain > 1.0 | |
end | |
if args.audio[args.state.other_track] | |
args.audio[args.state.other_track].gain -= 0.1 | |
args.audio[args.state.other_track].gain = 0.0 if args.audio[args.state.other_track].gain < 0.0 | |
end | |
if args.audio[:track_1] && args.audio[:track_2] | |
args.outputs.debug.watch "track_1 gain: #{args.audio[:track_1].gain.to_sf}" | |
args.outputs.debug.watch "track_2 gain: #{args.audio[:track_2].gain.to_sf}" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment