Lua for Game Development — Sample Game

Lua for Game Development is a calm, practical book for developers who want to use Lua as a real production tool: game loops, ECS, AI, UI, multiplayer, tooling, and deployment — with clean examples and engine-agnostic patterns.

A calm, practical handbook for building real games with Lua.

Lua is small, fast, embeddable, and battle-tested in countless shipped titles.
This book walks you from the very basics to shipping a production-ready game:

  • core language & tooling
  • game loops, scenes, and entities
  • UI, inventory, quests, AI, and world systems
  • multiplayer netcode & lag compensation
  • ECS, engine architecture & optimization
  • packaging, localization, and release pipelines

All examples are written in engine-agnostic Lua, so you can adapt them to Defold, LÖVE, Cocos2d-x, a custom C++ engine, or even a server-side runtime.

What You’ll Learn

Language & Foundations

  • Clean, modern Lua syntax and patterns
  • Tables, metatables, and light-weight OOP
  • Modules, project structure, and code organization
  • Coroutines and timeline scripting

Core Game Systems

  • Game loops, scenes, and state management
  • Entities, movement, collision helpers
  • UI systems, HUD, menus & data binding
  • Inventory, items, equipment, and stats
  • Dialogue trees, quests, and story flags
  • NPC routines, towns, factions and reputation

AI, Combat & Simulation

  • Combat AI with FSM, Behavior Trees, Utility AI
  • Squad tactics and boss phase scripting
  • Town and economy simulation
  • Timeline-driven cutscenes and cinematic effects

Multiplayer & Netcode

  • Deterministic lockstep for RTS/SLG
  • Snapshot interpolation for action games
  • Client-side prediction and reconciliation
  • Lag compensation and server rewind
  • Entity replication and anti-cheat basics

Engine & Tooling

  • ECS (Entity-Component-System) architecture
  • Plugin/module system and hot reload
  • Profiling, debugging, and memory optimisation
  • Asset pipelines, data-driven content, and mod support
  • Save systems, replays, and world persistence

Packaging & Release

  • Project layout and build pipelines
  • Asset bundling and Lua bytecode packaging
  • Localization and i18n structure
  • Patch/updates, DLC and mod distribution
  • CI/CD integration for game builds

Who This Book Is For

  • Engine devs embedding Lua into a custom runtime or C++ engine
  • Indie devs using Defold, LÖVE, Cocos2d-x, Roblox/Luau or similar
  • Backend/server devs building Lua-driven game logic (SLG/MMO/rooms)
  • Tool builders and scripters who want clean, robust patterns

You don’t need to be a Lua expert — the book starts from zero and builds up progressively to advanced systems.

Structure & Navigation

The book is split into multiple chapters, each self-contained but designed to stack together:

  • Chapters 1–6: Lua language & game programming fundamentals
  • Chapters 7–12: Core gameplay systems (UI, items, quests, story)
  • Chapters 13–16: NPCs, combat AI, multiplayer netcode & sync models
  • Chapters 17–19: Save systems, ECS, tools, packaging & release pipeline

Use the sidebar navigation (or the chapter list) to jump into any section.

How to Use This Book

You can:

  • read it linearly from Chapter 1 to 19, or
  • dip into systems you care about right now (e.g., just AI or netcode), or
  • use it as a design reference for your own engine/architecture.

Each chapter includes:

  • engine-agnostic patterns
  • clear Lua code snippets
  • suggestions for integrating with popular engines
  • notes on performance, determinism, and production pitfalls

Source Code & Project Skeleton

A matching GitHub project skeleton is available:

  • engine/ — Lua ECS, systems, scheduler, net stubs
  • game/ — sample game (scenes, entities, UI, quests)
  • content/ — items, NPCs, quests, maps, localization
  • tools/ — build scripts, asset packers, localization checkers

You can adapt it directly or just copy patterns into your own repo.

Ready to Build?

Start with the introduction, wire up the sample engine, and ship a small prototype — then expand into AI, multiplayer, and polishing.

Happy hacking, and have fun making games with Lua.

Below is a GitHub-ready project structure with:

  • Directory tree
  • Short description of each folder
  • Ready-to-commit README, .gitignore, minimal engine and game entry points
  • A simple GitHub Actions CI workflow
  • Basic tools/build script placeholder

You can literally copy this into a new repo called lua-game-dev (or whatever name you like) and start filling in code.

1. Directory Tree

lua-game-dev/
├── README.md
├── LICENSE
├── .gitignore
├── .editorconfig
├── .github/
│   └── workflows/
│       └── ci.yml
├── engine/
│   ├── core/
│   │   ├── engine.lua
│   │   ├── ecs.lua
│   │   ├── event_bus.lua
│   │   └── scheduler.lua
│   ├── systems/
│   │   ├── movement_system.lua
│   │   ├── render_system.lua
│   │   ├── physics_system.lua
│   │   └── combat_system.lua
│   ├── util/
│   │   ├── vector.lua
│   │   ├── tween.lua
│   │   └── serialize.lua
│   └── net/
│       ├── net_common.lua
│       ├── server.lua
│       └── client.lua
├── game/
│   ├── main.lua
│   ├── config/
│   │   ├── settings.lua
│   │   └── keybindings.lua
│   ├── scenes/
│   │   ├── boot_scene.lua
│   │   ├── menu_scene.lua
│   │   └── game_scene.lua
│   ├── entities/
│   │   ├── player.lua
│   │   ├── enemy_goblin.lua
│   │   └── npc_elder.lua
│   ├── ui/
│   │   ├── hud.lua
│   │   ├── inventory_ui.lua
│   │   └── dialogue_ui.lua
│   └── scripts/
│       ├── quests/
│       │   └── main_01.lua
│       └── cutscenes/
│           └── intro.lua
├── content/
│   ├── items/
│   │   ├── weapons.lua
│   │   ├── armor.lua
│   │   └── consumables.lua
│   ├── npcs/
│   │   └── villagers.lua
│   ├── quests/
│   │   └── main_quests.lua
│   ├── maps/
│   │   └── village_map.lua
│   └── localization/
│       ├── en.lua
│       └── zh.lua
├── assets/
│   ├── sprites/
│   ├── tilesets/
│   ├── audio/
│   └── fonts/
├── tools/
│   ├── build.lua
│   ├── dev_console.lua
│   ├── pack_assets.lua
│   └── check_localization.lua
├── build/
│   ├── debug/
│   └── release/
└── docs/
    ├── BOOK.md
    ├── lua-game-dev-structure.md
    └── design/
        ├── ecs-design.md
        ├── netcode-design.md
        └── save-system-design.md

You can trim or expand this to match your engine of choice (Defold, LÖVE, custom, etc.).

2. Root README.md

# Lua Game Dev — Engine + Sample Game

This repository is a **GitHub-ready skeleton** for the "Lua for Game Development" book:

- A small but realistic **Lua engine layer** (ECS, systems, scheduler)
- A **sample game** that uses the engine (scenes, entities, UI, quests)
- A **content pipeline** (items, NPCs, quests, maps, localization)
- Basic **tools** for building, packing, and validating content
- Hooks for **multiplayer**, **save systems**, and **modding**

You can use this as:

- A reference implementation for the book
- A starting point for your own Lua game
- A sandbox to experiment with ECS, AI, netcode, and tools


## Repository Layout

```text
engine/   # Core engine: ECS, systems, utilities, netcode
game/     # Game-specific scripts: scenes, entities, UI, quest logic
content/  # Data-driven content: items, NPCs, quests, maps, localization
assets/   # Art/audio/fonts (raw or packed)
tools/    # Build scripts, asset packers, dev console, checkers
build/    # Build artifacts (debug/release bundles)
docs/     # Book markdown, design docs, and technical notes
.github/  # CI workflows

Quick Start

Assumption: You have Lua (or LuaJIT) installed and some engine/runtime that can run game/main.lua.

  1. Clone the repo:
git clone https://github.com/yourname/lua-game-dev.git
cd lua-game-dev
  1. Run the game (generic Lua runtime):
lua game/main.lua

Or integrate engine/ + game/ into your engine of choice (Defold, LÖVE, custom C++ host, etc.).

Engine Overview

The engine/ folder contains:

  • core/engine.lua — main game loop & scene handling
  • core/ecs.lua — minimal ECS (entities, components, systems)
  • core/event_bus.lua — global event dispatcher
  • core/scheduler.lua — coroutine-based task/timeline system
  • systems/* — example systems (movement, rendering, physics, combat)
  • util/* — math helpers, tweening, serialization
  • net/* — stubs for server/client & netcode experiments

You can either use this directly or adapt it to your own engine.

Game Overview

The game/ folder includes:

  • main.lua — entry point that wires engine + game
  • config/ — settings & keybindings
  • scenes/ — boot, menu, gameplay scenes
  • entities/ — player, enemies, NPCs
  • ui/ — HUD, inventory, dialogue UI
  • scripts/ — quests, cutscenes

It is intentionally small, but follows patterns from the book so you can extend it.

Tools

The tools/ folder is where all support scripts live:

  • build.lua — orchestrates build steps (pack scripts/assets into build/)
  • pack_assets.lua — packs sprites/audio/etc. into bundles (PAK/ZIP/etc.)
  • dev_console.lua — simple REPL-style dev console helper
  • check_localization.lua — validates localization keys across languages

These are meant as starting points; expand as you build your own pipeline.

Docs

See docs/ for:

  • BOOK.md — links to all book chapters (Hugo content or raw markdown)
  • design/* — ECS, netcode, save/load, and other design notes

CI / GitHub Actions

Under .github/workflows/ci.yml, a minimal CI job runs:

  • basic Lua syntax checking (stub)
  • placeholder build step (you can extend to run tests, linting, etc.)

License

Choose a license and put it in LICENSE (MIT, Apache-2.0, etc.).
By default, this skeleton assumes you’ll pick MIT or similar.

Next Steps

  • Plug in a real engine (Defold, LÖVE, C++ host, etc.)
  • Flesh out engine/core/ecs.lua, engine/core/engine.lua
  • Implement a simple scene in game/scenes/game_scene.lua
  • Hook input → player movement → rendering
  • Add a tiny level in content/maps/village_map.lua
  • Ship a small, complete prototype

3. .gitignore

# LuaGameDev generic ignore

# OS
.DS_Store
Thumbs.db

# Editors
.vscode/
.idea/
*.swp
*.swo

# Build output
/build/
dist/
*.log

# LuaRocks
*.rock
*.rockspec

# Temporary assets
assets/tmp/
assets/cache/

# Hugo site / generated docs (if you build a site)
public/
resources/

4. Minimal Engine Core

engine/core/engine.lua

-- engine/core/engine.lua
local Engine = {}
Engine.__index = Engine

function Engine.new()
  return setmetatable({
    scenes = {},
    current = nil,
    running = true,
  }, Engine)
end

function Engine:register_scene(name, scene)
  self.scenes[name] = scene
end

function Engine:switch_scene(name, ...)
  local s = self.scenes[name]
  assert(s, "Unknown scene: " .. tostring(name))
  if self.current and self.current.leave then
    self.current:leave()
  end
  self.current = s
  if self.current.enter then
    self.current:enter(...)
  end
end

function Engine:update(dt)
  if self.current and self.current.update then
    self.current:update(dt)
  end
end

function Engine:draw()
  if self.current and self.current.draw then
    self.current:draw()
  end
end

return Engine

engine/core/ecs.lua (tiny starting point)

-- engine/core/ecs.lua
local ECS = {
  next_id = 1,
  entities = {},
  components = {},
  systems = {}
}

function ECS:create_entity()
  local id = self.next_id
  self.next_id = id + 1
  self.entities[id] = true
  return id
end

function ECS:add_component(id, name, data)
  self.components[name] = self.components[name] or {}
  self.components[name][id] = data
end

function ECS:get_component(id, name)
  local c = self.components[name]
  return c and c[id]
end

function ECS:remove_entity(id)
  self.entities[id] = nil
  for _, comps in pairs(self.components) do
    comps[id] = nil
  end
end

function ECS:add_system(fn)
  table.insert(self.systems, fn)
end

function ECS:update(dt)
  for _, sys in ipairs(self.systems) do
    sys(self, dt)
  end
end

return ECS

5. Minimal Game Entry Point

game/main.lua

-- game/main.lua
-- Entry point: wires engine + game scenes

local Engine = require("engine.core.engine")

local engine = Engine.new()

-- Simple scenes
local boot_scene = require("game.scenes.boot_scene")
local menu_scene = require("game.scenes.menu_scene")
local game_scene = require("game.scenes.game_scene")

engine:register_scene("boot", boot_scene)
engine:register_scene("menu", menu_scene)
engine:register_scene("game", game_scene)

engine:switch_scene("boot")

-- Fake main loop for plain Lua (replace with real engine loop)
local last = os.clock()
while engine.running do
  local now = os.clock()
  local dt = now - last
  last = now

  engine:update(dt)
  engine:draw()

  -- crude framerate cap
  if dt < 1/60 then
    local sleep = 1/60 - dt
    if sleep > 0 then
      -- naive busy-wait sleep
      local t0 = os.clock()
      while os.clock() - t0 < sleep do end
    end
  end
end

Example Boot Scene

game/scenes/boot_scene.lua:

local boot = {}

function boot:enter()
  print("[Boot] Initializing game...")
  -- Load content, config, etc. here (or show a loading screen).
end

function boot:update(dt)
  -- For now, just jump to menu immediately.
  local Engine = require("engine.core.engine") -- or pass engine via global/context
  -- In a real engine, you'd have a global or injected context so you don't require here.
  -- This is just a placeholder.
end

function boot:draw()
  -- no-op; replace with your engine's rendering calls
end

return boot

You’ll likely adjust how engine is passed into scenes (via DI, global context, or a shared module).

6. Simple CI Workflow (.github/workflows/ci.yml)

name: CI

on:
  push:
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Lua
        uses: leafo/gh-actions-lua@v10
        with:
          luaVersion: "5.4"

      - name: Lua syntax check (basic)
        run: |
          find engine game content tools -name "*.lua" -print0 | xargs -0 -n1 lua -p

      - name: Placeholder build
        run: |
          echo "Run your build pipeline here (tools/build.lua, etc.)"

7. Minimal Build Tool (tools/build.lua)

-- tools/build.lua
-- Placeholder build orchestrator

local function run(cmd)
  print("[build] " .. cmd)
  os.execute(cmd)
end

local function main()
  print("== Lua Game Dev Build ==")
  -- Example: pack content, copy engine, etc.
  -- You will adapt this to your actual engine/runtime.
  run("mkdir -p build/debug")
  -- TODO: add pack_assets.lua invocation, bytecode compilation, etc.
end

main()

Keep Reading

Follow the engineering thread

Get the next practical Birdor note, or browse the archive for related systems, tooling, and architecture work.

Join newsletter Browse articles