在使用agent辅助love2d游戏制作时,可以用一些skill和mcp来让ai更符合项目规范和减少出错

这里我用的agent是Zcode

首先先下载mcp

点击前往下载

我们需要先克隆这个mcp到一个文件夹

已复制
git clone https://github.com/shayarnett/love2d-mcp.git
cd love2d-mcp

然后安装依赖

已复制
npm install

​ image-20260913231324641

此时启动构建服务器

已复制
npm run build

找到mcp设置项添加mcp

​ image-20260913231443258

​ image-20260913231538003

显示绿点即接入成功

​ image-20260913231610824

不过我们需要在代码层引入这个MCP

首先制作一个mcp_bridge.lua文件

LUA已复制
-- MCP Bridge - TCP server for communicating with MCP server
local socket = require("socket")

local mcp_bridge = {}
local server = nil
local clients = {}
local objectGetter = nil

-- Initialize TCP server
function mcp_bridge.init(port)
    server = assert(socket.tcp())
    server:bind("*", port)
    server:listen(5)
    server:settimeout(0) -- Non-blocking
    print("MCP Bridge listening on port " .. port)
end

-- Set function to get game objects
function mcp_bridge.setObjectGetter(getter)
    objectGetter = getter
end

-- Handle incoming connections and commands
function mcp_bridge.update()
    if not server then return end

    -- Accept new clients
    local client = server:accept()
    if client then
        client:settimeout(0)
        table.insert(clients, client)
        print("MCP client connected")
    end

    -- Handle existing clients
    for i = #clients, 1, -1 do
        local client = clients[i]
        local line, err = client:receive("*l")

        if line then
            -- Process command
            local success, response = pcall(mcp_bridge.handleCommand, line)
            if success then
                client:send(response .. "\n")
            else
                client:send(json.encode({error = tostring(response)}) .. "\n")
            end
        elseif err == "closed" then
            -- Client disconnected
            client:close()
            table.remove(clients, i)
            print("MCP client disconnected")
        end
        -- err == "timeout" means no data available, continue
    end
end

-- Handle a command from MCP server
function mcp_bridge.handleCommand(line)
    local command = json.decode(line)

    if command.command == "list_objects" then
        return mcp_bridge.listObjects()
    elseif command.command == "get_object" then
        return mcp_bridge.getObject(command.id)
    elseif command.command == "run_lua" then
        return mcp_bridge.runLua(command.code)
    else
        return json.encode({error = "Unknown command: " .. tostring(command.command)})
    end
end

-- List all objects
function mcp_bridge.listObjects()
    if not objectGetter then
        return json.encode({error = "No object getter configured"})
    end

    local objects = objectGetter()
    local result = {}

    for id, obj in pairs(objects) do
        table.insert(result, {
            id = id,
            type = obj.type,
            x = obj.x,
            y = obj.y
        })
    end

    return json.encode({objects = result})
end

-- Get specific object details
function mcp_bridge.getObject(id)
    if not objectGetter then
        return json.encode({error = "No object getter configured"})
    end

    local objects = objectGetter()
    local obj = objects[id]

    if not obj then
        return json.encode({error = "Object not found: " .. tostring(id)})
    end

    return json.encode({object = obj})
end

-- Run arbitrary Lua code with access to game objects
function mcp_bridge.runLua(code)
    local func, err = loadstring(code)
    if not func then
        return json.encode({error = "Syntax error: " .. tostring(err)})
    end

    -- Set up environment with access to objects
    local env = {
        objects = objectGetter and objectGetter() or {},
        love = love,
        print = print,
        pairs = pairs,
        ipairs = ipairs,
        type = type,
        tostring = tostring,
        tonumber = tonumber,
        table = table,
        math = math,
        string = string,
    }
    setfenv(func, env)

    local success, result = pcall(func)
    if not success then
        return json.encode({error = "Runtime error: " .. tostring(result)})
    end

    -- Handle table results by encoding them
    if type(result) == "table" then
        return json.encode({result = result})
    else
        return json.encode({result = tostring(result)})
    end
end

-- Shutdown server
function mcp_bridge.shutdown()
    -- Close all clients
    for _, client in ipairs(clients) do
        client:close()
    end
    clients = {}

    -- Close server
    if server then
        server:close()
        server = nil
        print("MCP Bridge shut down")
    end
end

-- Simple JSON encoder/decoder
json = {}

function json.encode(obj)
    local t = type(obj)
    if t == "table" then
        local parts = {}
        local isArray = true
        local arraySize = 0

        -- Check if it's an array
        for k, v in pairs(obj) do
            if type(k) ~= "number" then
                isArray = false
                break
            end
            arraySize = arraySize + 1
        end

        if isArray and arraySize > 0 then
            for i, v in ipairs(obj) do
                table.insert(parts, json.encode(v))
            end
            return "[" .. table.concat(parts, ",") .. "]"
        else
            for k, v in pairs(obj) do
                local key = type(k) == "string" and json.encode(k) or tostring(k)
                table.insert(parts, key .. ":" .. json.encode(v))
            end
            return "{" .. table.concat(parts, ",") .. "}"
        end
    elseif t == "string" then
        return '"' .. obj:gsub('\\', '\\\\'):gsub('"', '\\"'):gsub('\n', '\\n') .. '"'
    elseif t == "number" or t == "boolean" then
        return tostring(obj)
    elseif t == "nil" then
        return "null"
    else
        return '"' .. tostring(obj) .. '"'
    end
end

function json.decode(str)
    local pos = 1

    local function skip_whitespace()
        while pos <= #str and str:sub(pos, pos):match("%s") do
            pos = pos + 1
        end
    end

    local function decode_string()
        local result = ""
        pos = pos + 1 -- skip opening quote
        while pos <= #str do
            local char = str:sub(pos, pos)
            if char == '"' then
                pos = pos + 1
                return result
            elseif char == "\\" then
                pos = pos + 1
                local escape = str:sub(pos, pos)
                if escape == "n" then result = result .. "\n"
                elseif escape == "t" then result = result .. "\t"
                elseif escape == "r" then result = result .. "\r"
                elseif escape == "\\" then result = result .. "\\"
                elseif escape == '"' then result = result .. '"'
                else result = result .. escape end
                pos = pos + 1
            else
                result = result .. char
                pos = pos + 1
            end
        end
        error("Unterminated string")
    end

    local function decode_value()
        skip_whitespace()
        local char = str:sub(pos, pos)

        if char == '"' then
            return decode_string()
        elseif char == "{" then
            local obj = {}
            pos = pos + 1
            skip_whitespace()
            if str:sub(pos, pos) == "}" then
                pos = pos + 1
                return obj
            end
            while true do
                skip_whitespace()
                local key = decode_string()
                skip_whitespace()
                if str:sub(pos, pos) ~= ":" then error("Expected :") end
                pos = pos + 1
                obj[key] = decode_value()
                skip_whitespace()
                char = str:sub(pos, pos)
                if char == "}" then
                    pos = pos + 1
                    return obj
                elseif char == "," then
                    pos = pos + 1
                else
                    error("Expected , or }")
                end
            end
        elseif char == "[" then
            local arr = {}
            pos = pos + 1
            skip_whitespace()
            if str:sub(pos, pos) == "]" then
                pos = pos + 1
                return arr
            end
            while true do
                table.insert(arr, decode_value())
                skip_whitespace()
                char = str:sub(pos, pos)
                if char == "]" then
                    pos = pos + 1
                    return arr
                elseif char == "," then
                    pos = pos + 1
                else
                    error("Expected , or ]")
                end
            end
        elseif str:sub(pos, pos + 3) == "true" then
            pos = pos + 4
            return true
        elseif str:sub(pos, pos + 4) == "false" then
            pos = pos + 5
            return false
        elseif str:sub(pos, pos + 3) == "null" then
            pos = pos + 4
            return nil
        else
            local num_str = str:match("^%-?%d+%.?%d*[eE]?[%+%-]?%d*", pos)
            if num_str then
                pos = pos + #num_str
                return tonumber(num_str)
            end
            error("Invalid JSON value at position " .. pos)
        end
    end

    return decode_value()
end

return mcp_bridge

并在main.lua中引入这个文件

LUA已复制
local mcp_bridge = require("mcp_bridge")

function love.load()
    -- Initialize MCP bridge on port 12345
    mcp_bridge.init(12345)

    -- Provide access to your game objects
    mcp_bridge.setObjectGetter(function()
        return your_game_objects_table
    end)
end

function love.update(dt)
    -- Call this every frame to handle MCP commands
    mcp_bridge.update()

    -- Your game logic here
end

function love.quit()
    mcp_bridge.shutdown()
end

启动游戏

BASH已复制
love game/   #在某目录启动游戏
或者
love .   #以当前目录启动游戏

此时就会弹出love2d的默认气球界面

​ image-20260914145919690

游戏会在端口 12345 上启动一个 TCP 服务器

连接MCP Inspector

这个我不知道有什么用

已复制
npx @modelcontextprotocol/inspector node build/index.js

​ image-20260914145126548

就是我们在启动后会进入到浏览器的一个面板,可以自行研究

​ image-20260914145213823

添加skill

我们使用这个认可度比较高的这个skill

点击前往网站

​ image-20260913232143473

直接复制给ai让它帮你装

已复制
Read https://lobehub.com/skills/chongdashu-love2d-pocket-bomber-game-love2d-gamedev/skill.md to understand how to install and use this skill. Install it as documented, then open the installed SKILL.md and follow its instructions. Ask for confirmation before any credential use, external service setup, or irreversible action.

然后就会在项目的根目录生成

​ image-20260914144318248

然后我们启动一个love2d项目,它自己就可以按照你的提示词调用工具打开love2d

​ image-20260913231850658

不过光靠ai的效果是一言难尽的,还需要我们大量的纠正和调试

bug

不过这个还是存在一些bug的,例如ai会反复尝试获取报错日志,但是因为未知原因有的时候时候始终获取不到,这个问题还是等作者更新吧