Lua for Game Development — Chapter 2: Language Essentials

A practical, game-oriented guide to Lua syntax, tables, functions, closures, coroutines, and debugging—focused on real-world use inside engines.

Lua’s syntax is small, elegant, and optimized for scripting game logic.
This chapter teaches Lua through a game development lens, focusing on features you will actually use when scripting AI, animations, UI, events, gameplay systems, or tools.

This is not a language textbook.
This is Lua for building real games.

1. The Building Blocks of Lua

Lua’s core types:

  • nil
  • boolean
  • number
  • string
  • tablethe most important type
  • functionfirst-class citizen

There are no classes, arrays, or objects—tables represent all of these.

2. Variables and Scope

Lua variables default to global, unless declared with local.

x = 10      -- global
local y = 20 -- local

Always use local in game code:

  • Faster (local lookups are optimized)
  • Safer (prevents global pollution)
  • Avoids subtle bugs

Best practice for game scripts:

local enemy_speed = 120
local max_hp = 80

Globals are only for shared constants:

GAME_VERSION = "1.0"

3. Tables: The Heart of Lua Game Programming

Lua tables are:

  • Dictionaries
  • Arrays
  • Objects
  • Components
  • Configuration structures
  • JSON-like data
  • ECS storage

Everything starts from tables.

3.1 Tables as Arrays

local enemies = { "goblin", "slime", "orc" }

print(enemies[1])  -- goblin

Remember: Lua arrays start at index 1, not 0.

3.2 Tables as Dictionaries

local stats = {
  hp = 100,
  attack = 12,
  speed = 4.5,
}

print(stats.hp)

3.3 Tables as Game Objects

Lua doesn’t have classes, but you can simulate objects easily.

local player = {
  x = 0,
  y = 0,
  hp = 100
}

function player:move(dx, dy)
  self.x = self.x + dx
  self.y = self.y + dy
end

4. Functions: The Workhorse of Game Logic

Functions are first-class, meaning they are:

  • Values
  • Passed as arguments
  • Returned from functions
  • Stored inside tables

This is why Lua excels at scripting.

4.1 Basic function

local function attack(target)
  print("Attacking", target)
end

Equivalent:

local attack = function(target)
  print("Attacking", target)
end

4.2 Anonymous functions (useful in events & UI)

button:onClick(function()
  print("clicked!")
end)

4.3 Multiple return values

Perfect for game logic:

local function damage(hp, amount)
  local new_hp = hp - amount
  local dead = new_hp <= 0
  return new_hp, dead
end

local hp, dead = damage(30, 50)

4.4 Closures (critical for timers, AI, state)

Closures “capture” variables:

local function create_counter()
  local n = 0
  return function()
    n = n + 1
    return n
  end
end

local counter = create_counter()
print(counter()) -- 1
print(counter()) -- 2

Many game features rely on closures:

  • AI memory
  • UI state
  • Animation systems
  • Cooldown timers
  • Internal state machines

5. Metatables: Custom Behavior for Game Objects

Metatables allow:

  • Operator overloading
  • Prototype inheritance
  • Default values
  • State machines
  • Custom lookup logic

5.1 Vector Example (common in games)

local Vec = {}
Vec.__index = Vec

function Vec.new(x, y)
  return setmetatable({x=x, y=y}, Vec)
end

function Vec.__add(a, b)
  return Vec.new(a.x + b.x, a.y + b.y)
end

local v1 = Vec.new(3, 4)
local v2 = Vec.new(1, 2)
local v3 = v1 + v2   -- operator overloaded

print(v3.x, v3.y)

You can extend this system to physics, movement, transforms, or camera logic.

5.2 Default Values via Metatable

Useful for entity configs:

local defaults = {hp=100, speed=3}
local mt = { __index = defaults }

local goblin = setmetatable({}, mt)
print(goblin.hp)   -- 100

6. Coroutines: The Secret to Game-Friendly Scripting

Lua coroutines are:

  • Cheaper than OS threads
  • More flexible than async/await
  • Ideal for AI, cutscenes, timelines, and animation logic

6.1 Basic coroutine

local co = coroutine.create(function()
  print("Step 1")
  coroutine.yield()
  print("Step 2")
end)

coroutine.resume(co)
coroutine.resume(co)

6.2 Coroutines as Timelines (SUPER important)

local function timeline()
  play("idle")
  wait(1)
  play("attack")
  wait(0.2)
  play("recover")
end

Game designers LOVE this style of code, which is why countless studios adopt Lua.

7. Error Handling and Debugging

Game scripting must fail gracefully.

7.1 pcall: protected call

local ok, err = pcall(function()
  error("boom")
end)

print(ok, err)

Used in:

  • Script hot reload
  • Sandbox/modding
  • Safe mission scripting

7.2 xpcall with traceback

local ok, err = xpcall(function()
  error("bad!")
end, debug.traceback)

print(err)

8. Logging & Debug Utilities

During development:

local function log(msg)
  print("[LOG]", msg)
end

log("Player spawned")

Production engines replace this with:

  • On-screen debug console
  • File logging
  • Remote log sinks

9. Putting It All Together: A Mini Scripted Enemy

This combines:

  • tables
  • functions
  • closures
  • timers via coroutines
  • simple AI state
local enemy = {
  x = 0,
  hp = 60,
}

function enemy:attack()
  print("enemy attacks!")
end

function enemy:run_ai()
  print("idle")
  wait(0.5)

  print("walk")
  self.x = self.x + 1
  wait(1)

  print("attack")
  self:attack()
  wait(0.2)

  print("done")
end

local co = coroutine.create(function() enemy:run_ai() end)

while coroutine.status(co) ~= "dead" do
  coroutine.resume(co)
end

This is the foundation of real AI scripts used in:

  • Defold
  • Roblox
  • Cocos
  • LÖVE
  • Custom native engines

10. Summary of Chapter 2

You learned:

  • Lua core syntax from a game development viewpoint
  • Tables as objects, arrays, configs, and components
  • Functions + closures powering game logic
  • Metatables for custom behavior
  • Coroutines for timelines and AI
  • Basic debugging and error-handling patterns
  • A complete mini enemy script

These skills form the backbone of all real Lua-based games.

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