Improved menus
This commit is contained in:
parent
dbe7b3401d
commit
386e4cd650
16 changed files with 95 additions and 68 deletions
|
@ -4,9 +4,11 @@
|
||||||
class SceneInstance
|
class SceneInstance
|
||||||
def initialize(_args, opts = {})
|
def initialize(_args, opts = {})
|
||||||
@tick_in_background = opts.tick_in_background._? false
|
@tick_in_background = opts.tick_in_background._? false
|
||||||
|
@reset_on_pop = opts.reset_on_pop._? false
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :tick_in_background
|
attr_reader :tick_in_background
|
||||||
|
attr_accessor :reset_on_pop
|
||||||
|
|
||||||
# called every tick of the game loop
|
# called every tick of the game loop
|
||||||
def tick(args) end
|
def tick(args) end
|
||||||
|
|
|
@ -26,7 +26,7 @@ ORANGE = { r: 255, g: 173, b: 31 }
|
||||||
PINK = { r: 245, g: 146, b: 198 }
|
PINK = { r: 245, g: 146, b: 198 }
|
||||||
PURPLE = { r: 133, g: 42, b: 216 }
|
PURPLE = { r: 133, g: 42, b: 216 }
|
||||||
RED = { r: 231, g: 89, b: 82 }
|
RED = { r: 231, g: 89, b: 82 }
|
||||||
YELLOW = { r: 240, g: 232, b: 89 }
|
YELLOW = { r: 255, g: 200, b: 5 }
|
||||||
|
|
||||||
DARK_BLUE = { r: 22, g: 122, b: 188 }
|
DARK_BLUE = { r: 22, g: 122, b: 188 }
|
||||||
DARK_GREEN = { r: 5, g: 84, b: 12 }
|
DARK_GREEN = { r: 5, g: 84, b: 12 }
|
||||||
|
|
|
@ -11,7 +11,7 @@ class MainMenu < MenuScene
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: :settings,
|
key: :settings,
|
||||||
on_select: ->(iargs) { Scene.push(iargs, :settings, reset: true) }
|
on_select: ->(iargs) { Scene.push(iargs, :settings, reset: true, reset_on_pop: true) }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -22,13 +22,11 @@ class MainMenu < MenuScene
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
super args, opts, menu_options
|
super args, opts, title, menu_options
|
||||||
end
|
end
|
||||||
|
|
||||||
# called every tick of the game loop
|
# called every tick of the game loop
|
||||||
def tick(args)
|
def tick(args)
|
||||||
draw_bg(args, DARK_PURPLE)
|
|
||||||
|
|
||||||
# actual menu logic is handled by the MenuScene super class
|
# actual menu logic is handled by the MenuScene super class
|
||||||
super
|
super
|
||||||
|
|
||||||
|
@ -36,28 +34,30 @@ class MainMenu < MenuScene
|
||||||
labels = []
|
labels = []
|
||||||
labels << label(
|
labels << label(
|
||||||
"v#{version}",
|
"v#{version}",
|
||||||
x: 32.from_left, y: 32.from_top,
|
x: 400.from_right, y: 150.from_bottom,
|
||||||
size: SIZE_XS, align: ALIGN_LEFT
|
size: SIZE_XS, align: ALIGN_RIGHT,
|
||||||
)
|
font: FONT_DOTMATRIX
|
||||||
labels << label(
|
).merge(YELLOW)
|
||||||
title.upcase, x: args.grid.w / 2, y: args.grid.top - 100,
|
# labels << label(
|
||||||
size: SIZE_LG, align: ALIGN_CENTER, font: FONT_BOLD_ITALIC
|
# title.upcase, x: args.grid.w / 2, y: args.grid.top - 100,
|
||||||
)
|
# size: SIZE_LG, align: ALIGN_CENTER, font: FONT_BOLD_ITALIC
|
||||||
|
# )
|
||||||
labels << label(
|
labels << label(
|
||||||
"#{text(:made_by)} #{dev_title}",
|
"#{text(:made_by)} #{dev_title}",
|
||||||
x: args.grid.left + 24, y: 48,
|
x: 242.from_left, y: 150.from_bottom,
|
||||||
size: SIZE_XS, align: ALIGN_LEFT
|
size: SIZE_XS, align: ALIGN_LEFT,
|
||||||
)
|
font: FONT_DOTMATRIX
|
||||||
labels << label(
|
).merge(YELLOW)
|
||||||
:controls_title,
|
# labels << label(
|
||||||
x: args.grid.right - 24, y: 84,
|
# :controls_title,
|
||||||
size: SIZE_SM, align: ALIGN_RIGHT
|
# x: args.grid.right - 24, y: 84,
|
||||||
)
|
# size: SIZE_SM, align: ALIGN_RIGHT
|
||||||
labels << label(
|
# )
|
||||||
args.inputs.controller_one.connected ? :controls_gamepad : :controls_keyboard,
|
# labels << label(
|
||||||
x: args.grid.right - 24, y: 48,
|
# args.inputs.controller_one.connected ? :controls_gamepad : :controls_keyboard,
|
||||||
size: SIZE_XS, align: ALIGN_RIGHT
|
# x: args.grid.right - 24, y: 48,
|
||||||
)
|
# size: SIZE_XS, align: ALIGN_RIGHT
|
||||||
|
# )
|
||||||
|
|
||||||
args.outputs.labels << labels
|
args.outputs.labels << labels
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,21 +10,28 @@
|
||||||
# }
|
# }
|
||||||
# ]
|
# ]
|
||||||
class MenuScene < SceneInstance
|
class MenuScene < SceneInstance
|
||||||
def initialize(args, opts = {}, menu_options = [])
|
def initialize(args, opts = {}, title = title(), menu_options = [])
|
||||||
super args, opts
|
super args, opts
|
||||||
|
|
||||||
@menu_state ||= {
|
@menu_state ||= {
|
||||||
current_option_i: 0,
|
current_option_i: 0,
|
||||||
hold_delay: 0
|
hold_delay: 0
|
||||||
}
|
}
|
||||||
@spacer ||= mobile? ? 100 : 60
|
@spacer ||= mobile? ? 100 : 80
|
||||||
@menu_options ||= menu_options
|
@menu_options ||= menu_options
|
||||||
@menu_y = opts.menu_y._?(420)
|
@menu_y = opts.menu_y._?(440)
|
||||||
|
@title ||= title
|
||||||
|
@rand_strings = (0..@menu_options.length).map do |i|
|
||||||
|
(0...@title.length).map { ('A'..'Z').to_a[rand(26)] }.join
|
||||||
|
end
|
||||||
|
@first_render = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_options(args)
|
def render_options(args)
|
||||||
labels = []
|
labels = []
|
||||||
@menu_options.each.with_index do |option, i|
|
@menu_options.each.with_index do |option, i|
|
||||||
|
active = @menu_state.current_option_i == i && (!mobile? || (mobile? && args.inputs.controller_one.connected))
|
||||||
|
|
||||||
text = case option.kind
|
text = case option.kind
|
||||||
when :toggle
|
when :toggle
|
||||||
"#{text(option[:key])}: #{text_for_setting_val(args, option[:key])}"
|
"#{text(option[:key])}: #{text_for_setting_val(args, option[:key])}"
|
||||||
|
@ -32,24 +39,36 @@ class MenuScene < SceneInstance
|
||||||
text(option[:key])
|
text(option[:key])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if (args.state.tick_count - @first_render) < 60 * (1.5 + i) * 0.2
|
||||||
|
if args.state.tick_count % 4 == 0
|
||||||
|
@rand_strings[i] = (0...(rand(text.length >= 3 ? text.length : 3) + 3)).map { ('A'..'Z').to_a[rand(26)] }.join
|
||||||
|
end
|
||||||
|
text = @rand_strings[i]
|
||||||
|
active = false
|
||||||
|
end
|
||||||
|
|
||||||
l = label(
|
l = label(
|
||||||
text,
|
text.upcase,
|
||||||
x: args.grid.w / 2,
|
x: (args.grid.w / 2) - 70,
|
||||||
y: @menu_y + (@menu_options.length - (i * @spacer)),
|
y: @menu_y + (@menu_options.length - (i * @spacer)),
|
||||||
align: ALIGN_CENTER,
|
align: ALIGN_CENTER,
|
||||||
size: SIZE_MD
|
size: 16,
|
||||||
|
font: FONT_DOTMATRIX_BOLD
|
||||||
)
|
)
|
||||||
l.key = option[:key]
|
l.key = option[:key]
|
||||||
l.width, l.height = args.gtk.calcstringbox(l.text, l.size_enum)
|
l.width, l.height = args.gtk.calcstringbox(l.text, l.size_enum, l.font)
|
||||||
labels << l
|
|
||||||
|
|
||||||
if @menu_state.current_option_i == i && (!mobile? || (mobile? && args.inputs.controller_one.connected))
|
labels << l.merge(active ? WHITE : YELLOW)
|
||||||
args.outputs.solids << {
|
|
||||||
x: l.x - (l.width / 1.4) - 24 + (Math.sin(args.state.tick_count / 8) * 4),
|
if active
|
||||||
y: l.y - 22,
|
labels << label(
|
||||||
w: 16,
|
'.',
|
||||||
h: 16
|
x: l.x - (l.width / 2) - 26 - (Math.sin(args.state.tick_count / 8) * 4),
|
||||||
}.merge(WHITE)
|
y: l.y + 15,
|
||||||
|
align: ALIGN_CENTER,
|
||||||
|
size: 18,
|
||||||
|
font: FONT_DOTMATRIX
|
||||||
|
).merge(WHITE)
|
||||||
end
|
end
|
||||||
|
|
||||||
button_border = { w: 340, h: 80, x: l.x - 170, y: l.y - 55 }.merge(WHITE)
|
button_border = { w: 340, h: 80, x: l.x - 170, y: l.y - 55 }.merge(WHITE)
|
||||||
|
@ -67,6 +86,18 @@ class MenuScene < SceneInstance
|
||||||
# called every tick of the game loop
|
# called every tick of the game loop
|
||||||
def tick(args)
|
def tick(args)
|
||||||
super
|
super
|
||||||
|
@first_render = args.state.tick_count if @first_render.nil?
|
||||||
|
|
||||||
|
Sprite.for(:menu).render(args)
|
||||||
|
|
||||||
|
args.outputs.labels << label(
|
||||||
|
@title.to_s.upcase,
|
||||||
|
x: (args.grid.w / 2) - 70,
|
||||||
|
y: args.grid.top - 175,
|
||||||
|
align: ALIGN_CENTER,
|
||||||
|
size: SIZE_LG,
|
||||||
|
font: FONT_RUBIK_BLACK
|
||||||
|
).merge(TRUE_BLACK)
|
||||||
|
|
||||||
render_options(args)
|
render_options(args)
|
||||||
|
|
||||||
|
@ -118,6 +149,7 @@ class MenuScene < SceneInstance
|
||||||
|
|
||||||
@menu_state.current_option_i = 0
|
@menu_state.current_option_i = 0
|
||||||
@menu_state.hold_delay = 0
|
@menu_state.hold_delay = 0
|
||||||
|
@first_render = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def text_for_setting_val(args, key)
|
def text_for_setting_val(args, key)
|
||||||
|
|
|
@ -11,11 +11,11 @@ class PauseMenu < MenuScene
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: :settings,
|
key: :settings,
|
||||||
on_select: ->(args) { Scene.push(args, :settings, reset: true) }
|
on_select: ->(args) { Scene.push(args, :settings, reset: true, reset_on_pop: true) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: :return_to_main_menu,
|
key: :return_to_main_menu,
|
||||||
on_select: ->(args) { Scene.switch(args, :main_menu) }
|
on_select: ->(args) { Scene.switch(args, :main_menu, reset: true) }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -26,21 +26,12 @@ class PauseMenu < MenuScene
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
super args, opts, menu_options
|
super args, opts, :paused, menu_options
|
||||||
end
|
end
|
||||||
|
|
||||||
# called every tick of the game loop
|
# called every tick of the game loop
|
||||||
def tick(args)
|
def tick(args)
|
||||||
super
|
super
|
||||||
Music.pause(args) unless Music.stopped(args) || Music.paused(args)
|
Music.pause(args) unless Music.stopped(args) || Music.paused(args)
|
||||||
|
|
||||||
args.outputs.labels << label(
|
|
||||||
:paused,
|
|
||||||
x: args.grid.w / 2,
|
|
||||||
y: args.grid.top - 200,
|
|
||||||
align: ALIGN_CENTER,
|
|
||||||
size: SIZE_LG,
|
|
||||||
font: FONT_BOLD
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,7 +45,7 @@ class SettingsMenu < MenuScene
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
super args, opts, menu_options
|
super args, opts, :settings, menu_options
|
||||||
end
|
end
|
||||||
|
|
||||||
# called every tick of the game loop
|
# called every tick of the game loop
|
||||||
|
@ -54,14 +54,5 @@ class SettingsMenu < MenuScene
|
||||||
|
|
||||||
# actual menu logic is handled by the MenuScene super class
|
# actual menu logic is handled by the MenuScene super class
|
||||||
super
|
super
|
||||||
|
|
||||||
args.outputs.labels << label(
|
|
||||||
:settings,
|
|
||||||
x: args.grid.w / 2,
|
|
||||||
y: args.grid.top - 200,
|
|
||||||
align: ALIGN_CENTER,
|
|
||||||
size: SIZE_LG,
|
|
||||||
font: FONT_BOLD
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,10 +16,11 @@ TEXT = {
|
||||||
paused: "Paused",
|
paused: "Paused",
|
||||||
quit: "Quit",
|
quit: "Quit",
|
||||||
resume: "Resume",
|
resume: "Resume",
|
||||||
return_to_main_menu: "Return to Main Menu",
|
return_to_main_menu: "Main Menu",
|
||||||
settings: "Settings",
|
settings: "Settings",
|
||||||
sfx: "Sound Effects",
|
sfx: "Sound Effects",
|
||||||
start: "Start",
|
start: "Start",
|
||||||
|
cube_tube: "Cube tube",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Gets the text for the passed in `key`. Raises if it does not exist. We don't
|
# Gets the text for the passed in `key`. Raises if it does not exist. We don't
|
||||||
|
@ -31,12 +32,15 @@ end
|
||||||
SIZE_XS = 0
|
SIZE_XS = 0
|
||||||
SIZE_SM = 4
|
SIZE_SM = 4
|
||||||
SIZE_MD = 6
|
SIZE_MD = 6
|
||||||
SIZE_LG = 10
|
SIZE_LG = 14
|
||||||
|
|
||||||
FONT_REGULAR = "fonts/Atkinson-Hyperlegible-Regular-102.ttf"
|
FONT_REGULAR = "fonts/Atkinson-Hyperlegible-Regular-102.ttf"
|
||||||
FONT_ITALIC = "fonts/Atkinson-Hyperlegible-Italic-102.ttf"
|
FONT_ITALIC = "fonts/Atkinson-Hyperlegible-Italic-102.ttf"
|
||||||
FONT_BOLD = "fonts/Atkinson-Hyperlegible-Bold-102.ttf"
|
FONT_BOLD = "fonts/Atkinson-Hyperlegible-Bold-102.ttf"
|
||||||
FONT_BOLD_ITALIC = "fonts/Atkinson-Hyperlegible-BoldItalic-102.ttf"
|
FONT_BOLD_ITALIC = "fonts/Atkinson-Hyperlegible-BoldItalic-102.ttf"
|
||||||
|
FONT_DOTMATRIX = "fonts/DotMatrix.ttf"
|
||||||
|
FONT_DOTMATRIX_BOLD = "fonts/DotMatrix-Bold.ttf"
|
||||||
|
FONT_RUBIK_BLACK = "fonts/Rubik-Black.ttf"
|
||||||
|
|
||||||
# Friendly method with sensible defaults for creating DRGTK label data
|
# Friendly method with sensible defaults for creating DRGTK label data
|
||||||
# structures.
|
# structures.
|
||||||
|
|
|
@ -40,8 +40,10 @@ module Scene
|
||||||
end
|
end
|
||||||
|
|
||||||
# Change the current scene by pushing it onto the scene stack
|
# Change the current scene by pushing it onto the scene stack
|
||||||
def push(args, scene, reset: false)
|
def push(args, scene, reset: false, reset_on_pop: false)
|
||||||
args.state.scene_stack ||= []
|
args.state.scene_stack ||= []
|
||||||
|
prev_scene = args.state.scene_stack.last
|
||||||
|
prev_scene.reset_on_pop = reset_on_pop unless prev_scene.nil?
|
||||||
the_scene = scene.is_a?(SceneInstance) ? scene : SCENES[scene].new(args)
|
the_scene = scene.is_a?(SceneInstance) ? scene : SCENES[scene].new(args)
|
||||||
args.state.scene_stack.push(the_scene)
|
args.state.scene_stack.push(the_scene)
|
||||||
|
|
||||||
|
@ -49,10 +51,14 @@ module Scene
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return to the previous scene on the stack
|
# Return to the previous scene on the stack
|
||||||
def pop(args, reset: false)
|
def pop(args, reset: nil)
|
||||||
scene = args.state.scene_stack&.pop
|
args.state.scene_stack&.pop
|
||||||
|
scene = args.state.scene_stack.last
|
||||||
|
scene = scene._?(default(args))
|
||||||
|
reset = scene.reset_on_pop if reset.nil?
|
||||||
|
puts reset, scene, scene.reset_on_pop
|
||||||
|
|
||||||
switch(args, scene._?(default(args)), reset: reset, push_or_pop: true)
|
switch(args, scene, reset: reset, push_or_pop: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def default(args)
|
def default(args)
|
||||||
|
|
BIN
fonts/DotMatrix-Bold.ttf
Normal file
BIN
fonts/DotMatrix-Bold.ttf
Normal file
Binary file not shown.
BIN
fonts/DotMatrix.ttf
Normal file
BIN
fonts/DotMatrix.ttf
Normal file
Binary file not shown.
BIN
fonts/Rubik-Black.ttf
Normal file
BIN
fonts/Rubik-Black.ttf
Normal file
Binary file not shown.
|
@ -24,5 +24,6 @@ module Sprite
|
||||||
screen_s2: SpriteInstance.new({ w: 250, h: 210, path: 'sprites/screen-s2.png' }),
|
screen_s2: SpriteInstance.new({ w: 250, h: 210, path: 'sprites/screen-s2.png' }),
|
||||||
screen_s3: SpriteInstance.new({ w: 250, h: 210, path: 'sprites/screen-s3.png' }),
|
screen_s3: SpriteInstance.new({ w: 250, h: 210, path: 'sprites/screen-s3.png' }),
|
||||||
screen_s4: SpriteInstance.new({ w: 250, h: 210, path: 'sprites/screen-s4.png' }),
|
screen_s4: SpriteInstance.new({ w: 250, h: 210, path: 'sprites/screen-s4.png' }),
|
||||||
|
menu: SpriteInstance.new({ w: 1280, h: 720, path: 'sprites/menu.png' }),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
BIN
sprites/menu.png
Normal file
BIN
sprites/menu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 673 KiB |
BIN
wip-assets/sprites/menu.pdn
Normal file
BIN
wip-assets/sprites/menu.pdn
Normal file
Binary file not shown.
BIN
wip-assets/sprites/subway-title.vox
Normal file
BIN
wip-assets/sprites/subway-title.vox
Normal file
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue