Just Game Engine
A comprehensive 2D game engine built for Flutter, providing everything you need to create high-performance games with rich visual effects, animations, and physics.
📚 Documentation
- Quick Start Guide - Get started in 5 minutes
- API Reference - Detailed API documentation for all classes
- Changelog - Version history and release notes
Features
Just Game Engine is a complete game development framework with 11 major subsystems:
🎮 Core Engine
- Game Loop: Fixed timestep (60 UPS) with variable rendering for consistent gameplay
- State Management: Full lifecycle management (initialize, start, pause, resume, stop)
- Time Management: Delta time, time scaling, FPS tracking
- System Coordination: Centralized management of all subsystems
🎨 Rendering Engine
- 2D Canvas-Based Rendering: High-performance drawing with Flutter's Canvas API
- Renderable Objects: Circles, rectangles, lines, text, and custom renderables
- Camera System: Pan, zoom, and rotation with smooth transforms
- Layer Management: Z-order sorting for proper depth rendering
- Debug Visualization: Bounding boxes, coordinate grids, and performance metrics
🖼️ Sprite System
- Image Rendering: Load and display images with easy asset management
- Sprite Sheets: Efficiently manage multiple sprites in a single texture
- Nine-Slice Scaling: Scalable UI elements that maintain corner details
- Flipping: Horizontal and vertical sprite flipping
✨ Animation System
- Sprite Animations: Frame-based animations from sprite sheets with customizable frame rates
SpriteAnimationclass for sprite sheet playbackfromSpriteSheet()factory for easy sprite animation creation- Independent frame count and duration per animation
- Automatic frame calculation from sprite dimensions
- Property Tweening: Animate position, rotation, scale, opacity, and color
- Easing Functions: 15+ built-in easing curves (linear, quad, cubic, elastic, bounce, etc.)
- Animation Sequences: Chain animations to play one after another
- Animation Groups: Run multiple animations in parallel
- Loop and Ping-Pong: Repeat animations infinitely or bounce back and forth
- Speed Control: Adjust animation playback speed dynamically (0.1x - 5.0x)
💥 Particle Effects
- Particle Emitters: Configurable emission rate, lifetime, and spawning
- Visual Properties: Size gradients, color gradients, velocity, and gravity
- Particle Shapes: Circles, squares, and stars
- Built-in Presets: Explosion, fire, smoke, sparkle, rain, and snow effects
- Custom Particles: Create your own particle systems
⚛️ Physics Engine
- Rigid Body Dynamics: Position, velocity, mass, and drag simulation
- Collision Detection: Circular collision detection
- Collision Resolution: Elastic collisions with restitution
- Debug Rendering: Visualize physics bodies and velocity vectors
- Performance: Optimized broad-phase and narrow-phase collision detection
🌳 Scene Graph
- Hierarchical Structure: Parent-child node relationships
- Transform Propagation: Automatic world-space transform calculation
- Scene Management: Create, load, and manage multiple scenes
- Node Queries: Find nodes by name or traverse the tree
- Attachable Renderables: Link visual objects to scene nodes
🧩 Entity-Component System (ECS)
- Data-Oriented Architecture: Composition over inheritance for flexible entity design
- Entity Management: Create and destroy entities with unique IDs
- Component System: 13 built-in components (Transform, Velocity, Physics, Health, etc.)
- System Processing: 9 built-in systems for movement, rendering, physics, and more
- Query System: Find entities by component types
- World Management: Centralized entity and system coordination
- Hierarchy Support: Parent-child entity relationships
� Input Management
- Keyboard Input: Key press, hold, and release detection with axis support
- Mouse Input: Position tracking, button states, scroll wheel, and delta movement
- Touch Input: Multi-touch support with pressure and size tracking
- Controller/Gamepad: Analog sticks, triggers, buttons, and D-pad support
- Event System: Callbacks for custom input handling
- Dead Zone: Configurable dead zones for analog inputs
- Integrated: Automatic event capture through GameWidget with Focus and Listener
🎵 Additional Systems
-
Audio Engine: Complete audio playback system with multi-channel mixing
- Multi-Channel: Master, Music, SFX, Voice, and Ambient channels with independent volume control
- Sound Effects: Player pooling (10 concurrent SFX) with automatic cleanup
- Music: Background music with fade in/out effects and seamless looping
- Audio Mixer: Per-channel volume control, mute/unmute, and master volume
- Integration: Built on audioplayers package for cross-platform support
-
Asset Management: Efficient loading and caching of game resources
- Image Assets: Load PNG/JPG images with memory tracking
- Audio Assets: Load MP3/WAV/OGG/FLAC audio files as binary data
- Text Assets: Load plain text files from asset bundle
- JSON Assets: Load and parse JSON configuration files
- Binary Assets: Load raw binary data for custom formats
- Caching: Automatic asset caching with memory usage statistics
- Asset Bundles: Group multiple assets for batch loading/unloading
-
Networking: Multiplayer and server communication (placeholder)
Getting Started
Prerequisites
- Flutter SDK 3.11.0 or higher
- Dart 3.0.0 or higher
Installation
Add this to your package's pubspec.yaml file:
dependencies:
just_game_engine:
path: ../packages/just_game_engine # Adjust path as needed
Then run:
flutter pub get
Usage
Basic Setup
import 'package:flutter/material.dart';
import 'package:just_game_engine/just_game_engine.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize the engine
final engine = Engine();
await engine.initialize();
// Set up your game
setupGame(engine);
// Start the engine
engine.start();
// Run your Flutter app
runApp(MyGameApp(engine: engine));
}
void setupGame(Engine engine) {
// Add renderables
engine.rendering.addRenderable(
CircleRenderable(
radius: 50,
fillColor: Colors.blue,
position: Offset.zero,
),
);
}
Creating Animations
// Sprite animation from sprite sheet
final sprite = Sprite();
sprite.image = spriteSheetImage;
sprite.renderSize = const Size(64, 64);
sprite.position = const Offset(0, 0);
final spriteAnim = SpriteAnimation.fromSpriteSheet(
sprite: sprite,
frameCount: 8, // 8 frames
frameWidth: 64, // Each frame 64px wide
frameHeight: 64, // Each frame 64px tall
duration: 1.0, // Total duration: 1 second
loop: true,
);
engine.animation.addAnimation(spriteAnim);
spriteAnim.play();
// Position tween
final moveAnim = PositionTween(
target: myRenderable,
start: Offset(-100, 0),
end: Offset(100, 0),
duration: 2.0,
easing: Easings.easeInOutQuad,
loop: true,
);
moveAnim.play();
// Rotation animation
final rotateAnim = RotationTween(
target: myRenderable,
start: 0,
end: math.pi * 2,
duration: 3.0,
easing: Easings.linear,
loop: true,
);
rotateAnim.play();
// Animation sequence
final sequence = AnimationSequence(
animations: [animation1, animation2, animation3],
loop: true,
);
sequence.play();
// Control animation speed
moveAnim.speed = 2.0; // 2x speed
spriteAnim.speed = 0.5; // Half speed
Adding Particle Effects
// Create a fire effect
final fireEmitter = ParticleEffects.fire(
position: Offset(100, 200),
)..emissionRate = 30;
particles.add(fireEmitter);
// Create an explosion
final explosion = ParticleEffects.explosion(
position: Offset(0, 0),
);
particles.add(explosion);
// Render particles
rendering.addRenderable(
CustomRenderable(
onRender: (canvas, size) {
for (final emitter in particles) {
emitter.update(deltaTime);
emitter.render(canvas, size);
}
},
),
);
Using the Scene Graph
// Create a scene
final scene = engine.sceneEditor.createScene('Level1');
// Create parent node
final parentNode = SceneNode('parent')
..localPosition = Offset(100, 100);
scene.addNode(parentNode);
// Create child node
final childNode = SceneNode('child')
..localPosition = Offset(50, 0)
..renderable = myCircle;
parentNode.addChild(childNode);
// Child automatically inherits parent's transform
Using the Entity-Component System
// Access the ECS World
final world = engine.world;
// Add systems
world.addSystem(MovementSystem());
world.addSystem(RenderSystem());
world.addSystem(PhysicsSystemECS()..gravity = const Offset(0, 100));
// Create an entity
final player = world.createEntity(name: 'Player');
// Add components
player.addComponent(TransformComponent(
position: const Offset(0, 0),
));
player.addComponent(VelocityComponent(
velocity: const Offset(100, 0),
maxSpeed: 200,
));
player.addComponent(RenderableComponent(
renderable: CircleRenderable(
radius: 30,
fillColor: Colors.blue,
),
));
player.addComponent(PhysicsBodyComponent(
radius: 30,
mass: 1.0,
restitution: 0.8,
));
player.addComponent(HealthComponent(maxHealth: 100));
// Query entities by components
final movingEntities = world.query([TransformComponent, VelocityComponent]);
for (final entity in movingEntities) {
debugPrint('Entity ${entity.name} can move!');
}
// Find specific entity
final enemy = world.findEntityByName('Enemy');
if (enemy != null) {
enemy.getComponent<HealthComponent>()?.damage(10);
}
Physics Simulation
// Create physics bodies
final body1 = PhysicsBody(
position: Offset(-100, 0),
velocity: Offset(50, 0),
radius: 30,
mass: 1.0,
restitution: 0.8,
);
engine.physics.addBody(body1);
// Enable debug rendering
rendering.addRenderable(
CustomRenderable(
onRender: (canvas, size) {
engine.physics.renderDebug(canvas, size);
},
),
);
Camera Controls
// Move camera
engine.rendering.camera.moveBy(Offset(10, 0));
// Zoom
engine.rendering.camera.zoomBy(1.1);
// Reset camera
engine.rendering.camera.reset();
// Look at position
engine.rendering.camera.lookAt(Offset(100, 100));
Using Input System
final input = engine.input;
// Keyboard input
if (input.keyboard.isKeyDown(LogicalKeyboardKey.keyW)) {
// W key is currently held down
player.moveUp();
}
if (input.keyboard.isKeyPressed(LogicalKeyboardKey.space)) {
// Space was just pressed this frame
player.jump();
}
// Get axis input for smooth movement
final horizontal = input.keyboard.horizontal; // -1, 0, or 1
final vertical = input.keyboard.vertical; // -1, 0, or 1
player.move(Offset(horizontal, vertical) * speed);
// Mouse input
final mousePos = input.mouse.position;
final worldPos = engine.rendering.camera.screenToWorld(mousePos, screenSize);
if (input.mouse.isLeftButtonDown) {
// Left mouse button is held
player.shootAt(worldPos);
}
if (input.mouse.isButtonPressed(MouseButton.right)) {
// Right mouse button was just clicked
spawnObject(worldPos);
}
// Mouse wheel zoom
if (input.mouse.scrollDelta.dy != 0) {
engine.rendering.camera.zoomBy(1.0 - input.mouse.scrollDelta.dy * 0.001);
}
// Touch input
for (final touch in input.touch.touches) {
// Handle each active touch point
debugPrint('Touch at ${touch.position}');
}
// Gamepad input
final leftStick = input.controller.leftStick;
player.move(leftStick * speed);
if (input.controller.isButtonPressed(GamepadButton.a)) {
player.jump();
}
Loading Assets
// Create asset manager
final assetManager = AssetManager();
// Load different asset types
final playerImage = await assetManager.loadImage('assets/images/player.png');
final backgroundMusic = await assetManager.loadAudio('assets/audio/music.mp3');
final gameConfig = await assetManager.loadJson('assets/data/config.json');
final levelData = await assetManager.loadText('assets/data/level1.txt');
// Access loaded data
final sprite = Sprite();
sprite.image = playerImage.image;
final configMap = gameConfig.data as Map<String, dynamic>;
final playerSpeed = configMap['player']['speed'];
// Check cache stats
final stats = assetManager.getCacheStats();
debugPrint('Loaded ${stats['totalAssets']} assets');
debugPrint('Memory usage: ${stats['totalMemory']} bytes');
// Load asset bundles
final levelBundle = AssetBundle(
name: 'Level1',
assets: [
ImageAsset(path: 'assets/level1/bg.png'),
AudioAsset(path: 'assets/level1/music.mp3'),
JsonAsset(path: 'assets/level1/data.json'),
],
);
await levelBundle.load(assetManager);
// Unload when done
levelBundle.unload(assetManager);
Playing Audio
// Initialize audio engine
final audioEngine = AudioEngine();
await audioEngine.initialize();
// Play background music with fade in
await audioEngine.playMusic(
'assets/audio/background.mp3',
volume: 0.7,
loop: true,
fadeInDuration: 2.0,
);
// Play sound effects
audioEngine.playSfx('assets/audio/jump.wav', volume: 0.8);
audioEngine.playSfx('assets/audio/shoot.wav');
audioEngine.playSfx('assets/audio/explosion.wav', volume: 1.0);
// Control volume
audioEngine.setMasterVolume(0.8);
audioEngine.setChannelVolume(AudioChannel.music, 0.5);
audioEngine.setChannelVolume(AudioChannel.sfx, 1.0);
// Mute/unmute
audioEngine.muteChannel(AudioChannel.music);
audioEngine.toggleMute(); // Toggle master mute
// Stop music with fade out
audioEngine.stopMusic(fadeOutDuration: 1.5);
// Pause and resume
audioEngine.pauseMusic();
audioEngine.resumeMusic();
Architecture
Just Game Engine
├── Core Engine
│ ├── Engine (Main orchestrator)
│ ├── GameLoop (Fixed timestep loop)
│ ├── TimeManager (Delta time tracking)
│ └── SystemManager (Subsystem coordination)
├── Rendering Engine
│ ├── RenderingEngine (Canvas rendering)
│ ├── Camera (View transformation)
│ ├── Renderable (Base class)
│ └── GameWidget (Flutter integration)
├── Sprite System
│ ├── Sprite (Image rendering)
│ ├── SpriteSheet (Texture atlas)
│ └── NineSliceSprite (Scalable UI)
├── Animation System
│ ├── Animation (Base class)
│ ├── SpriteAnimation (Frame-based)
│ ├── TweenAnimation (Property lerp)
│ └── Easings (Curve functions)
├── Particle Effects
│ ├── ParticleEmitter (Emission control)
│ ├── Particle (Individual particle)
│ └── ParticleEffects (Presets)
├── Physics Engine
│ ├── PhysicsEngine (Simulation)
│ └── PhysicsBody (Rigid body)
├── Scene Graph
│ ├── SceneEditor (Scene management)
│ ├── Scene (Node container)
│ └── SceneNode (Transform hierarchy)
├── Entity-Component System
│ ├── World (Entity management)
│ ├── Entity (Component container)
│ ├── Component (Data storage)
│ ├── System (Processing logic)
│ ├── Built-in Components:
│ │ ├── TransformComponent
│ │ ├── VelocityComponent
│ │ ├── RenderableComponent
│ │ ├── PhysicsBodyComponent
│ │ ├── HealthComponent
│ │ └── 8 more...
│ └── Built-in Systems:
│ ├── MovementSystem
│ ├── RenderSystem
│ ├── PhysicsSystemECS
│ └── 6 more...
├── Input Management
│ ├── InputManager (Main coordinator)
│ ├── KeyboardInput (Key states)
│ ├── MouseInput (Position, buttons, scroll)
│ ├── TouchInput (Multi-touch)
│ └── ControllerInput (Gamepad support)
├── Asset Management
│ ├── AssetManager (Loading & caching)
│ ├── ImageAsset (PNG/JPG)
│ ├── AudioAsset (MP3/WAV/OGG/FLAC)
│ ├── TextAsset (Plain text)
│ ├── JsonAsset (JSON config)
│ ├── BinaryAsset (Raw data)
│ └── AssetBundle (Grouped loading)
├── Audio Engine
│ ├── AudioEngine (Multi-channel mixer)
│ ├── AudioClip (Playback control)
│ ├── SoundEffectManager (SFX)
│ ├── MusicManager (Background music)
│ └── AudioMixer (Volume control)
└── Additional Systems
└── Networking (Placeholder)
Performance Tips
- Use Object Pooling: Reuse particles and projectiles instead of creating new ones
- Limit Renderables: Only render what's visible on screen
- Batch Rendering: Group similar draw calls together
- Profile Regularly: Use Flutter DevTools to identify bottlenecks
- Optimize Collision Detection: Use spatial partitioning for many objects
- Cache Calculations: Store frequently used values like cos/sin results
Examples
Check out the example/ folder for complete examples:
core_system_example.dart- Basic engine setup and usageecs_example.dart- Entity-Component System with physics and collisionsinput_test_example.dart- Complete input system demo (keyboard, mouse, touch)
API Reference
Core Classes
Engine- Main engine singletonGameLoop- Game loop with fixed timestepTimeManager- Time tracking and delta time
Rendering Classes
RenderingEngine- 2D rendering systemCamera- Camera transformation and controlsRenderable- Base class for all renderablesCircleRenderable,RectangleRenderable,LineRenderable,TextRenderable,CustomRenderable
Animation Classes
Animation- Base animation classSpriteAnimation- Frame-based sprite animationPositionTween,RotationTween,ScaleTween,OpacityTween,ColorTweenAnimationSequence- Sequential animationsAnimationGroup- Parallel animationsEasings- Easing function library
Particle Classes
ParticleEmitter- Particle emission controllerParticle- Individual particle instanceParticleEffects- Built-in effect presets
Physics Classes
PhysicsEngine- Physics simulationPhysicsBody- Rigid body with collision
Scene Classes
SceneEditor- Scene managementScene- Scene containerSceneNode- Hierarchical transform node
ECS Classes
World- Entity and system managerEntity- Component container with unique IDComponent- Base class for data componentsSystem- Base class for processing systems- Built-in Components:
TransformComponent,VelocityComponent,RenderableComponent,PhysicsBodyComponent,HealthComponent,LifetimeComponent,TagComponent,ParentComponent,ChildrenComponent,InputComponent,AnimationStateComponent,SpriteComponent - Built-in Systems:
MovementSystem,RenderSystem,PhysicsSystemECS,LifetimeSystem,HierarchySystem,HealthSystem,AnimationSystemECS,BoundarySystem
Input Classes
InputManager- Main input coordinator with keyboard, mouse, touch, and controller accessKeyboardInput- Key press/hold/release detection with axis supportMouseInput- Mouse position, buttons, scroll, and delta trackingTouchInput- Multi-touch support with touch pointsControllerInput- Gamepad analog sticks, triggers, and buttonsMouseButton- Mouse button constants (left, right, middle)GamepadButton- Gamepad button constants (A, B, X, Y, etc.)GamepadAxis- Gamepad axis identifiers (left stick, right stick, triggers)
Asset Management Classes
AssetManager- Asset loading and caching coordinatorAsset- Base class for all asset typesImageAsset- Image asset loader (PNG/JPG)AudioAsset- Audio asset loader (MP3/WAV/OGG/FLAC)TextAsset- Plain text file loaderJsonAsset- JSON configuration file loaderBinaryAsset- Raw binary data loaderAssetBundle- Grouped asset loading/unloading
Audio Engine Classes
AudioEngine- Multi-channel audio playback coordinatorAudioClip- Individual audio source controllerSoundEffectManager- Sound effect playback wrapperMusicManager- Background music control with fade effectsAudioMixer- Volume and mute control interfaceAudioChannel- Audio channel enum (master, music, sfx, voice, ambient)AudioState- Playback state enum (stopped, playing, paused)
Dependencies
- Flutter SDK: 3.11.0 or higher
- Dart SDK: 3.0.0 or higher
- audioplayers: ^6.1.0 (for Audio Engine)
Contributing
Contributions are welcome! This engine is in active development.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Authors
- Just Unknown - Initial work
Acknowledgments
- Built with Flutter
- Inspired by Unity, Godot, and Flame engines
Libraries
- just_game_engine
- Just Game Engine for Flutter