在使用agent辅助love2d游戏制作时,可以用一些skill和mcp来让ai更符合项目规范和减少出错
这里我用的agent是Zcode
首先先下载mcp
我们需要先克隆这个mcp到一个文件夹
已复制
git clone https://github.com/shayarnett/love2d-mcp.git
cd love2d-mcp
然后安装依赖
已复制
npm install

此时启动构建服务器
已复制
npm run build
找到mcp设置项添加mcp


显示绿点即接入成功

不过我们需要在代码层引入这个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的默认气球界面

游戏会在端口 12345 上启动一个 TCP 服务器
连接MCP Inspector
这个我不知道有什么用
已复制
npx @modelcontextprotocol/inspector node build/index.js

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

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

直接复制给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.
然后就会在项目的根目录生成

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

不过光靠ai的效果是一言难尽的,还需要我们大量的纠正和调试
bug
不过这个还是存在一些bug的,例如ai会反复尝试获取报错日志,但是因为未知原因有的时候时候始终获取不到,这个问题还是等作者更新吧


评论区