Improved menus

This commit is contained in:
Gordon Pedersen 2023-04-11 16:26:39 +10:00
parent dbe7b3401d
commit 386e4cd650
16 changed files with 95 additions and 68 deletions

View file

@ -4,9 +4,11 @@
class SceneInstance
def initialize(_args, opts = {})
@tick_in_background = opts.tick_in_background._? false
@reset_on_pop = opts.reset_on_pop._? false
end
attr_reader :tick_in_background
attr_accessor :reset_on_pop
# called every tick of the game loop
def tick(args) end

View file

@ -26,7 +26,7 @@ ORANGE = { r: 255, g: 173, b: 31 }
PINK = { r: 245, g: 146, b: 198 }
PURPLE = { r: 133, g: 42, b: 216 }
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_GREEN = { r: 5, g: 84, b: 12 }

View file

@ -11,7 +11,7 @@ class MainMenu < MenuScene
},
{
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
super args, opts, menu_options
super args, opts, title, menu_options
end
# called every tick of the game loop
def tick(args)
draw_bg(args, DARK_PURPLE)
# actual menu logic is handled by the MenuScene super class
super
@ -36,28 +34,30 @@ class MainMenu < MenuScene
labels = []
labels << label(
"v#{version}",
x: 32.from_left, y: 32.from_top,
size: SIZE_XS, align: ALIGN_LEFT
)
labels << label(
title.upcase, x: args.grid.w / 2, y: args.grid.top - 100,
size: SIZE_LG, align: ALIGN_CENTER, font: FONT_BOLD_ITALIC
)
x: 400.from_right, y: 150.from_bottom,
size: SIZE_XS, align: ALIGN_RIGHT,
font: FONT_DOTMATRIX
).merge(YELLOW)
# labels << label(
# title.upcase, x: args.grid.w / 2, y: args.grid.top - 100,
# size: SIZE_LG, align: ALIGN_CENTER, font: FONT_BOLD_ITALIC
# )
labels << label(
"#{text(:made_by)} #{dev_title}",
x: args.grid.left + 24, y: 48,
size: SIZE_XS, align: ALIGN_LEFT
)
labels << label(
:controls_title,
x: args.grid.right - 24, y: 84,
size: SIZE_SM, align: ALIGN_RIGHT
)
labels << label(
args.inputs.controller_one.connected ? :controls_gamepad : :controls_keyboard,
x: args.grid.right - 24, y: 48,
size: SIZE_XS, align: ALIGN_RIGHT
)
x: 242.from_left, y: 150.from_bottom,
size: SIZE_XS, align: ALIGN_LEFT,
font: FONT_DOTMATRIX
).merge(YELLOW)
# labels << label(
# :controls_title,
# x: args.grid.right - 24, y: 84,
# size: SIZE_SM, align: ALIGN_RIGHT
# )
# labels << label(
# args.inputs.controller_one.connected ? :controls_gamepad : :controls_keyboard,
# x: args.grid.right - 24, y: 48,
# size: SIZE_XS, align: ALIGN_RIGHT
# )
args.outputs.labels << labels
end

View file

@ -10,21 +10,28 @@
# }
# ]
class MenuScene < SceneInstance
def initialize(args, opts = {}, menu_options = [])
def initialize(args, opts = {}, title = title(), menu_options = [])
super args, opts
@menu_state ||= {
current_option_i: 0,
hold_delay: 0
}
@spacer ||= mobile? ? 100 : 60
@spacer ||= mobile? ? 100 : 80
@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
def render_options(args)
labels = []
@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
when :toggle
"#{text(option[:key])}: #{text_for_setting_val(args, option[:key])}"
@ -32,24 +39,36 @@ class MenuScene < SceneInstance
text(option[:key])
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(
text,
x: args.grid.w / 2,
text.upcase,
x: (args.grid.w / 2) - 70,
y: @menu_y + (@menu_options.length - (i * @spacer)),
align: ALIGN_CENTER,
size: SIZE_MD
size: 16,
font: FONT_DOTMATRIX_BOLD
)
l.key = option[:key]
l.width, l.height = args.gtk.calcstringbox(l.text, l.size_enum)
labels << l
l.width, l.height = args.gtk.calcstringbox(l.text, l.size_enum, l.font)
if @menu_state.current_option_i == i && (!mobile? || (mobile? && args.inputs.controller_one.connected))
args.outputs.solids << {
x: l.x - (l.width / 1.4) - 24 + (Math.sin(args.state.tick_count / 8) * 4),
y: l.y - 22,
w: 16,
h: 16
}.merge(WHITE)
labels << l.merge(active ? WHITE : YELLOW)
if active
labels << label(
'.',
x: l.x - (l.width / 2) - 26 - (Math.sin(args.state.tick_count / 8) * 4),
y: l.y + 15,
align: ALIGN_CENTER,
size: 18,
font: FONT_DOTMATRIX
).merge(WHITE)
end
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
def tick(args)
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)
@ -118,6 +149,7 @@ class MenuScene < SceneInstance
@menu_state.current_option_i = 0
@menu_state.hold_delay = 0
@first_render = nil
end
def text_for_setting_val(args, key)

View file

@ -11,11 +11,11 @@ class PauseMenu < MenuScene
},
{
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,
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
super args, opts, menu_options
super args, opts, :paused, menu_options
end
# called every tick of the game loop
def tick(args)
super
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

View file

@ -45,7 +45,7 @@ class SettingsMenu < MenuScene
)
end
super args, opts, menu_options
super args, opts, :settings, menu_options
end
# called every tick of the game loop
@ -54,14 +54,5 @@ class SettingsMenu < MenuScene
# actual menu logic is handled by the MenuScene super class
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

View file

@ -16,10 +16,11 @@ TEXT = {
paused: "Paused",
quit: "Quit",
resume: "Resume",
return_to_main_menu: "Return to Main Menu",
return_to_main_menu: "Main Menu",
settings: "Settings",
sfx: "Sound Effects",
start: "Start",
cube_tube: "Cube tube",
}
# 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_SM = 4
SIZE_MD = 6
SIZE_LG = 10
SIZE_LG = 14
FONT_REGULAR = "fonts/Atkinson-Hyperlegible-Regular-102.ttf"
FONT_ITALIC = "fonts/Atkinson-Hyperlegible-Italic-102.ttf"
FONT_BOLD = "fonts/Atkinson-Hyperlegible-Bold-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
# structures.

View file

@ -40,8 +40,10 @@ module Scene
end
# 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 ||= []
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)
args.state.scene_stack.push(the_scene)
@ -49,10 +51,14 @@ module Scene
end
# Return to the previous scene on the stack
def pop(args, reset: false)
scene = args.state.scene_stack&.pop
def pop(args, reset: nil)
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
def default(args)

BIN
fonts/DotMatrix-Bold.ttf Normal file

Binary file not shown.

BIN
fonts/DotMatrix.ttf Normal file

Binary file not shown.

BIN
fonts/Rubik-Black.ttf Normal file

Binary file not shown.

View file

@ -24,5 +24,6 @@ module Sprite
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_s4: SpriteInstance.new({ w: 250, h: 210, path: 'sprites/screen-s4.png' }),
menu: SpriteInstance.new({ w: 1280, h: 720, path: 'sprites/menu.png' }),
}
end

BIN
sprites/menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 KiB

BIN
wip-assets/sprites/menu.pdn Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.