Project template (gale-admin)
You can organize the source code of your project as you want. However, this library provides the a command to create a basic template for your project. If you want to use it, you only need to execute the following command:
gale-admin create-project {project_name}
For instance, if you want to create a project called “my_first_arpg_game”, then you should execute:
gale-admin create-project my_first_arpg_game
It will create a directory with the same name with the following structure:
my_first_arpg_game
├── assets
│ ├── fonts
│ ├── graphics
│ └── sounds
├── main.py
├── README.md
├── settings.py
└── src
└── MyFirstArpgGame.py
fontsis an empty directory where you should store your font files.graphicsis an empty directory where you should store your image files.soundsis an empty directory where you should store your audio files.README.mdcontains a base README file. It contains the following contents:
# My First Arpg Game
main.pyis the main program to run your game, it contains the following contents:
"""
This module was autogenerated by gale.
"""
from src.MyFirstArpgGame import MyFirstArpgGame
if __name__ == "__main__":
# MyFirstArpgGame takes every gale.game.Game argument (title,
# window size, ...) straight from settings.py / gale.conf.global_settings,
# so there's no need to pass any of them here -- see settings.py.
game = MyFirstArpgGame()
game.exec()
settings.pycontains some constants and the registering of the key escape to quit the game. Its contents is the following:
"""
This module was autogenerated by gale.
Every setting here overrides gale.conf.global_settings' default for
gale.game.Game -- anything you remove (or never had) falls back to
gale's own default automatically; see gale.conf for the full list.
You can also add settings of your own here (for instance PLAYER_SPEED)
and read them back the same way, with `from gale.conf import settings`.
"""
import pathlib
import pygame
from gale import frames
from gale import input_handler
# No need to call pygame.mixer.init()/pygame.font.init() here: importing
# gale.game (which this project's src/ module does) already calls
# pygame.init(), which initializes every subsystem pygame ships with,
# mixer and font included -- and does so without raising if, say, no
# audio device is available, unlike calling pygame.mixer.init() directly.
input_handler.InputHandler.set_keyboard_action(input_handler.KEY_ESCAPE, "quit")
TITLE = "My First Arpg Game"
# Size we want to emulate
VIRTUAL_WIDTH = 320
VIRTUAL_HEIGHT = 180
# Size of our actual window
WINDOW_WIDTH = 1280
WINDOW_HEIGHT = 720
BASE_DIR = pathlib.Path(__file__).parent
# Register your textures from the graphics folder, for instance:
# TEXTURES = {
# "my_texture": pygame.image.load(BASE_DIR / "graphics/my_texture.png")
# }
TEXTURES = {}
# Register your frames, for instance:
# FRAMES = {
# "my_frames": frames.generate_frames(TEXTURES["my_texture"], 16, 16)
# }
FRAMES = {}
# Register your sound from the sounds ```folder, for instance:
# SOUNDS = {
# "my_sound": pygame.mixer.Sound(BASE_DIR, "assets" / "sounds/my_sound.wav"),
# }
SOUNDS = {}
# Register your fonts from the fonts folder, for instance:
# FONTS = {
# "small": pygame.font.Font(BASE_DIR / "fonts/font.ttf", 8)
# }
FONTS = {}
Overridable settings (gale.conf)
gale.conf.settings is what actually resolves TITLE,
WINDOW_WIDTH, and the rest of gale.game.Game’s configuration:
on first access, it tries to import your project’s own settings.py
(the one generated above) and looks up each setting there first,
falling back to gale.conf.global_settings – the same defaults
Game’s constructor used to hardcode directly – for anything your
project doesn’t define. That’s why main.py above can call
MyFirstArpgGame() with no arguments at all: everything it needs
already comes from settings.py (or, for whatever settings.py
doesn’t set, gale’s own defaults) automatically.
This works both ways: if you delete a constant from settings.py
entirely (say, FPS, which the template doesn’t even generate),
gale.conf.settings.FPS still resolves, to gale.conf.global_settings.FPS
(60). And since the lookup isn’t limited to a fixed list of names, you
can add your own settings the exact same way – for instance
PLAYER_SPEED = 200 in settings.py – and read them back
anywhere with:
from gale.conf import settings
settings.PLAYER_SPEED
You’re never required to go through settings.py/gale.conf at
all: passing an argument to Game() explicitly (e.g.
MyFirstArpgGame(window_width=1920)) always wins over both your
project’s settings and gale’s defaults, for whichever single instance
you construct that way.
src/MyFirstArpgGame.pycontains the class to define your game. Its contents is the following:
"""
This module was autogenerated by gale.
"""
import pygame
from gale.game import Game
from gale.input_handler import InputData, InputHandler, InputListener
from gale.state import StateMachine
class MyFirstArpgGame(Game, InputListener):
def init(self) -> None:
self.state_machine = StateMachine()
InputHandler.register_listener(self)
def update(self, dt: float) -> None:
self.state_machine.update(dt)
def render(self, surface: pygame.Surface) -> None:
self.state_machine.render(surface)
def on_input(self, input_id: str, input_data: InputData) -> None:
if (input_id == "quit" and input_data.pressed):
self.quit()
Adding a state (gale-admin create-state)
Once inside a project’s directory (the one with main.py and
src/ in it), you can scaffold a new gale.state.BaseState
subclass with:
gale-admin create-state {state_name}
For instance:
gale-admin create-state menu
creates src/states/menu_state.py (and src/states/__init__.py,
if it doesn’t exist yet) with a MenuState class stubbed out with
every BaseState method (enter, exit, on_input,
update, render), ready to be registered with a
StateMachine:
from src.states.menu_state import MenuState
self.state_machine = StateMachine({"menu": MenuState})
self.state_machine.change("menu")
The name is normalized regardless of how it’s cased or whether a
trailing state/State is already there — menu,
menu_state, and MenuState all produce the same
MenuState class in the same file.