下载 App

MapCalibrator 开发经验总结,直接作为 prompt 交给 AI 复刻

精华03/19112 浏览开发交流
NanoVG 地图编辑器/调校面板 — 架构模式速查一、核心架构:三文件分离MapData.lua        — 纯数据 + 坐标工具 + 持久化(不含任何绘制代码)
MapRenderer.lua    — 只负责渲染,从 MapData 读取数据驱动绘制
MapCalibrator.lua  — 编辑器 UI 叠加层,修改 MapData 中的数据
关键原则:渲染器不持有数据,编辑器不直接绘制游戏元素。三者通过 MapData 这个"单一数据源"解耦。二、数据表设计模式lua
复制-- 所有可编辑元素统一使用 zone-local 归一化坐标(0~1)
MapData.BUILDINGS = {
    { id = "唯一ID",        -- 持久化用,不可变
      name = "显示名",      -- UI 列表显示
      zone = "zoneName",    -- 所属区域名
      nx = 0.50, ny = 0.30, -- zone 内归一化坐标
      drawScale = 1.0,      -- 绘制缩放
      buildingType = "xxx", -- 类型分派键
      -- ...其他业务字段
    },
}
-- 第二类元素(景观/装饰)完全同构
MapData.LANDSCAPES = {
    { id = "唯一ID", name = "显示名", zone = "zoneName",
      nx = 0.30, ny = 0.65, drawScale = 1.0,
      landscapeType = "bamboo",        -- 类型分派键
      extra = { count = 9 },           -- 类型特有参数放 extra 表
    },
}
要点:
  • nx/ny 是相对 zone 的 0~1 归一化值,不是绝对像素,方便 zone 整体移动/缩放
  • extra 表存放类型特有参数(竹子数量、花色、桥旋转角等),避免主表字段爆炸
  • id 用中文可读字符串,兼顾调试和持久化
复制-- 1. 归一化 → 绝对像素
function MapData.GetXxxPos(item)
    local z = MapData.ZONES[item.zone]
    return z.x + item.nx * z.w, z.y + item.ny * z.h
end
-- 2. 屏幕点 → 地图坐标
function MapData.ScreenToMap(sx, sy, cam, scrW, scrH)
    return (sx - scrW * 0.5) / cam.zoom + cam.x,
           (sy - scrH * 0.5) / cam.zoom + cam.y
end
-- 3. 命中检测(屏幕坐标输入)
function MapData.HitTestXxx(sx, sy, cam, scrW, scrH)
    local mx, my = MapData.ScreenToMap(sx, sy, cam, scrW, scrH)
    local hitR = 18 / cam.zoom  -- 命中半径随缩放自适应
    for i, item in ipairs(MapData.XXX) do
        local ix, iy = MapData.GetXxxPos(item)
        local dx, dy = mx - ix, my - iy
        if dx*dx + dy*dy <= hitR*hitR then return i end
    end
    return nil
end
-- 4. 实时更新单项
function MapData.UpdateXxx(index, fields)
    local item = MapData.XXX[index]
    if fields.nx then item.nx = fields.nx end
    if fields.ny then item.ny = fields.ny end
    if fields.drawScale then item.drawScale = fields.drawScale end
end
四、JSON 覆盖持久化模式lua
复制-- 设计思路:代码内保留默认值,JSON 只存用户修改的覆盖层
-- 好处:代码更新默认值不会被旧存档覆盖,新增元素自动使用默认值
function MapData.SaveLayout()
    -- 序列化所有元素的 id + nx + ny + drawScale
    local data = { version = 2, buildings = {...}, landscapes = {...} }
    local file = File("map_layout.json", FILE_WRITE)
    file:WriteString(cjson.encode(data))
    file:Close()
end
function MapData.LoadLayout()
    -- 反序列化后通过 id 索引表快速查找并覆盖
    -- 用 _buildingIdIndex[id] → 数组下标 的惰性索引加速
end
五、编辑器 UI 架构(MapCalibrator)5.1 状态设计lua
复制MapCalibrator.active = false          -- 模式开关
MapCalibrator.selectedType = nil      -- "building" | "landscape" | nil
MapCalibrator.selectedIdx = nil       -- 选中项在对应数组中的索引
MapCalibrator.dragging = false        -- 拖拽中
MapCalibrator.dragOffX/Y = 0          -- 拖拽偏移(防止元素跳到鼠标位置)
MapCalibrator.listScrollY = 0         -- 列表滚动
MapCalibrator.collapsedZones = {}     -- zone折叠状态 { [zoneName] = bool }
5.2 事件消费模式lua
复制-- 在 main.lua 的输入事件中:
if MapCalibrator.active then
    if MapCalibrator.HandleMouseDown(lx, ly, cam, w, h) then
        return  -- 消费了,不传给相机拖拽
    end
end
-- 未消费 → 正常相机拖拽逻辑
-- HandleMouseDown 内部返回规则:
--   UI 区域(工具栏/列表/面板)→ return true(总是消费)
--   地图区域命中元素 → return true(开始拖拽)
--   地图区域未命中 → return false(允许相机拖拽穿透)
5.3 拖拽实现(带偏移)lua
复制-- MouseDown 时记录偏移
local itemX, itemY = GetPos(item)
local mouseMapX, mouseMapY = ScreenToMap(lx, ly, cam, w, h)
dragOffX = itemX - mouseMapX   -- 关键:不是 0,而是差值
dragOffY = itemY - mouseMapY
-- MouseMove 时应用偏移
local mx, my = ScreenToMap(lx, ly, cam, w, h)
mx = mx + dragOffX  -- 加回偏移,元素不会跳
my = my + dragOffY
-- 反算回 zone-local 归一化坐标
local newNx = math.max(0.02, math.min(0.98, (mx - z.x) / z.w))
local newNy = math.max(0.02, math.min(0.98, (my - z.y) / z.h))
5.4 渲染分层lua
复制-- 在 MapRenderer.render() 中:
nvgSave(vg)
-- ...相机变换...
    -- 游戏内容渲染...
    MapCalibrator.RenderHighlight(vg, cam)  -- ① 相机空间内:虚线选框
nvgRestore(vg)
MapCalibrator.Render(vg, w, h, cam, fontSans) -- ② 屏幕空间:工具栏/列表/面板
5.5 折叠分组列表lua
复制-- 渲染逻辑:
for _, zoneName in ipairs(ZONE_ORDER) do
    -- 绘制 zone 标题(带 +/- 指示符 + 元素计数)
    -- 点击标题 → collapsedZones[zoneName] = not collapsedZones[zoneName]
    if not collapsed then
        -- 先渲染该 zone 的建筑(圆点标记 + 家族色)
        -- 再渲染该 zone 的景观(方块标记 + 绿色系)
    end
end
-- 命中检测逻辑必须与渲染逻辑完全同构(同样的遍历顺序和折叠跳过)
5.6 多类型选中的统一处理lua
复制-- 用 selectedType + selectedIdx 二元组标识选中项
-- 通过 getSelectedItem() 统一获取数据,避免到处 if/else
local function getSelectedItem()
    if selectedType == "building" then
        return MapData.BUILDINGS[selectedIdx], "building"
    elseif selectedType == "landscape" then
        return MapData.LANDSCAPES[selectedIdx], "landscape"
    end
end
-- 滚轮/面板/高亮等操作都通过这个统一接口
-- 只在最终调用 UpdateBuilding/UpdateLandscape 时分派
六、数据驱动渲染的分派表模式lua
复制-- 用类型字符串 → 绘制函数的映射表,替代 if/elseif 链
local LANDSCAPE_DRAW = {
    bamboo = function(vg, x, y, s, extra)
        drawBambooCluster(vg, x, y, s, extra and extra.count or 7)
    end,
    pineTree = function(vg, x, y, s) drawPineTree(vg, x, y, s) end,
    flowerTree = function(vg, x, y, s, extra)
        drawFlowerTree(vg, x, y, s, extra and extra.color)
    end,
    -- ...
}
-- 渲染时一行搞定
function drawLandscapeSprites(vg, zoneName)
    for _, ls in ipairs(MapData.LANDSCAPES) do
        if ls.zone == zoneName then
            local x, y = MapData.GetLandscapePos(ls)
            LANDSCAPE_DRAW[ls.landscapeType](vg, x, y, ls.drawScale, ls.extra)
        end
    end
end
七、给 AI 的一句话 Prompt 模板请为我的 NanoVG 2D 地图项目添加一个嵌入式调校面板(F1 切换),支持:
  1. 左侧按区域分组的元素列表,点区域名可折叠/展开
  2. 支持多种元素类型(建筑+景观),列表中用不同图标/颜色区分
  3. 在地图上拖拽调整元素位置(带偏移防跳),滚轮调缩放
  4. 右侧属性面板显示选中项的坐标和缩放值,带 +/- 按钮微调
  5. 关闭时自动 JSON 持久化,启动时加载覆盖默认值
  6. 采用三文件架构:数据层(MapData) / 渲染层(MapRenderer) / 编辑器层(MapCalibrator)
  7. 所有可编辑元素使用 zone-local 归一化坐标(nx/ny 0~1)
  8. 渲染层中的硬编码绘制调用全部改为数据驱动(分派表模式)
  9. 编辑器 UI 在屏幕空间绘制,选中高亮在相机空间绘制
输入事件使用消费模式:编辑器返回 true 消费,false 穿透给相机
horizontal linehorizontal line
以上就是完整的模式总结,涵盖数据设计、坐标系统、拖拽交互、折叠列表、持久化、渲染分层六大方面。下次新项目把这段贴给 AI 即可直接复刻。
猜你想搜
taptap 制造mapcalibrator开发经验
别再烧积分了!从每次 2000 到每次 50 的血泪经验截图
别再烧积分了!从每次 2000 到每次 50 的血泪经验
适用场景:所有在用 TapTap Code 开发游戏的小白 难度:★☆☆☆☆ 背景最近群里总有人"炫耀"一次操作花了几千积分,说实话这不是值得高兴的事——这是在告诉所有人你的开发方式有根本性问题。我也走过这条弯路,所以来说说怎么改。 积分消耗的本质是什么积分 = AI 处理的 token 数量 = 你发给 AI 的内容 + AI 回给你的内容所以烧积分的本质只有两种: 你让 AI 做了大量无效/重
精华
82 赞
23 回复
如何提升你游戏中的UI美术水平截图
如何提升你游戏中的UI美术水平
一、请为你的游戏挑选一款合适的字体 很多开发者没有注意到其实UI界面中最重要的元素是字体,一款合适的字体能给整个游戏的界面感官带来蜕变。 请看案例 那么如何挑选适合自己的字体呢? 我总结出了一些小经验可供参考: 黑体:通用 宋体:古风、武侠、修仙 楷体:古风、武侠、修仙 圆体:卡通、动漫、Q版 科技:科幻、未来、电子 顺手分享一个免费可商用字体网站: 当然 如果你不知道自己的游戏适合什么风格
精华
126 赞
44 回复
开发心得-界面布局心得截图
开发心得-界面布局心得
大家好,我正在用 TapTap 制造制作一款2D 治愈林间徒步旅行游戏《徒步荒野》。 我分享一下我的开发心得,希望能有用,下面是一些界面布局的心得。 第一步:定版式、操作 先确定游戏是横版还是竖版、需要哪些操作按钮,敲定整体玩法思路。 我的玩法灵感参考《边境之旅》,主打散步看风景、沿途触发随机事件。 原版是 3D,考虑到自己是新手,选择2D 横版街机式来做,制作更简单、好上手。 第二步:敲定全局美
6 赞
3 回复
01:29
官方阅,申请积分计划2,希望官方继续支持我完成这款游戏,截图
官方阅,申请积分计划2,希望官方继续支持我完成这款游戏,
视频内容只是一部分, 本游戏耗时3个月,目前完成进度百分之80, 是生存游戏,进入修仙界,走剧情找到宗门加入, 这时游戏才是真正开始, 提升实力境界后就可以变强,获取功法法术法宝, @视频内是游戏里挑战妖族入侵一种主流玩法,游戏就是围绕着这个来进行, 还有很多地方可以战斗,获得资源修炼变强,比如去藏书阁学习功法, 去法宝楼抽法宝,每一个宗门数十个法宝抽取 去修仙界探索,获得各自的资源,可能遇见妖兽
1 赞
3 回复
永恒虚空持续开发日记1——关于游戏中的新名词:场频截图
永恒虚空持续开发日记1——关于游戏中的新名词:场频
写在前边:首先感谢TapTap制造版主大大@汪崽 对于作品潜力的肯定,给与了我积分计划二的权限[表情_不好意思]。这边也在加紧开发改进各种功能中,期望尽快给大家带来成熟的版本。[表情_猫咪举手] 大家好,我是永恒虚空的制作人黑猫不详,大家叫我黑猫就好。从今天开始我将以后置开发日记的形式给大家分享我在TapTap制造中制作的游戏永恒虚空的开发过程,新人希望大家多多关照! 今天先给大家介绍我作品中最重
投票
3 赞
【UI教程】如何将已有的效果图应用为实际的UI界面截图
【UI教程】如何将已有的效果图应用为实际的UI界面
在经过了一些折腾和比较折磨的尝试后,我总算是摸清楚了嗒啦啦怎么把UI效果图转换为实际游戏中的UI组件,今天就给大家带来这部分的经验分享。 tips:1.本篇教程需要各位掌握最基本的ps使用,具体的操作部分我会大致讲解,但仍需各位自行学习ps的基本操作,部分可以由ai代劳的环节我会注明。 2.本篇教程需要在以我前两篇教程作为前置环节,请在阅读这两篇前置教程后再进行本篇教程内容的学习 在按照前
12 赞
5 回复
关于制作出质量很高的横版战斗角色的方法截图
关于制作出质量很高的横版战斗角色的方法
其实全篇就一句话,角色为啥做起来这么好看,因为我一帧一帧K的。 在做角色之前,我们需要一个角色的设计稿,这个东西很简单,你把人设写好剩下的交给AI就行了,所有图片,序列帧都可以让AI绘制和处理,学会绘图是你要制作高质量游戏的第一课。 这里拿我在做的角色举例,他的参考图是这样: 当你拥有了一个角色以后,就要想办法让她动起来了。 那么如何让她动起来,答案是序列帧。 在此之前你需要知道,最容易出现一个误
42 赞
12 回复
浅记《阿方的旅程》开发历程截图
浅记《阿方的旅程》开发历程
预计阅读时长10分钟,欢迎您先品鉴游戏,再回来看这篇“流水账” 这是我第一次实际接触“用自然语言制作游戏”,也是我第一次接触游戏开发工作,所以一切都是从零开始,跌跌撞撞,懵懵懂懂。[表情_豹哭] 我是通过获得的本次开发资格,6月22日晚,在微信公众号收到了测试资格的消息,最开始公布主题“跳跃”的时候,脑子里最先想到的就是几款非常经典的游戏「马里奥」「Flappy Bird」「Doodle Jump
10 赞
1 回复
本地部署 AI 对接 TapMaker 指南 —— 以 CodeBuddy 为例V1.1截图
本地部署 AI 对接 TapMaker 指南 —— 以 CodeBuddy 为例V1.1
写在前面 大家好,我是橘猫,和你一样也是TapMaker的萌新玩家。在"全能神"天哥的悉心指导下,我顺利完成了本地开发环境的部署。经过这段时间的实战测试,已经成功打通了「本地开发 → 同步至TapMaker」的完整流程。 以下内容全部来自我的个人实操经验,旨在为同样在探索本地部署的朋友提供一个参考。每个人的电脑环境、网络情况、AI工具版本都不尽相同,文中的步骤仅供交流学习。如果你照着操作后遇到问题
37 赞
21 回复
01:21
请都看过来~!!!截图
请都看过来~!!!
我是傻…… 很遗憾错的过了这次活动的报名,但刚好我的游戏也是在这个期间完成的,看了下大佬们的作品,感觉我做的还是太粗浅了。模型粗糙,UI也用的原始版本,很多地方都还需要打磨,可惜...这次赠送积分已经败光了。以后的积分大概只能先维护下游戏修复点bug,希望以后还能申请积分... 既然都这样了,我也想看看和大佬们的作品有多大的差距。 星三国/星三国纪元 (其实名字我都还没决定好) 游戏了一半灵感来自
2 赞
4 回复