Godot Game - Meta Elements
Ugh! Finally started working on the meta elements of my game, such as menu screens and the like. I’ve been putting this off for a long while. It’s uninteresting, mechanical work, and not the reason that got me into this project in the first place. Which is ironic, as it probably should be the most important aspect of the entire game, given that it’s what players will see when they first launch it. Maybe if I was in the business of making money from this, I’d put a bit more effort into it. But this is just some fun, and it’s hard having fun doing things that I know need to be done if this is going to be more than just a collection of scenes that I launch within Godot.
So I’ll have to put something together to fulfil this task. And as someone in London once said: “well begun is half done.” Well, I’m not half done. Nowhere close. But I have started, so that’s something.
And yeah, this is not going to be anything fancy. Just a couple of menu screens, navigable by the arrow keys, that’ll be more hand-rolled than it probably should be. Each screen will consist mainly of labels, with the selected one being a different label style. For today, the work is purely focused on the mechanics: selection, confirmation, and layout. Styling and backgrounds will come later, not to mention actually coming up with a good name.
I’ve abstracted out the menu selection mechanics into a separate subclass. And I know I’m going to forget how to do this (because I have once already) so I’m going to document the process of defining new classes in GDScript.
So, here it is: create a new script, and set the class_name. It’s probably a good idea to explicitly specify the superclass using the extends keyword. You could extend Object, but those are not memory managed and you’ll need to explicitly free instances when you no longer need them. Instead, consider extending RefCounted, which uses reference counters to manage and free its lifecycle.
The rest of the class looks like any other script.
# Filename: menu_item_helper.tscn
class_name MenuItemHelper
extends RefCounted
# constants
const REFIRE_TIME_INIT = 0.5
const REFIRE_TIME_REPEAT = 0.1
# static (class) variables
static var _res_item_normal: LabelSettings = load("res://meta/menu_label.tres")
static var _res_item_selected: LabelSettings = load("res://meta/menu_label_selected.tres")
# instance variables
var _labels: Array = []
var _selection: int = 0
var _refire_action: String = ""
var _refire_time: float
# instance signals
signal on_confirm(selection: int)
signal on_cancel()
signal on_changed(new_selection: int)
The special function _init, which can accept arguments, will be used by the constructor to initialise the instance. This can call the initialiser of the superclass by calling super() if required.
# Filename: menu_item_helper.tscn
func _init(labels: Array) -> void:
_labels = labels
_selection = 0
_refresh_options()
To actually create instances of this new class, call Classname.new() and supply the arguments required by the initialiser:
# Filename: main_menu.tscn
func _ready() -> void:
_menu_helper = MenuItemHelper.new([
opt_newgame,
opt_options
])
_menu_helper.on_confirm.connect(_on_confirm)
I suppose this helper could’ve extended Node so that it would receive process callbacks automatically. I didn’t want to do this as I wanted the instance managed by the scene. But the helper does have a process method (simple called process) which handles user input that the scene just needs to calling it explicitly:
# Filename: main_menu.tscn
func _process(delta: float) -> void:
_menu_helper.process(delta)
func _on_confirm(item: int) -> void:
if item == ITEM_NEW_GAME:
get_tree().change_scene_to_file("res://scenes/meta/new_game_menu.tscn")
Callbacks seem to be supported too, which makes it relatively trivial to support event handling. And this is another thing I needed to remember: how to transition between scenes. Selecting “New Game” from the main menu needs to send the player to the New Game menu. I don’t know if Godot has a navigation stack, and since I’m only expecting a few screens, I’m just doing it manually.
So yeah, nothing super exciting to show today. But to show that work has started is maybe enough for now.