A lightweight 2D game engine built with Matter.js for physics and Canvas for rendering. Features a component-based architecture, scene management, and UI system.
Screen.Recording.2024-12-30.at.22.51.33.mp4
- Physics Engine: Powered by Matter.js for accurate 2D physics simulation
- Scene Management: Easy scene transitions and level loading
- Component System: Flexible entity-component architecture
- Input Handling: Keyboard input with configurable controls
- Camera System: Smooth camera following with deadzone
- Abstracted rendering interface for multiple backends
- Efficient Canvas renderer with double buffering
- Separate world and UI rendering contexts
- Built-in performance monitoring
- Debug visualization system
- State change tracking for optimization
- FPS and render statistics
- Simple UI framework with buttons and text elements
- Screen-space rendering with no camera transforms
- Event handling and hit testing
- Clipping support for scrollable areas
- Consistent styling system
- Sprite loading and animation support
- Category-based collision filtering
- Built-in debug visualization for:
- Physics bodies
- Velocities and forces
- Entity states
- Performance metrics
- Sound system with music and effects
- JSON-based level definition format
- Prefab system for reusable entity templates
- Layered object organization
- Flexible property system
- Asset and animation configuration
- Physics property customization
- Install dependencies:
npm install- Generate sprite assets:
npm run generate-sprites- Start development server:
npm run devA side-scrolling platformer showcasing:
- Physics-based movement
- Moving platforms
- Enemy AI
- Collectibles
- Trigger zones
Controls:
- Arrow keys: Move left/right
- Space: Jump
- X: Attack
A top-down maze game demonstrating:
- Top-down physics
- Collision detection
- Smooth movement
Controls:
- Arrow keys: Move in all directions
An interactive physics sandbox featuring:
- Dynamic object spawning
- Different physics materials
- Object interactions
Controls:
- Space: Spawn random objects
m2d-engine/
├── src/
│ ├── core/ # Engine core components
│ │ ├── m2d.js # Main engine class
│ │ ├── scene.js # Scene management
│ │ ├── entity.js # Base entity class
│ │ ├── Camera.js # Camera system
│ │ └── ui/ # UI components
│ ├── actors/ # Game-specific actors
│ ├── constraints/ # Entity constraints/components
│ └── examples/ # Example implementations
├── scripts/ # Build and utility scripts
└── assets/ # Game assets and sprites
Levels are defined in JSON format with the following structure:
{
"version": "1.0",
"type": "level",
"name": "level1",
"title": "Level 1",
"gameType": "platformer",
"world": {
"gravity": { "x": 0, "y": 1 }
},
"assets": {
"spritesheets": {
"player": {
"url": "path/to/sprite.png",
"frameWidth": 32,
"frameHeight": 32,
"animations": {
"idle": { "frames": [[0, 0]] },
"run": { "frames": [[1, 0], [2, 0]] }
}
}
}
},
"prefabs": {
"player": {
"actor": "Player",
"spritesheet": "player",
"physics": {
"bodyType": "dynamic",
"friction": 0.1,
"frictionStatic": 0.5
}
},
"movingPlatform": {
"actor": "MovingPlatform",
"spritesheet": "platform",
"physics": {
"bodyType": "kinematic",
"friction": 1
}
}
},
"layers": [
{
"name": "main",
"objects": [
{
"prefab": "player",
"x": 100,
"y": 100,
"width": 32,
"height": 32,
"properties": {}
},
{
"prefab": "movingPlatform",
"x": 200,
"y": 200,
"width": 96,
"height": 32,
"properties": {
"path": {
"points": [
{"x": 200, "y": 200},
{"x": 400, "y": 200}
],
"speed": 3,
"waitTime": 1000
}
}
}
]
}
]
}- version: Level format version
- type: Scene type ("level" or "ui")
- world: Physics world configuration
- assets: Sprite and audio assets used in the level
- prefabs: Reusable entity definitions with physics and sprite properties
- layers: Groups of game objects with their positions and properties
- bodyType: Physics body type ("static", "dynamic", or "kinematic")
- friction/frictionStatic: Surface friction properties
- isSensor: Whether the object triggers collisions without physical response
- path: Movement path for platforms (points, speed, waitTime)
- triggers: Actions or events triggered by collision
All scenes in M2D Engine must be registered with a fetch method that returns the scene configuration. The scene manager uses this method to load scene data asynchronously.
Register a scene using addScene:
// Loading a level scene from a JSON file
game.sceneManager.addScene('level1', {
fetch: async () => (await import('./scenes/level1.json')).default,
onEnter() {
game.soundManager.playMusic('game');
}
});
// Loading a UI scene from a JSON file
game.sceneManager.addScene('mainMenu', {
fetch: async () => (await import('./scenes/mainMenu.json')).default,
onEnter() {
game.soundManager.playMusic('menu');
}
});The scene configuration:
- Must include a
fetchmethod that returns the scene data - Can include lifecycle hooks like
onEnter - Supports lazy loading through dynamic imports
- Can load data from any async source (files, API, etc.)
- Is cached after first load for faster scene switching
Switch between scenes using:
await game.sceneManager.switchTo('sceneName');This will:
- Unload the current scene if one exists
- Load the new scene using its
fetchmethod - Initialize the scene with assets and entities
- Call the scene's
onEnterhook if defined
- Create a new scene file extending the base Scene class
- Define your game actors extending the Entity class
- Set up your scene configuration with sprites and entities
- Register your actors and scenes with the engine
Example:
// Create game instance
const game = new M2D(canvas, {
width: 1280,
height: 720,
initialScene: 'mainMenu'
});
// Register actors
game.registerActor('player', Player);
game.registerActor('enemy', Enemy);
// Register scenes
game.sceneManager.addScene('mainMenu', {
fetch: async () => (await import('./scenes/mainMenu.json')).default
});
game.sceneManager.addScene('level1', {
fetch: async () => (await import('./scenes/level1.json')).default
});
// Start the game
game.start();ISC