Lua 语言示例

Lua编程示例,用于游戏开发和嵌入式脚本

💻 Lua Hello World lua

🟢 simple

Lua Hello World程序和基本语法示例

⏱️ 15 min 🏷️ lua, programming, beginner, game development, scripting
Prerequisites: Basic programming concepts
-- Lua Hello World Examples

-- 1. Basic Hello World
print("Hello, World!")

-- 2. Hello World with variable
local message = "Hello, World!"
print(message)

-- 3. Hello World with function
function sayHello()
    return "Hello, World!"
end

print(sayHello())

-- 4. Hello World with function parameters
function greet(name)
    return "Hello, " .. name .. "!"
end

print(greet("World"))
print(greet("Lua"))

-- 5. Hello World with table
local greeter = {
    message = "Hello, World!"
}

function greeter:sayHello()
    return self.message
end

print(greeter:sayHello())

-- 6. Hello World with class-like structure
local Greeter = {}
Greeter.__index = Greeter

function Greeter.new(message)
    local self = setmetatable({}, Greeter)
    self.message = message or "Hello, World!"
    return self
end

function Greeter:greet()
    return self.message
end

local greeter1 = Greeter.new()
local greeter2 = Greeter.new("Hello from custom class!")

print(greeter1:greet())
print(greeter2:greet())

-- 7. Hello World multiple times
for i = 1, 5 do
    print("Hello, World! " .. i)
end

-- 8. Hello World with table
local greetings = {"Hello", "Bonjour", "Hola", "Ciao", "こんにちは"}

for _, greeting in ipairs(greetings) do
    print(greeting .. ", World!")
end

-- 9. Hello World with map-like table
local greetings = {
    en = "Hello",
    es = "Hola",
    fr = "Bonjour",
    de = "Hallo",
    ja = "こんにちは"
}

for lang, greeting in pairs(greetings) do
    print(greeting .. ", World! (" .. lang .. ")")
end

-- 10. Hello World with user input simulation
-- Note: In a real environment, you might use io.read()
function simulateUserInput()
    local userName = "Lua User"  -- Simulated input
    return "Hello, " .. userName .. "!"
end

print(simulateUserInput())

-- Basic data types examples
-- Numbers (Lua has only one number type: double)
local integer = 42
local float = 3.14
local scientific = 1.23e-4

-- Strings
local singleQuote = 'Hello'
local doubleQuote = "World"
local multiline = [[
This is a multiline string
that can span multiple lines
without escaping.
]]

-- Booleans
local isTrue = true
local isFalse = false

-- Nil (represents absence of value)
local nilValue = nil

-- Tables (main data structure)
local emptyTable = {}
local list = {1, 2, 3, 4, 5}
local map = {name = "Lua", version = "5.4", awesome = true}

print("Data types:")
print("Integer:", integer, "Type:", type(integer))
print("Float:", float, "Type:", type(float))
print("String:", singleQuote, "Type:", type(singleQuote))
print("Boolean:", isTrue, "Type:", type(isTrue))
print("Nil:", nilValue, "Type:", type(nilValue))
print("Table:", list, "Type:", type(list))

-- Control flow examples
local age = 18

-- If-else statement
if age >= 18 then
    print("You are an adult")
else
    print("You are a minor")
end

-- If-elseif-else
local score = 85
local grade

if score >= 90 then
    grade = "A"
elseif score >= 80 then
    grade = "B"
elseif score >= 70 then
    grade = "C"
else
    grade = "F"
end

print("Score:", score, "Grade:", grade)

-- Loop examples
-- Numeric for loop
print("For loop:")
for i = 1, 3 do
    print("Iteration:", i)
end

-- While loop
print("While loop:")
local count = 0
while count < 3 do
    print("Count:", count)
    count = count + 1
end

-- Repeat-until loop
print("Repeat-until loop:")
local repeatCount = 0
repeat
    print("Repeat count:", repeatCount)
    repeatCount = repeatCount + 1
until repeatCount >= 3

-- Functions as first-class citizens
-- Anonymous function
local multiply = function(a, b)
    return a * b
end

print("5 * 3 =", multiply(5, 3))

-- Function that returns multiple values
function getMinMax(numbers)
    local min = numbers[1]
    local max = numbers[1]

    for _, num in ipairs(numbers) do
        if num < min then min = num end
        if num > max then max = num end
    end

    return min, max
end

local numbers = {5, 2, 8, 1, 9, 3}
local minVal, maxVal = getMinMax(numbers)
print("Min:", minVal, "Max:", maxVal)

-- String manipulation
local text = "Hello, Lua World!"

print("Original:", text)
print("Upper:", string.upper(text))
print("Lower:", string.lower(text))
print("Length:", #text)
print("Substring:", string.sub(text, 1, 5))
print("Find Lua:", string.find(text, "Lua"))

-- String concatenation and formatting
local name = "Lua"
local version = "5.4"
local formatted = string.format("Language: %s, Version: %s", name, version)
print(formatted)

-- Table operations
-- Lists (arrays)
local fruits = {"apple", "banana", "cherry", "date"}

print("Fruits list:")
for i, fruit in ipairs(fruits) do
    print(i, fruit)
end

-- Add to end
table.insert(fruits, "elderberry")
print("After insert:", table.concat(fruits, ", "))

-- Remove from end
local removed = table.remove(fruits)
print("Removed:", removed)
print("Remaining:", table.concat(fruits, ", "))

-- Map-like tables
local person = {
    name = "John Doe",
    age = 30,
    city = "New York",
    skills = {"programming", "gaming", "music"}
}

print("Person information:")
print("Name:", person.name)
print("Age:", person.age)
print("City:", person.city)
print("Skills:", table.concat(person.skills, ", "))

-- Dynamic table access
local key = "age"
print("Dynamic access:", person[key])

-- Table iteration
print("Person details:")
for key, value in pairs(person) do
    if type(value) ~= "table" then
        print(key .. ":", value)
    end
end

-- Error handling with pcall
function riskyOperation(shouldFail)
    if shouldFail then
        error("Something went wrong!")
    end
    return "Success!"
end

-- Safe execution
local success, result = pcall(riskyOperation, false)
if success then
    print("Operation result:", result)
else
    print("Operation failed:", result)
end

local success, result = pcall(riskyOperation, true)
if success then
    print("Operation result:", result)
else
    print("Operation failed:", result)
end

-- Variable scope examples
local globalVar = "I am global"  -- Available everywhere

function testScope()
    local localVar = "I am local"  -- Available only in this function
    print("Inside function - Global:", globalVar)
    print("Inside function - Local:", localVar)
end

testScope()
print("Outside function - Global:", globalVar)
-- print(localVar)  -- This would cause an error: localVar is not defined

-- Metatables demonstration
local vector = {x = 3, y = 4}

-- Metatable for vector operations
local vectorMeta = {
    __add = function(a, b)
        return {x = a.x + b.x, y = a.y + b.y}
    end,
    __tostring = function(v)
        return "(" .. v.x .. ", " .. v.y .. ")"
    end
}

setmetatable(vector, vectorMeta)

local vector2 = {x = 1, y = 2}
setmetatable(vector2, vectorMeta)

local result = vector + vector2
print("Vector addition:", tostring(result))

💻 Lua 表和元表 lua

🟡 intermediate ⭐⭐⭐⭐

高级表操作、元表和Lua中的面向对象编程

⏱️ 30 min 🏷️ lua, tables, metatables, oop, data structures
Prerequisites: Lua basics, Understanding of metatables, Object-oriented concepts
-- Lua Tables and Metatables Examples

-- 1. Advanced Table Operations

-- Table as array/list
local numbers = {10, 20, 30, 40, 50}

-- Array operations
function arrayCopy(arr)
    local copy = {}
    for i, v in ipairs(arr) do
        copy[i] = v
    end
    return copy
end

function arrayFilter(arr, predicate)
    local result = {}
    for i, v in ipairs(arr) do
        if predicate(v) then
            table.insert(result, v)
        end
    end
    return result
end

function arrayMap(arr, transform)
    local result = {}
    for i, v in ipairs(arr) do
        result[i] = transform(v)
    end
    return result
end

function arrayReduce(arr, accumulator, initial)
    local result = initial
    for _, v in ipairs(arr) do
        result = accumulator(result, v)
    end
    return result
end

-- Test array operations
local doubled = arrayMap(numbers, function(x) return x * 2 end)
local evens = arrayFilter(numbers, function(x) return x % 2 == 0 end)
local sum = arrayReduce(numbers, function(acc, x) return acc + x end, 0)

print("Original:", table.concat(numbers, ", "))
print("Doubled:", table.concat(doubled, ", "))
print("Even numbers:", table.concat(evens, ", "))
print("Sum:", sum)

-- 2. Table as dictionary/map
local person = {
    name = "Alice",
    age = 30,
    city = "New York",
    email = "[email protected]",
    hobbies = {"reading", "hiking", "coding"}
}

-- Dictionary operations
function tableCopy(t)
    local copy = {}
    for k, v in pairs(t) do
        if type(v) == "table" then
            copy[k] = tableCopy(v)
        else
            copy[k] = v
        end
    end
    return copy
end

function tableKeys(t)
    local keys = {}
    for k, _ in pairs(t) do
        table.insert(keys, k)
    end
    return keys
end

function tableValues(t)
    local values = {}
    for _, v in pairs(t) do
        table.insert(values, v)
    end
    return values
end

function tableMerge(t1, t2)
    local result = tableCopy(t1)
    for k, v in pairs(t2) do
        result[k] = v
    end
    return result
end

-- Test dictionary operations
local keys = tableKeys(person)
local values = tableValues(person)
local extended = tableMerge(person, {country = "USA", phone = "555-1234"})

print("Person keys:", table.concat(keys, ", "))
print("Extended person:")
for k, v in pairs(extended) do
    print("  " .. k .. ":", type(v) == "table" and table.concat(v, ", ") or v)
end

-- 3. Stack and Queue implementations

-- Stack implementation using table
local Stack = {}
Stack.__index = Stack

function Stack.new()
    return setmetatable({}, Stack)
end

function Stack:push(item)
    table.insert(self, item)
end

function Stack:pop()
    if #self == 0 then
        return nil
    end
    return table.remove(self)
end

function Stack:peek()
    if #self == 0 then
        return nil
    end
    return self[#self]
end

function Stack:isEmpty()
    return #self == 0
end

function Stack:size()
    return #self
end

-- Test Stack
local stack = Stack.new()
stack:push("first")
stack:push("second")
stack:push("third")

print("Stack size:", stack:size())
print("Peek:", stack:peek())
print("Pop:", stack:pop())
print("Pop:", stack:pop())
print("Is empty:", stack:isEmpty())

-- Queue implementation using table
local Queue = {}
Queue.__index = Queue

function Queue.new()
    return setmetatable({head = 0, tail = -1}, Queue)
end

function Queue:enqueue(item)
    self.tail = self.tail + 1
    self[self.tail] = item
end

function Queue:dequeue()
    if self.head > self.tail then
        return nil
    end
    local value = self[self.head]
    self[self.head] = nil  -- Free memory
    self.head = self.head + 1
    return value
end

function Queue:isEmpty()
    return self.head > self.tail
end

function Queue:size()
    if self:isEmpty() then
        return 0
    end
    return self.tail - self.head + 1
end

-- Test Queue
local queue = Queue.new()
queue:enqueue("Alice")
queue:enqueue("Bob")
queue:enqueue("Charlie")

print("Queue size:", queue:size())
print("Dequeue:", queue:dequeue())
print("Dequeue:", queue:dequeue())
print("Queue size after dequeues:", queue:size())

-- 4. Metatables - The Magic of Lua

-- Basic metatable example
local myTable = {a = 10, b = 20}

-- Create metatable with __index method
local myMeta = {
    __index = function(table, key)
        print("Accessing missing key:", key)
        return "default value"
    end,

    __newindex = function(table, key, value)
        print("Setting new key:", key, "to:", value)
        rawset(table, key, value)
    end
}

setmetatable(myTable, myMeta)

print(myTable.c)  -- Triggers __index
myTable.c = 30    -- Triggers __newindex
print(myTable.c)  -- Regular access

-- 5. Operator overloading with metatables

-- Vector class
local Vector = {}
Vector.__index = Vector

function Vector.new(x, y)
    local self = setmetatable({x = x or 0, y = y or 0}, Vector)
    return self
end

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

function Vector.__sub(a, b)
    return Vector.new(a.x - b.x, a.y - b.y)
end

function Vector.__mul(a, scalar)
    if type(a) == "table" and type(scalar) == "number" then
        return Vector.new(a.x * scalar, a.y * scalar)
    elseif type(a) == "number" and type(scalar) == "table" then
        return Vector.new(a * scalar.x, a * scalar.y)
    end
    error("Invalid multiplication")
end

function Vector.__eq(a, b)
    return math.abs(a.x - b.x) < 0.0001 and math.abs(a.y - b.y) < 0.0001
end

function Vector.__tostring(v)
    return string.format("(%g, %g)", v.x, v.y)
end

function Vector:magnitude()
    return math.sqrt(self.x * self.x + self.y * self.y)
end

function Vector:normalize()
    local mag = self:magnitude()
    if mag > 0 then
        return Vector.new(self.x / mag, self.y / mag)
    end
    return Vector.new(0, 0)
end

function Vector:dot(other)
    return self.x * other.x + self.y * other.y
end

-- Test Vector class
local v1 = Vector.new(3, 4)
local v2 = Vector.new(1, 2)

print("Vector1:", v1)
print("Vector2:", v2)
print("Addition:", v1 + v2)
print("Subtraction:", v1 - v2)
print("Multiplication:", v1 * 2)
print("Magnitude:", v1:magnitude())
print("Normalized:", v1:normalize())
print("Dot product:", v1:dot(v2))

-- 6. Read-only table proxy
function makeReadOnly(t)
    local proxy = {}
    local meta = {
        __index = t,
        __newindex = function(t, k, v)
            error("Attempt to modify read-only table")
        end,
        __pairs = function()
            return pairs(t)
        end,
        __ipairs = function()
            return ipairs(t)
        end
    }
    setmetatable(proxy, meta)
    return proxy
end

local config = {
    max_connections = 100,
    timeout = 30,
    debug = false
}

local readonlyConfig = makeReadOnly(config)

print("Max connections:", readonlyConfig.max_connections)
-- readonlyConfig.max_connections = 200  -- This would throw an error

-- 7. Table with default values
function withDefaults(defaults)
    local meta = {
        __index = defaults
    }
    return setmetatable({}, meta)
end

local gameSettings = withDefaults({
    volume = 0.5,
    difficulty = "normal",
    fullscreen = false,
    resolution = {width = 1920, height = 1080}
})

print("Volume:", gameSettings.volume)
gameSettings.volume = 0.8  -- This overwrites the default
print("New volume:", gameSettings.volume)
print("Resolution width:", gameSettings.resolution.width)

-- 8. Chainable interface (fluent interface)
local StringBuilder = {}
StringBuilder.__index = StringBuilder

function StringBuilder.new()
    return setmetatable({buffer = {}}, StringBuilder)
end

function StringBuilder:add(text)
    table.insert(self.buffer, text)
    return self
end

function StringBuilder:addLine(text)
    table.insert(self.buffer, text or "")
    return self
end

function StringBuilder:clear()
    self.buffer = {}
    return self
end

function StringBuilder:toString()
    return table.concat(self.buffer)
end

function StringBuilder:length()
    return #self.buffer
end

-- Test StringBuilder
local sb = StringBuilder.new()
local result = sb
    :add("Hello")
    :add(" ")
    :add("World")
    :addLine()
    :add("This is Lua")
    :addLine("StringBuilder example")
    :toString()

print("StringBuilder result:")
print(result)

-- 9. Weak tables for caching
local cache = setmetatable({}, {__mode = "v"})  -- Weak values

function expensiveComputation(x)
    if cache[x] then
        print("Cache hit for", x)
        return cache[x]
    end

    print("Computing for", x)
    local result = x * x * x  -- Simulate expensive operation
    cache[x] = result
    return result
end

print("Result 1:", expensiveComputation(10))
print("Result 2:", expensiveComputation(10))
print("Result 3:", expensiveComputation(20))
print("Result 4:", expensiveComputation(10))

-- 10. Observable table pattern
function makeObservable(t, onChange)
    local proxy = {}
    local meta = {
        __index = function(_, k)
            return t[k]
        end,

        __newindex = function(_, k, v)
            local oldValue = t[k]
            t[k] = v
            if onChange and oldValue ~= v then
                onChange(k, oldValue, v)
            end
        end,

        __pairs = function()
            return pairs(t)
        end,

        __ipairs = function()
            return ipairs(t)
        end
    }

    return setmetatable(proxy, meta)
end

local playerData = makeObservable({
    score = 0,
    lives = 3,
    level = 1
}, function(key, oldValue, newValue)
    print(string.format("Player %s changed: %s -> %s", key, oldValue, newValue))
end)

playerData.score = 100
playerData.lives = 2
playerData.level = 2

💻 Lua 协程和模块 lua

🟡 intermediate ⭐⭐⭐⭐

协程编程、模块系统和实用游戏开发模式

⏱️ 35 min 🏷️ lua, coroutines, modules, game dev, entity system, async
Prerequisites: Lua basics, Table operations, Understanding of game programming concepts
-- Lua Coroutines and Modules Examples

-- 1. Basic Coroutine Examples

-- Simple coroutine
function simpleCoroutine()
    print("Coroutine started")
    for i = 1, 3 do
        print("Coroutine step", i)
        coroutine.yield("Yielded value " .. i)
    end
    print("Coroutine finished")
    return "Final result"
end

-- Create and run coroutine
local co = coroutine.create(simpleCoroutine)

print("Coroutine status:", coroutine.status(co))

-- Resume coroutine multiple times
while true do
    local success, result = coroutine.resume(co)
    print("Resume result:", success, result)
    if not success or coroutine.status(co) == "dead" then
        break
    end
end

print("Final coroutine status:", coroutine.status(co))

-- 2. Coroutine for task scheduling
function taskScheduler()
    local tasks = {}

    -- Add task function
    local function addTask(name, func, delay)
        table.insert(tasks, {
            name = name,
            func = func,
            delay = delay,
            timeRemaining = delay,
            coroutine = coroutine.create(func)
        })
    end

    -- Update function (call this every frame)
    local function update(deltaTime)
        for i, task in ipairs(tasks) do
            task.timeRemaining = task.timeRemaining - deltaTime

            if task.timeRemaining <= 0 and coroutine.status(task.coroutine) ~= "dead" then
                local success, result = coroutine.resume(task.coroutine)
                if not success then
                    print("Task error:", task.name, result)
                    table.remove(tasks, i)
                elseif coroutine.status(task.coroutine) == "dead" then
                    print("Task completed:", task.name)
                    table.remove(tasks, i)
                end
            end
        end
    end

    return {
        addTask = addTask,
        update = update,
        getTaskCount = function() return #tasks end
    }
end

-- Test task scheduler
local scheduler = taskScheduler()

-- Define some tasks
local function countdownTask()
    for i = 5, 1, -1 do
        print("Countdown:", i)
        coroutine.yield()
    end
    print("Blast off!")
end

local function printTask()
    for i = 1, 3 do
        print("Printing line", i)
        coroutine.yield()
    end
end

-- Add tasks with delays
scheduler:addTask("Countdown", countdownTask, 1.0)
scheduler:addTask("Print", printTask, 2.0)

-- Simulate game loop
for frame = 1, 10 do
    print("=== Frame", frame, "===")
    scheduler:update(1.0)  -- 1 second per frame
    if scheduler.getTaskCount() == 0 then
        break
    end
end

-- 3. Coroutine for animation
function animateValue(startValue, endValue, duration, updateFunc)
    local startTime = os.clock()

    while true do
        local currentTime = os.clock()
        local elapsed = currentTime - startTime
        local progress = math.min(elapsed / duration, 1.0)

        -- Easing function (ease in-out)
        local easedProgress = progress < 0.5
            and 2 * progress * progress
            or 1 - math.pow(-2 * progress + 2, 2) / 2

        local currentValue = startValue + (endValue - startValue) * easedProgress
        updateFunc(currentValue)

        if progress >= 1.0 then
            break
        end

        coroutine.yield()
    end
end

-- Create animation coroutine
local animationCo = coroutine.create(function()
    animateValue(0, 100, 3.0, function(value)
        print(string.format("Animation progress: %.1f%%", value))
    end)
    print("Animation complete!")
end)

-- Run animation
while coroutine.status(animationCo) ~= "dead" do
    coroutine.resume(animationCo)
    os.execute("sleep 0.1")  -- Wait 100ms (in real code, use proper timing)
end

-- 4. Generator pattern with coroutines
function rangeGenerator(start, stop, step)
    step = step or 1
    return coroutine.create(function()
        for i = start, stop, step do
            coroutine.yield(i)
        end
    end)
end

function fibonacciGenerator(n)
    return coroutine.create(function()
        local a, b = 0, 1
        for i = 1, n do
            coroutine.yield(a)
            a, b = b, a + b
        end
    end)
end

-- Test generators
print("Range generator:")
local rangeGen = rangeGenerator(1, 5)
while true do
    local success, value = coroutine.resume(rangeGen)
    if not success or coroutine.status(rangeGen) == "dead" then
        break
    end
    print("  Value:", value)
end

print("\nFibonacci generator:")
local fibGen = fibonacciGenerator(10)
while true do
    local success, value = coroutine.resume(fibGen)
    if not success or coroutine.status(fibGen) == "dead" then
        break
    end
    print("  Fibonacci:", value)
end

-- 5. Coroutine-based state machine
function createStateMachine()
    local state = "idle"
    local co = coroutine.create(function()
        while true do
            if state == "idle" then
                print("State: Idle")
                state = coroutine.yield("idle")

            elseif state == "loading" then
                print("State: Loading...")
                for i = 1, 3 do
                    print("  Loading", i, "/ 3")
                    coroutine.yield("loading")
                end
                state = "playing"

            elseif state == "playing" then
                print("State: Playing")
                state = coroutine.yield("playing")

            elseif state == "paused" then
                print("State: Paused")
                state = coroutine.yield("paused")

            else
                print("Unknown state:", state)
                state = "idle"
            end
        end
    end)

    return {
        setState = function(newState) state = newState end,
        update = function()
            if coroutine.status(co) ~= "dead" then
                local success, currentState = coroutine.resume(co)
                return success and currentState or "error"
            end
            return "dead"
        end
    }
end

-- Test state machine
local stateMachine = createStateMachine()

for i = 1, 10 do
    print("--- Update", i, "---")
    local currentState = stateMachine.update()

    -- Simulate state transitions
    if i == 2 then
        stateMachine.setState("loading")
    elseif i == 5 then
        stateMachine.setState("playing")
    elseif i == 7 then
        stateMachine.setState("paused")
    elseif i == 8 then
        stateMachine.setState("playing")
    end
end

-- 6. Module system examples

-- In Lua, modules are typically created as tables
-- Here's how to structure a module:

-- mathUtils.lua (simulated here)
local mathUtils = {}

function mathUtils.clamp(value, min, max)
    if value < min then return min end
    if value > max then return max end
    return value
end

function mathUtils.lerp(a, b, t)
    return a + (b - a) * t
end

function mathUtils.randomFloat(min, max)
    return min + math.random() * (max - min)
end

function mathUtils.distance(x1, y1, x2, y2)
    return math.sqrt((x2 - x1)^2 + (y2 - y1)^2)
end

-- Add metatable for module
setmetatable(mathUtils, {
    __tostring = function()
        return "MathUtils module"
    end
})

-- Test the math utilities module
print("\n=== Math Utils Module ===")
print("Clamp 15 to [0, 10]:", mathUtils.clamp(15, 0, 10))
print("Lerp 0 to 10 by 0.3:", mathUtils.lerp(0, 10, 0.3))
print("Random float [1, 5]:", string.format("%.2f", mathUtils.randomFloat(1, 5)))
print("Distance between (0,0) and (3,4):", mathUtils.distance(0, 0, 3, 4))

-- 7. Game entity system with modules

-- Entity component system
local Entity = {}
Entity.__index = Entity

function Entity.new(id)
    local self = setmetatable({
        id = id,
        components = {},
        tags = {}
    }, Entity)
    return self
end

function Entity:addComponent(name, component)
    self.components[name] = component
    return self
end

function Entity:getComponent(name)
    return self.components[name]
end

function Entity:hasComponent(name)
    return self.components[name] ~= nil
end

function Entity:addTag(tag)
    self.tags[tag] = true
    return self
end

function Entity:hasTag(tag)
    return self.tags[tag] == true
end

function Entity:update(dt)
    -- Update all components that have update method
    for name, component in pairs(self.components) do
        if component.update then
            component:update(self, dt)
        end
    end
end

-- Component modules
local Transform = {}
Transform.__index = Transform

function Transform.new(x, y)
    return setmetatable({
        x = x or 0,
        y = y or 0,
        rotation = 0,
        scaleX = 1,
        scaleY = 1
    }, Transform)
end

function Transform:update(entity, dt)
    -- Transform component logic
end

local Health = {}
Health.__index = Health

function Health.new(maxHealth)
    return setmetatable({
        current = maxHealth,
        maximum = maxHealth
    }, Health)
end

function Health:update(entity, dt)
    -- Regeneration logic could go here
end

function Health:takeDamage(amount)
    self.current = math.max(0, self.current - amount)
    return self.current <= 0
end

function Health:heal(amount)
    self.current = math.min(self.maximum, self.current + amount)
end

local Movement = {}
Movement.__index = Movement

function Movement.new(speed)
    return setmetatable({
        speed = speed or 100,
        direction = {x = 1, y = 0}
    }, Movement)
end

function Movement:update(entity, dt)
    local transform = entity:getComponent("transform")
    if transform then
        transform.x = transform.x + self.direction.x * self.speed * dt
        transform.y = transform.y + self.direction.y * self.speed * dt
    end
end

function Movement:setDirection(x, y)
    local length = math.sqrt(x * x + y * y)
    if length > 0 then
        self.direction.x = x / length
        self.direction.y = y / length
    end
end

-- Test entity system
print("\n=== Entity System ===")

local player = Entity.new("player")
    :addComponent("transform", Transform.new(100, 100))
    :addComponent("health", Health.new(100))
    :addComponent("movement", Movement.new(150))
    :addTag("player")
    :addTag("alive")

print("Player created with ID:", player.id)
print("Player has transform:", player:hasComponent("transform"))
print("Player has health:", player:hasComponent("health"))
print("Player is alive:", player:hasTag("alive"))

-- Update player
for i = 1, 3 do
    print("\n--- Update", i, "---")
    local movement = player:getComponent("movement")
    local transform = player:getComponent("transform")

    movement:setDirection(1, 0)  -- Move right
    player:update(0.1)  -- 100ms update

    print("Player position:", string.format("(%.1f, %.1f)", transform.x, transform.y))
end

-- Take damage
local health = player:getComponent("health")
health:takeDamage(30)
print("Player health after damage:", health.current .. "/" .. health.maximum)

-- 8. Coroutine-based AI behavior
local AIBehavior = {}
AIBehavior.__index = AIBehavior

function AIBehavior.new(entity)
    local self = setmetatable({
        entity = entity,
        state = "patrol",
        target = nil,
        coroutine = nil
    }, AIBehavior)

    -- Start AI coroutine
    self.coroutine = coroutine.create(function()
        self:runAI()
    end)

    return self
end

function AIBehavior:runAI()
    while true do
        if self.state == "patrol" then
            print("AI: Patrolling...")
            for i = 1, 3 do
                self:moveTo(math.random(-100, 100), math.random(-100, 100))
                coroutine.yield()
            end
            self.state = "search"

        elseif self.state == "search" then
            print("AI: Searching for targets...")
            self.target = {x = 50, y = 50}  -- Simulate finding a target
            self.state = "chase"
            coroutine.yield()

        elseif self.state == "chase" then
            if self.target then
                print("AI: Chasing target at", self.target.x, self.target.y)
                self:moveTo(self.target.x, self.target.y)

                local transform = self.entity:getComponent("transform")
                local dist = mathUtils.distance(transform.x, transform.y,
                                              self.target.x, self.target.y)
                if dist < 5 then
                    print("AI: Reached target!")
                    self.target = nil
                    self.state = "patrol"
                end
            else
                self.state = "patrol"
            end
            coroutine.yield()
        end
    end
end

function AIBehavior:moveTo(x, y)
    local transform = self.entity:getComponent("transform")
    if transform then
        print("AI moving to:", x, y)
        transform.x = x
        transform.y = y
    end
end

function AIBehavior:update(dt)
    if self.coroutine and coroutine.status(self.coroutine) ~= "dead" then
        local success, result = coroutine.resume(self.coroutine)
        if not success then
            print("AI Error:", result)
        end
    end
end

-- Test AI behavior
print("\n=== AI Behavior ===")
local enemy = Entity.new("enemy")
    :addComponent("transform", Transform.new(0, 0))
    :addComponent("movement", Movement.new(100))

local ai = AIBehavior.new(enemy)

-- Run AI for several updates
for i = 1, 10 do
    print("\nAI Update", i)
    ai:update(0.1)
end

print("\nAll examples completed!")