jugar-yaml
ELI5 YAML-First declarative game definitions. Define entire games in YAML.
Overview
jugar-yaml provides a declarative way to define games using YAML configuration files. This follows the "ELI5" (Explain Like I'm 5) philosophy - making game development accessible.
Basic Structure
game:
title: "My First Game"
width: 800
height: 600
background: "#1a1a2e"
entities:
- name: player
position: [400, 300]
sprite: "player.png"
components:
- type: velocity
value: [0, 0]
- type: collider
shape: circle
radius: 20
- name: enemy
position: [100, 100]
sprite: "enemy.png"
ai:
behavior: patrol
waypoints:
- [100, 100]
- [700, 100]
- [700, 500]
- [100, 500]
Loading Games
#![allow(unused)] fn main() { use jugar_yaml::prelude::*; // Load from file let game = GameDefinition::load("game.yaml")?; // Create engine from definition let engine = JugarEngine::from_definition(game); engine.run(game_loop); }
Components
Define components declaratively:
entities:
- name: ball
position: [400, 300]
components:
# Physics
- type: rigid_body
mass: 1.0
restitution: 0.9
# Rendering
- type: sprite
texture: "ball.png"
scale: [1.0, 1.0]
# Custom data
- type: custom
data:
health: 100
damage: 10
Behaviors
AI behaviors in YAML:
entities:
- name: guard
ai:
behavior: behavior_tree
tree:
type: selector
children:
- type: sequence
children:
- type: condition
check: "health < 20"
- type: action
do: flee
- type: sequence
children:
- type: condition
check: "sees_player"
- type: action
do: attack
- type: action
do: patrol
Input Mapping
input:
actions:
move_up:
- key: W
- key: ArrowUp
- gamepad: LeftStickUp
move_down:
- key: S
- key: ArrowDown
- gamepad: LeftStickDown
fire:
- key: Space
- mouse: Left
- gamepad: South
Scenes
Define multiple scenes:
scenes:
main_menu:
entities:
- name: title
position: [400, 200]
text: "My Game"
font_size: 48
- name: start_button
position: [400, 350]
button:
text: "Start"
action: load_scene:gameplay
gameplay:
entities:
- name: player
# ...
Templates
Reusable entity templates:
templates:
coin:
sprite: "coin.png"
components:
- type: collider
shape: circle
radius: 15
- type: custom
data:
value: 10
entities:
- template: coin
position: [100, 200]
- template: coin
position: [200, 200]
- template: coin
position: [300, 200]
components:
- type: custom
data:
value: 50 # Override
Physics World
physics:
gravity: [0, 980]
iterations: 8
static_bodies:
- name: ground
position: [400, 580]
shape:
type: box
size: [800, 40]
- name: left_wall
position: [0, 300]
shape:
type: box
size: [20, 600]
Validation
The YAML is validated at load time:
#![allow(unused)] fn main() { let result = GameDefinition::load("game.yaml"); match result { Ok(game) => { /* valid */ } Err(YamlError::InvalidPosition(msg)) => { println!("Position error: {}", msg); } Err(YamlError::MissingField(field)) => { println!("Missing required field: {}", field); } // ... } }
Example: Complete Pong
game:
title: "YAML Pong"
width: 800
height: 600
physics:
gravity: [0, 0]
entities:
- name: ball
position: [400, 300]
components:
- type: velocity
value: [300, 200]
- type: collider
shape: circle
radius: 10
- name: paddle_left
position: [50, 300]
components:
- type: collider
shape: box
size: [10, 80]
input:
up: W
down: S
- name: paddle_right
position: [750, 300]
components:
- type: collider
shape: box
size: [10, 80]
ai:
behavior: follow_ball
speed: 200
ui:
- name: score
position: [400, 50]
text: "0 - 0"
font_size: 32