Building a Game
Welcome to the comprehensive guide for building games with TYPE! This tutorial will walk you through the step-by-step process of creating your first game using the ECS (Entity Component System) architecture.
Your Game Development Area
All game development happens in the src/__Project__/
folder. This directory simulates any project folder and contains all the essential files for your game.
Understanding the Project Structure
Before building your game, you need to understand the key files that define your game's structure:
src/__Project__/
├── component.manage.json # 🧩 Component registry
├── scenes.manage.json # 🎬 Scene configuration
├── system.manage.json # ⚙️ System registry
├── Initial.scene.json # 🎯 Scene definitions
├── Bunny.blueprint.json # 📐 Entity blueprints
├── ExampleComponent.component.ts # 💎 Custom components
├── ExampleSystem.system.ts # 🔧 Custom systems
└── bunny.png # 🎨 Game assets
Step 1: Understanding Management Files
component.manage.json
This file registers all custom components available in your game:
{
"ExampleComponent": "ExampleComponent.component.js"
}
Key Points:
- Maps component names to their JavaScript files (
.js
extension, not.ts
) - Loaded during
EntityEngine.setup()
before any entities are created - Custom components extend the available component types
- Components must follow the
ComponentInstanceManage
interface
scenes.manage.json
This file defines your game's scene configuration:
{
"initialScene": "Initial",
"scenes": {
"Initial": "Initial.scene.json"
}
}
Key Points:
initialScene
: Defines which scene loads first when the game startsscenes
: Maps scene names to their JSON file paths- Game automatically loads the initial scene during startup
- Used by SceneEngine for scene management and transitions
system.manage.json
This file registers all custom systems that process game logic:
{
"ExampleSystem": "ExampleSystem.system.js"
}
Key Points:
- Maps system names to their JavaScript files (
.js
extension, not.ts
) - Systems are loaded and managed by SystemEngine
- Systems process entities with matching components
- Custom systems extend the available game logic processors
Step 2: Creating Scene Files
Scene Structure (<SceneName>.scene.json
)
Scene files define the game world, entities, and their initial state:
{
"name": "Initial",
"path": "Initial.scene.json",
"systems": [],
"gameObjects": [
{
"name": "Bunny",
"blueprint": {
"name": "Bunny",
"path": "Bunny.blueprint.json"
},
"components": [
{
"name": "SpriteComponent",
"data": {
"texture_path": "bunny.png",
"position": { "x": 400, "y": 300 },
"scale": { "x": 1, "y": 1 },
"rotation": 0,
"alpha": 1,
"visible": true,
"anchor": 0.5
}
}
]
}
]
}
Scene Properties:
name
: Scene identifierpath
: Self-reference to the scene filesystems
: Array of systems enabled for this scenegameObjects
: Array of entities with their components and data
Step 3: Creating Blueprint Files
Blueprint Structure (<Blueprint>.blueprint.json
)
Blueprints are reusable entity templates that define component combinations:
{
"name": "Bunny",
"path": "Bunny.blueprint.json",
"components": [
{
"name": "SpriteComponent",
"data": {
"texture_path": "bunny.png",
"position": { "x": 400, "y": 300 },
"scale": { "x": 1, "y": 1 },
"rotation": 0,
"alpha": 1,
"visible": true,
"anchor": 0.5
}
},
{
"name": "MouseComponent",
"data": {
"screenPosition": { "x": 0, "y": 0 },
"windowPosition": { "x": 0, "y": 0 },
"buttons": {
"left": false,
"right": false,
"middle": false
},
"wheel": {
"deltaX": 0,
"deltaY": 0,
"deltaZ": 0
}
}
}
]
}
Blueprint Benefits:
- Reusability: Create templates for common entity types
- Consistency: Ensure entities start with the same configuration
- Editor Integration: Blueprints can be used in the visual editor
- Component Prefabs: Define default component data
Step 4: Building Your First Game
Let's create a simple game step by step:
1. Set Up Your Scene Configuration
First, configure your scenes in scenes.manage.json
:
{
"initialScene": "MainMenu",
"scenes": {
"MainMenu": "MainMenu.scene.json",
"GameLevel": "GameLevel.scene.json"
}
}
2. Create a Main Menu Scene
Create MainMenu.scene.json
:
{
"name": "MainMenu",
"path": "MainMenu.scene.json",
"systems": [],
"gameObjects": [
{
"name": "StartButton",
"blueprint": {
"name": "Button",
"path": "Button.blueprint.json"
},
"components": [
{
"name": "SpriteComponent",
"data": {
"texture_path": "start-button.png",
"position": { "x": 400, "y": 300 },
"scale": { "x": 1, "y": 1 },
"anchor": 0.5
}
},
{
"name": "MouseComponent",
"data": {
"buttons": { "left": false, "right": false, "middle": false }
}
}
]
}
]
}
3. Create a Button Blueprint
Create Button.blueprint.json
:
{
"name": "Button",
"path": "Button.blueprint.json",
"components": [
{
"name": "SpriteComponent",
"data": {
"position": { "x": 0, "y": 0 },
"scale": { "x": 1, "y": 1 },
"rotation": 0,
"alpha": 1,
"visible": true,
"anchor": 0.5
}
},
{
"name": "MouseComponent",
"data": {
"screenPosition": { "x": 0, "y": 0 },
"windowPosition": { "x": 0, "y": 0 },
"buttons": { "left": false, "right": false, "middle": false }
}
}
]
}
4. Create a Game Level Scene
Create GameLevel.scene.json
:
{
"name": "GameLevel",
"path": "GameLevel.scene.json",
"systems": ["MovementSystem"],
"gameObjects": [
{
"name": "Player",
"blueprint": {
"name": "Player",
"path": "Player.blueprint.json"
},
"components": [
{
"name": "SpriteComponent",
"data": {
"texture_path": "player.png",
"position": { "x": 100, "y": 300 },
"scale": { "x": 1, "y": 1 }
}
}
]
}
]
}
Step 5: Adding Custom Components
1. Create a Custom Component
Create PlayerMovement.component.ts
:
import type {
ComponentInstanceManage,
ComponentSerialized,
} from "../../__Engine__/Component/ComponentInstanceManage";
export interface PlayerMovementData {
speed?: number;
direction: { x: number; y: number };
}
export interface PlayerMovementComponent {
speed: number;
direction: { x: number; y: number };
}
export default {
name: "PlayerMovementComponent",
create: (data: PlayerMovementData): PlayerMovementComponent => ({
speed: data.speed ?? 100,
direction: { ...data.direction },
}),
serialize: (
component: PlayerMovementComponent,
): ComponentSerialized<"PlayerMovementComponent", PlayerMovementData> => ({
name: "PlayerMovementComponent",
data: {
speed: component.speed,
direction: component.direction,
},
}),
} as ComponentInstanceManage<"PlayerMovementComponent", PlayerMovementData, PlayerMovementComponent>;
2. Register the Component
Add to component.manage.json
:
{
"ExampleComponent": "ExampleComponent.component.js",
"PlayerMovementComponent": "PlayerMovement.component.js"
}
Step 6: Adding Custom Systems
1. Create a Movement System
Create Movement.system.ts
:
import type { SpriteComponent } from "../../__Engine__/Component/Drawable/SpriteComponent";
import type { System } from "../../__Engine__/Systems/System";
import type { TypeEngine } from "../../__Engine__/TypeEngine";
import type { PlayerMovementComponent } from "./PlayerMovement.component";
export class MovementSystem implements System<TypeEngine> {
name = "MovementSystem";
priority = 1;
enabled = true;
async init(_engine: TypeEngine): Promise<void> {
// never use
}
update(engine: TypeEngine, deltaTime: number): void {
const entities = engine.EntityEngine.query<{
SpriteComponent: SpriteComponent[];
PlayerMovementComponent: PlayerMovementComponent[];
}>(["SpriteComponent", "PlayerMovementComponent"]);
for (const { components } of entities) {
for (let i = 0; i < components.SpriteComponent.length; i++) {
const sprite = components.SpriteComponent[i];
const movement = components.PlayerMovementComponent[i];
// Update position based on movement
sprite.position.x += movement.direction.x * movement.speed * deltaTime;
sprite.position.y += movement.direction.y * movement.speed * deltaTime;
}
}
}
}
2. Register the System
Add to system.manage.json
:
{
"ExampleSystem": "ExampleSystem.system.js",
"MovementSystem": "Movement.system.js"
}
Step 7: Testing Your Game
1. Build and Run
# Type check your code
pnpm test:type
# Lint your code
pnpm lint
# Run tests
pnpm test
# Build the game
pnpm build
# Start development mode
pnpm dev
2. Development Workflow
- Write Tests First: Create
.spec.ts
files for your components and systems - Red Phase: Ensure tests fail initially
- Green Phase: Implement minimal code to pass tests
- Refactor Phase: Improve code while maintaining test coverage
Best Practices
File Organization
- Keep related blueprints and scenes together
- Use descriptive names for your files
- Organize assets in subdirectories if needed
Component Design
- Keep components focused on single responsibilities
- Use primitive objects (interfaces) instead of classes
- Implement proper serialization/deserialization
- Components should be pure data structures
System Architecture
- Systems should have
name
,priority
,enabled
,init
, andupdate
methods enabled
should always be set totrue
init
method should be implemented but never used (comment:// never use
)- No additional state should be stored in systems
- Engine is always passed as first argument to update method
- Use deltaTime for frame-rate independent updates
- Avoid tight coupling between systems
Scene Management
- Design scenes for specific game states (menu, gameplay, pause)
- Use systems array to enable/disable logic per scene
- Keep scene files focused and manageable
Next Steps
Now that you understand the basics:
- Explore Advanced Components: Learn about physics components and input components
- Study System Architecture: Dive deeper into system management and the SystemEngine
- Understand Entity Lifecycle: Learn about entity creation and management
- Build Complex Scenes: Experiment with scene transitions and multi-scene games
File Extensions
Remember that management files (.manage.json
) reference .js
files, not .ts
files. The build process compiles TypeScript to JavaScript automatically.
Asset Management
Place all game assets (images, sounds) directly in the src/__Project__/
folder or organized subdirectories for easy referencing in your components.