tier
layered game framework
Download
Module:
Example place, includes my suggested layout + an example ClientRunner and ServerRunner:
Structure
tier consists of 3 layers of modules:
- systems (uses)
- managers (uses and used)
- providers (used)
This is to create a clear separation of concerns and modularity. They all follow “rules” about how they interact with other layers.
Rules
The framework does not force you to follow any rules. It’s up to you to ensure your modules follow the rules/structure of the framework.
Generally modules only require down the tree (systems > managers > providers). The only exception is Managers can require other Managers.
-
systems: have no public functionality and should not be used by other modules. Their purpose is to use the Managers and Providers to run core game logic
-
managers: can use other Managers and use Providers. Their purpose is to manage more specific game functionalities
-
providers: have only public functionality and should not use any other modules in the framework. They are foundational modules with modular functionality.
Lifecycle
- call
tier.prepare
Server does some checks to make sure things are set up correctly.
Client will yield until the server finishes loading (can be changed for client-only games).
- Add your modules
Add all your modules to the framework via tier.gather
(or individually if choose)
- call
tier.begin
tier will consecutively move through it’s stages, requiring the respective modules for the stage and their respective startup functions. Each layer’s startup functions can be found in the “Module Startup” section.
Here are the stages the framework goes through:
Module Startup
-
systems
Do not have any startup functions -
managers
The framework will check for a.Init
and a.Start
. Why is this important? Since managers can require other managers, there’s a chance for race conditions. Module A needs to require Module B, but module B needs to do so important work first. This work can be organized into the .Init function, and then Module A can safely use Module B when .Start is called. -
providers
The framework will check for a.Init
. This is not exactly important and does not serve much purpose, but it’s available for people who prefer an init function in their script.
Organization Examples
-
A camera module = fits into a Provider (CameraProvider). Why? The module offers foundational and modular functionality that will be used by other modules. The CameraProvider has no reason to be requiring other modules in the framework.
-
An effect module = fits into a Manager (EffectManager). Why? The module offers public functionality that could possibly be used by other Managers or Systems, but could possibly need to require the CameraProvider for effects.
-
A game loop module = fits into a System (GameLoopSystem). Why? The module handles the core loop of the game. It requires multiple different Managers and Systems to do things at certain times. This module offers no public functionality as its only purpose is to keep things running in a loop.
API
tier.getStage(): (number?, string?)
-- returns the stage number, and name of the stage
tier.getTasks(): (number, number, number)
-- returns the current task, total tasks, and an alpha (current/total)
tier.prepare()
-- prepares the framework
tier.gather(From: Instance, Deep: boolean, Add: (ModuleScript) -> (), Filter: ((ModuleScript) -> (boolean?))?)
-- auto gathers modules from a directory (from)
-- "deep" to use GetDescendants instead of GetChildren
-- adds them to the framework using the Add function
-- optional "filter" for deciding which modules to use out of the selection
-- Example: tier.gather(SystemsFolder, false, tier.system, ModuleScriptCallback)
tier.provider(ModuleScript: ModuleScript)
-- adds a provider to the framework
tier.manager(ModuleScript: ModuleScript)
-- adds a manager to the framework
tier.system(ModuleScript: ModuleScript)
-- adds a system to the framework
tier.begin()
-- starts the framework
Summary
Let me know your thoughts on my framework! It’s a concept that I’ve been formulating for a while now as I’ve worked with countless other frameworks. It may take a little to get used to, but when you get the hang of how it works and what goes where, it’s pretty nice to work with. It’s a bit of a different approach from other frameworks since it more focuses on a specific structure.
I’m open to any critiques or concerns you may have, just let me know!