Skip to content
Draft

Game Development

Draft provides game development capabilities through its performance and cross-platform support.

Draft’s native compilation, no-GC design, and cross-platform support make it suitable for game development.

Terminal window
draft new my-game --template game
cd my-game

Project structure:

my-game/
├── draft.toml
├── assets/
│ ├── sprites/
│ ├── sounds/
│ └── shaders/
├── src/
│ ├── main.draft
│ ├── game.draft
│ ├── player.draft
│ └── enemy.draft
└── draft.toml
import game
action main()
@engine = @game#Engine::new("My Game", 800, 600)
@engine#on_start(fn() =>
load_assets()
spawn_player()
fin action.)
@engine#on_update(fn(@dt) =>
update_physics(@dt)
update_entities(@dt)
check_collisions()
fin action.)
@engine#on_render(fn() =>
clear_screen()
draw_entities()
draw_ui()
fin action.)
@engine#run()
fin action.
import game
record Entity
x: Float
y: Float
vx: Float
vy: Float
sprite: Sprite
health: Int
fin record.
impl Entity
action update(@self: mut Entity, @dt: Float)
@self#x = @self#x + @self#vx * @dt
@self#y = @self#y + @self#vy * @dt
fin action.
action draw(@self: Entity)
@game#draw_sprite(@self#sprite, @self#x, @self#y)
fin action.
fin impl.
import game
action load_sprites()
@player_sprite = @game#load_sprite("assets/player.png")
@enemy_sprite = @game#load_sprite("assets/enemy.png")
@bullet_sprite = @game#load_sprite("assets/bullet.png")
fin action.
action draw_player(@player: Entity)
@game#draw_sprite(@player#sprite, @player#x, @player#y)
fin action.
import game
action handle_input(@player: mut Entity)
if @game#key_pressed(@game#KEY_LEFT)
@player#vx = -200
else if @game#key_pressed(@game#KEY_RIGHT)
@player#vx = 200
else
@player#vx = 0
fin if.
if @game#key_pressed(@game#KEY_SPACE)
shoot(@player)
fin if.
fin action.
import game
action handle_gamepad()
@pad = @game#gamepad(0)
if @pad#button_pressed(@game#BUTTON_A)
jump()
fin if.
@axis_x = @pad#axis(@game#AXIS_LEFT_X)
@axis_y = @pad#axis(@game#AXIS_LEFT_Y)
fin action.
import game
action update_physics(@dt: Float)
for @entity in @entities
@entity#vy = @entity#vy + @game#GRAVITY * @dt
@entity#x = @entity#x + @entity#vx * @dt
@entity#y = @entity#y + @entity#vy * @dt
fin for.
fin action.
import game
action check_collisions()
for @bullet in @bullets
for @enemy in @enemies
if @game#collides(@bullet, @enemy)
@enemy#health = @enemy#health - 1
@bullets#remove(@bullet)
fin if.
fin for.
fin for.
fin action.
import game
@rect = @game#Rect::new(@x, @y, @width, @height)
@circle = @game#Circle::new(@x, @y, @radius)
if @game#rect_collides(@rect_a, @rect_b)
handle_collision()
fin if.
if @game#circle_collides(@circle_a, @circle_b)
handle_collision()
fin if.
import game
action load_audio()
@jump_sound = @game#load_sound("assets/jump.wav")
@explosion_sound = @game#load_sound("assets/explosion.wav")
@bgm = @game#load_music("assets/bgm.ogg")
fin action.
action play_sounds()
@game#play_sound(@jump_sound)
@game#play_music(@bgm, loop: true)
fin action.
import game
action create_explosion(@x: Float, @y: Float)
@emitter = @game#ParticleEmitter::new()
@emitter#position(@x, @y)
@emitter#count(100)
@emitter#speed(100, 300)
@emitter#life(0.5, 1.5)
@emitter#color(@game#RED, @game#YELLOW)
@emitter#emit()
fin action.
import game
action setup_camera()
@camera = @game#Camera::new()
@camera#follow(@player)
@camera#zoom(2.0)
@camera#bounds(0, 0, 2000, 2000)
fin action.
import game
action load_level()
@tilemap = @game#Tilemap::new("assets/level1.tmx")
@tilemap#layer("background")
@tilemap#layer("collision")
@tilemap#layer("foreground")
fin action.
action draw_tilemap()
@game#draw_tilemap(@tilemap, @camera#x, @camera#y)
fin action.
import game
action draw_hud()
@game#draw_text("Score: " + string(@score), 10, 10)
@game#draw_text("Lives: " + string(@lives), 10, 30)
@game#draw_health_bar(@player#health, 10, 50, 200, 20)
fin action.
import game
action main()
@engine = @game#Engine::new("My Game", 800, 600)
@engine#add_scene("menu", fn() => MenuScene::new())
@engine#add_scene("game", fn() => GameScene::new())
@engine#add_scene("gameover", fn() => GameOverScene::new())
@engine#switch_scene("menu")
@engine#run()
fin action.
import game
action load_shaders()
@shader = @game#Shader::new()
@shader#vertex("assets/shaders/vertex.glsl")
@shader#fragment("assets/shaders/fragment.glsl")
@game#use_shader(@shader)
fin action.
import game
import websocket
action connect_to_server()
@ws = @websocket#connect("ws://game-server.example.com")
@ws#on_message(fn(@data) =>
handle_network_message(@data)
fin action.)
fin action.
action send_position(@x: Float, @y: Float)
@ws#send(json#stringify({
"type", "position",
"x", @x,
"y", @y
}))
fin action.
import game
import json
action save_game()
@save_data = {
"level", @current_level,
"score", @score,
"lives", @lives,
"position", {@player#x, @player#y}
}
@game#save("save.dat", @json#stringify(@save_data))
fin action.
action load_game()
@data = @game#load("save.dat")
@save = @json#parse(@data)
@current_level = @save["level"]
@score = @save["score"]
fin action.
import game
record BulletPool
bullets: Array<Bullet>
active: Int
fin record.
impl BulletPool
action new(@size: Int) -> BulletPool
@bullets = []
for @i in 0..@size
@bullets#push(Bullet::new())
fin for.
return BulletPool { bullets: @bullets, active: 0 }
fin action.
action get(@self: mut BulletPool) -> Bullet?
if @self#active < @self#bullets#length
@bullet = @self#bullets[@self#active]
@self#active = @self#active + 1
return @bullet
fin if.
return none
fin action.
fin impl.
import game
action setup_spatial_hash()
@grid = @game#SpatialHash::new(64) @// Cell size
for @entity in @entities
@grid#insert(@entity)
fin for.
fin action.
action query_nearby(@x: Float, @y: Float, @radius: Float) -> Array<Entity>
return @grid#query(@x, @y, @radius)
fin action.
Terminal window
draft build --target native --release
Terminal window
draft build --target wasm --release
Terminal window
draft build --target mobile --release
Terminal window
@// Desktop
draft deploy --target windows
draft deploy --target macos
draft deploy --target linux
@// Web
draft deploy --target wasm --hosting itch.io
@// Mobile
draft deploy --target ios
draft deploy --target android
Metric Target
Frame rate 60 FPS
Frame time < 16ms
Draw calls < 1000
Memory < 256 MB
Load time < 3s