A framework designed specifically for tactical turn-based games, developed with Godot C#!
EZ-Chess is a scaffolding framework that abstracts the tactical game flow to facilitate development. It provides a modular and extensible architecture for developing turn-based board games in the Godot engine using C#. Its core concepts include separating game state from presentation, an event-driven pipeline system, and a multiplayer synchronization mechanism based on GitHub Discussions.
-
State and Node Separation:
The game piece’s state and node are separated intoPieceStateandPieceInstance. ThePieceStatedrives the changes inPieceInstance. This allows frontend-backend separation and asynchronous frontend updates. -
Pipeline Architecture:
Player actions are encapsulated as objects and queued. The state pipeline listens and executes them asynchronously. The rendering pipeline works the same way, enabling asynchronous updates. -
Event-Driven:
Pipelines are not directly coupled but communicate through an event bus. When the state pipeline receives a player operation, it subscribes the event to the event bus. Once execution completes, it publishes the event, which triggers rendering operations in the render pipeline. -
Component-Based Design:
Tactical games usually contain many equipments and buffs. Overusing inheritance can cause class explosion, so EZ-Chess uses decorator chains to wrapPieceStateandPieceInstance, allowing more flexible configuration.
- The main framework code is located in
addons/script. valveabstracts player actions such as movement and attack.stateandinstanceabstract logic and node data. They generally don’t need inheritance unless special behavior is required.decoratorcontains decorators, divided intoPieceStateDecoratorandPieceInstanceDecorator, which wrap state and instance respectively.PieceAdapterwrapsstateandinstance. Inherit it and implement actions such as movement (usually by calling functions instate).PieceFactorycreates chess pieces. InheritPieceFactoryBaseand attach it to theGameLoadernode ingame.tscn.
How to run example:
Run the project directly in Godot. Enter your GitHub token (must have “Discussions: write” and “Repository: read” permissions),
then click Create Room to host the game. Other players can join by entering the discussion number (shown on the top-left of the host’s screen).
- PieceAdapter: Core adapter for chess pieces. A Godot Node that connects the logical state (
IPieceState) and the visual representation (IPieceInstance). - IPieceState / PieceState: Defines logic such as type, faction, HP, and mobility. Does not handle visuals or input.
- IPieceInstance / PieceInstance: Defines visual behavior such as textures, hover feedback, selection, and animation.
- PieceDecorator (and subtypes PieceStateDecorator, PieceInstanceDecorator): Implements the decorator pattern for dynamic behavior extension.
- IPiece: The base interface implemented by all piece-related objects.
- IInterfaceQueryable: Provides
Query<T>()to search for specific interface implementations within a piece or its decorators.
- Pipeline (abstract base): Defines the event-handling structure and lifecycle. Implements
ILaunchableandIStoppable. - StatePipeline: Handles logical state change events.
- RenderPipeline: Handles visual rendering and animation events.
- Valve: The smallest executable unit in a pipeline.
- StateValve: Executes
IPieceState-related events. - InstanceValve: Executes
IPieceInstance-related events.
- StateValve: Executes
- PipelineEventBus: A global event bus for decoupled communication between valves and pipelines.
- RegisterValveAttribute / ValveGenerator: Attribute-based code generator that auto-binds custom Valve classes with their actions.
- IAction: Interface representing an object capable of executing an action when a specific event is received.
- HexMap: Manages hex grid logic, coordinate conversion, and pathfinding.
- TerrainLayers: Manages terrain layers with different movement costs.
- AStar: Custom A* pathfinding implementation supporting hex grids and terrain cost calculation.
- GameManager: Main controller managing players, pieces, and the game flow.
- GameLoader: Initializes settings, creates players, pipelines, and chess pieces.
- GameState: A global singleton storing the entire game state (turns, players, selection, etc.).
- GameConfig / GameSelector: Handles game module selection, room configuration, and GitHub login.
- GithubUtils: Handles GitHub Discussions API interactions for room creation, message posting, state sync, and operation management.
- IOperationRunner / IEnvironmentRunner: Define how operations and environmental updates are executed.
- Pipelines: Synchronize game history and continuously poll for updates.
- PlayerPipeline / OtherPipeline: Represent the current player’s pipelines and others’ pipelines.
- Group<K, V>: A dictionary wrapper allowing multiple values per key.
- HexUtils: Hex grid utilities for distance and adjacency.
- AdjectiveConverter: Converts verbs to “-able” adjectives for code generation.
- Vector2IConverter: JSON serializer/deserializer for
Vector2I.
Includes IPiece, PieceAdapter, IPieceState, IPieceInstance.
- IPiece: Base interface defining shared attributes such as
PipelineAdapter,PiecesManager,Faction,PieceType, etc. - PieceAdapter:
- Inherits from
Node. Init(IPieceState state, IPieceInstance instance)binds logic and visuals.- Maintains mapping dictionaries between state and instance IDs.
- Injects references like
GameManager,HexMap, and pipelines into pieces. Decorate()applies runtime decorators.
- Inherits from
- PieceState: Inherits from
RefCounted, holds logical attributes (e.g., Faction, PieceType). - PieceInstance: Inherits from
Node2D, handles visual behavior, hover and click events.
- PieceDecorator(IPiece wrapped): Base class for all decorators. Implements
IPieceand delegates most properties to_wrapped. - PieceStateDecorator / PieceInstanceDecorator:
ImplementIAction<E>whereEis aPieceEvent.
Defines_ReceiveEvent(E @event)for event handling.
Supports adding valves viaAddValve<T, V>(E @event)and saving operations to GitHub.
- PipelineImpl: Base class for state/render pipelines using asynchronous valve execution.
- Valve: A
RefCountedunit withDoLaunch()for concrete logic. - RegisterValveAttribute: Custom attribute for marking Valve classes, enabling automatic generation of
StateValveorInstanceValve. - PipelineEventBus: Singleton providing
Subscribe<T>()andPublish<T>(). Used to notify render pipelines when state changes complete.
- GameLoader:
Executes initialization in_Ready(), loads configurations, creates players, factories, and pipelines, then launches the game.
- GithubUtils:
- Login(): Authenticate GitHub Token.
- CreateRoom() / GetRoomInfo() / EnterRoom() / ExitRoom(): Manage game room discussions.
- SaveOperation() / SubmitOperationOnInterval(): Cache and post game operations.
- ApplyComment() / ApplyOperations(): Retrieve and apply remote operations.
- Recover(): Restore game state from history.
- ChangeFaction() / IncreaseTurn(): Update environment data like current turn and player.
-
Define piece state and instance classes:
- Create a subclass of
PieceState(e.g.,MyPawnState). - Create a subclass of
PieceInstance(e.g.,MyPawnInstance).
- Create a subclass of
-
Create a piece factory:
- Subclass
PieceFactoryBase(e.g.,MyPieceFactory). - Implement
Create()usingCreate<T>()to instantiatePieceAdapter.
- Subclass
-
Configure
GameLoader:- Assign your factory script to
GameLoader.PieceFactoryScript.
- Assign your factory script to
-
Configure pieces in
config.json:- Define name, type, texture, position, and size.
- Define an event (e.g.,
MoveEvent). - Define a behavior interface (e.g.,
IMoveable : IAction<MoveEvent>). - Create decorators (
MoveStateDecorator,MoveInstanceDecorator). - Register valves using
[RegisterValve("Move", ValveTypes.STATE)]or[RegisterValve("Move", ValveTypes.INSTANCE)]. - Apply decorators with
PieceAdapter.Decorate(...).
- Configure
_terrainCostinHexMap. - Set
Terrainproperties inTileSetto link with movement costs.
- Implement
IOperationRunnerto handle operations. - Implement
IEnvironmentRunnerto update environment. - Configure them in
GameLoader. - Use
GithubUtilsto submit operations and update turns.
-
Game Module Selection (GameSelector):
Allows loading of external Godot PCK modules.
Each module contains aconfig.jsondefining name, version, description, factions, layout, etc. -
Game Configuration (GameConfig):
Choose faction, input GitHub Token, join or create a room. -
Game Loading (GameLoader):
Initializes the world, loads factories and handlers, creates pieces, and starts the pipelines.