下载 App
《我真没让他们建城啊!》开发者日志
今天中午摸鱼偷偷改的,其他的要下班改了。一个人,时间有限,抱歉
“道基界面那里滑不动。” “一大堆 UI 通知往脸上跳,我是玩游戏还是看通知?” “拆不了通天塔,天罚没用啊。”
三个反馈,三个完全不同的模块。修完发现它们其实是同一个病: 代码都在,看起来都在跑,但玩家的动作到不了系统里。
滚动代码写着,触摸收不到; 通知代码写着,没人管它们叠不叠; 天罚代码写着,但它忘了通知叛神系统。
三处"接上了",三处其实都没通电。
日期: 2026-10-19 当前进度: 通知系统重构为右下角滚动队列;修复道基面板无法滚动;修复通天塔不可被天罚拆除;通天塔新增可反复轰击的耐久条
一、道基面板"滑不动" —— 滚动写好了,触摸到不了 玩家第一句话:“道基界面那里滑不动。”
1.1 第一层:这个面板压根没有滚动 DrawPrestige 的 6 行永久升级项是写死排布的,没有任何滚动实现:
lua
复制 local rowH = Config.Clamp((listBottom - listTop) / #Prestige.DEFS, 40, 62) local ry = listTop for _, def in ipairs(Prestige.DEFS) do ... -- 一行行往下画,画到哪算哪 ry = ry + rowH end 行高下限被 Clamp 顶到 40,6 行就是 240。而手机横屏的逻辑高度只有 ~366,扣掉标题和页脚,可用高度只剩 120 左右——内容直接溢出面板,而且没有任何手段能把它拖出来。
这层好修:套上项目里已有的 Scroll 组件(图鉴/编年史/结算页都在用)。
1.2 第二层:Scroll 的拖动,在手机上从来没生效过 修完滚动,写探针一测,发现不对劲:拖 80px 只滚 10px。
继续查,看 Scroll.Begin 的拖动判定:
if inView and input and input:GetMouseButtonDown(MOUSEB_LEFT) then 它只认鼠标左键。
而 main.lua 里:
lua
复制 function HandleMouseDown(eventType, eventData) if Camera.touchActive then return end -- ← 触摸设备上,鼠标事件在这里被丢掉 ... end 触摸设备上,引擎会把触摸仿真成鼠标事件,但项目为了"避免同一次点按被消费两遍",在 touchActive 时把这些鼠标事件直接 return 掉了。
于是 Scroll 的拖动判定永远拿不到"按下"——手机上任何面板都拖不动。
这不是道基面板的问题,是 Scroll 的问题,只是玩家在道基面板上先撞见了它。
修法:按下判定同时接受两条路径。
local pressing = (input and input:GetMouseButtonDown(MOUSEB_LEFT)) or Camera.IsSingleTouch() Camera.IsSingleTouch() 是新增的,直接读触摸状态——触摸设备上鼠标事件既然被拦了,就绕开它。
1.3 顺手修的手感问题 拖 80px 只滚 10px,除了上面那个"根本收不到按下",还有个 inView 判定:
lua
复制 local inView = mx >= viewX and mx <= viewX + viewW and my >= viewY and my <= viewY + viewH if inView and pressing then ... 手指一移出可视区边界,滚动就中断。移动端列表的标准手感是"一旦开始拖,就持续跟随":
if pressing and (inView or s.dragY) then -- 已经在拖了,移出边界也继续 修完再测:拖 80px 滚 70px。
二、通知往脸上跳 —— 四条通道,各画各的 玩家第二句话很直白:
一大堆 UI 通知往脸上跳,我是玩游戏还是看通知?不能把进度或者通知全塞玩家脸上吧。
先数了一下当时有几条通道在往屏幕上放东西:
通道 位置 内容 图鉴大卡 DrawToast 中央 y=84 31 种文明现象 事件横幅 DrawEventBanner 中央 y=30 天气事件 / 战争 / 崩溃 / 纪元 领悟横幅 feedbackBanners 中央 y=104 知识领悟 / 复合知识 自建提示 feedbackToasts 右下角 聚落自建 四条通道,互不仲裁。
中央那三条可以同帧叠加:图鉴大卡在 84、事件横幅在 30、领悟横幅在 104,同时出现就是三个面板摞在屏幕正中央。
更糟的是图鉴 31 种现象全部走中央大卡。开局前几分钟现象密集触发,能连续弹 90 秒中央弹窗。
2.1 第一次尝试:仲裁(不够) 我一开始按 UX 文档 §75 做了个"中央仲裁":同一时刻只允许一个中央通道出声,优先级 纪元 > 事件 > 领悟 > 图鉴。修完确实不叠了。
但玩家紧接着给了更明确的要求:
我的建议是这些弹窗提示类的界面都在屏幕右边(条形装,多行),以队列的形式像一条一条滚动信息,逐渐消失。 是屏幕的右下角,不是安全区域,直接右下加。
好。那就不是"仲裁",是合并成一条队列。
2.2 真正做对:单一右下角队列 四条通道全部拆掉,换成一条队列:
所有通知按时间入队,从下往上堆叠,新的在下方 旧的向上滚、渐隐、到期消失 —— 形成一条持续流动的消息流 同时最多 4 条,超出丢弃最旧的(不排队——排队会把通知拖成永不断的流水线,那正是玩家抱怨的东西) NOTICE_STAGGER = 0.8s 逐条入场节流:一次发现多个现象时一条条往下冒,而不是"一批批闪烁" 配色按类型区分(左侧色条 + 描边):金色=图鉴/纪元、蓝色=领悟、绿色=自建、红色=事件/战争/崩溃。
2.3 玩家又追了两轮尺寸 第一轮:
太大了这个滚动还要缩小。
330×46 → 240×38。
第二轮直接给了数字:
宽度160,高度32。字体再小一点,主文案10,副文案8。图标10。
于是 240×38 → 160×32,字号主 10 / 副 8 / 图标 10。
还有个细节:玩家特意强调"是屏幕的右下角,不是安全区域,直接右下加"。所以锚点从 SafeR()/SafeB()(安全区边缘)改成 Camera.sw/Camera.sh(屏幕物理边缘)。但底部 hotbar 是居中的,宽屏时右边缘会逼近屏幕右侧——所以宽度取「hotbar 右缘 + 12 后的余量」与 160 的较小值,既贴右下角又不遮 hotbar。
2.4 最后是"两个铁器时代" 玩家发来截图:队列里并排两条"铁器时代"。
查下来是两个来源各报了一次:
Systems.eventBanner:纪元晋级,text = "铁器时代" 图鉴发现 era_iron:name = "铁器时代",带引言 图标都是 🗡、文案都是"铁器时代",队列没去重就并排显示。
加了一层通用去重:键 = 图标|文案。已在队列里、或 6 秒内刚播过的,直接跳过;若新来的带副文案(信息更全),就替换旧的——所以最终留下的是带引言的那条。
这条是通用机制,不只为纪元:任何两个来源撞车都会自动合并。
三、天罚没用 —— 一个 return,断了一整条因果链 ★ 这是今天最重要的一条。
玩家第三句话:
拆不了通天塔,天罚没用啊,再怎么天罚,玩家还是会结束。
先写探针实测,别猜。 同一 seed、900 秒:
策略 塔峰值 结局 不天罚 46 time 每 5 秒天罚 100 extinction 每 2 秒天罚 100 extinction 每 3 秒投食(安抚) 14 time(人口 117) 天罚不但拆不了塔,还让塔建得更快。
3.1 第一个 bug:塔的回落条件 Apostasy.lua 里塔进度只在 awakened == false 时回落:
lua
复制 if st.awakened then tw.progress = tw.progress + AC.TowerProgressRate * dt -- 只涨 else tw.progress = tw.progress - AC.TowerSuppressedDecay * dt -- 才回落 end 而天罚在 OnDivineAct 里只加怨气/创伤、只可能触发觉醒,永远不会让已觉醒的聚落回头。所以天罚期间塔只涨不落。
我加了"天罚直接轰塔"。
3.2 但实测还是没用 加完再测,塔进度 3→3、6→6,一点没掉。
继续查,找到真正的根因。GodHand.lua:
lua
复制 local smiteFn = SMITE_FN[toolId] if smiteFn then if not smiteFn(wx, wy) then return false, "此处没有可生效的目标,未消耗神力" end W.influence = W.influence - cost W.stats.toolsUsed = W.stats.toolsUsed + 1 W.stats.smites = (W.stats.smites or 0) + 1 local sfx = PLACE_SFX[toolId] if sfx then Audio.Play(sfx, wx, wy) end return true -- ← 这里就返回了 end 天罚分支提前 return 了。而文件末尾那句"让叛神系统看见神做了什么"的通知:
lua
复制 -- 叛神系统:让聚落"看见"神做了什么。恩赐安抚,天罚激怒。 local okA, Apostasy = pcall(require, "Apostasy") if okA and Apostasy and Apostasy.OnDivineAct then Apostasy.OnDivineAct(toolId, wx, wy) end 在 return true 之后。 天罚根本走不到这里。
所以真相是:天罚对叛神系统完全不可见。 不加怨气、不轰塔、不点醒。
玩家感知的"天罚没用",是字面意义上的——天罚确实什么都没做。
3.3 为什么这条比第一条严重 第一条(塔的回落条件)是逻辑设计问题:代码跑到了,只是条件不对。
第二条是接线断了:一段代码写在那里,看起来很正常,但永远执行不到。
而且它是今天唯一一条完全静默的失败——不报错、不崩溃、不警告。前面那些 bug 好歹有个现象(拖不动、面板叠一起),这个什么都没有,因为"什么都没发生"本身就是它的症状。
修法很简单,smite 分支补上通知调用。修完再测:天罚塔峰值 100 → 3,进度被反复削回 0(3→0、1→0)。
顺带把态势面板的误导文案也改了。它一直写着"拆掉他们的图腾可打断"——但拆图腾只摧毁玩家自己的造物,对塔毫无影响。这句话从头到尾都在骗玩家。
四、塔的耐久条 修完"天罚能拆塔",我问了一句:要不要让天罚更"外科手术"一点,少误伤平民?
玩家选了另一条路:
塔本身有个可被反复轰击的耐久条
好。那就是把"塔"从一个进度条变成一个可被反复攻击的耐久体。
塔耐久 = 建塔进度(满 100 = 建成 = 失败) 每记天罚削一截:雷 22 / 火雨 26 / 地震 45 → 约 2~5 记轰塌 塔顶画耐久条(绿 → 金 → 红),命中时整条 + 塔身闪白 轰塌后 50 秒重建冷却 闪白用的是时间戳:
lua
复制 tw.hitUntil = W.time + 0.35 -- 模拟层写 -- 渲染层只读:if tw.hitUntil and W.animT < tw.hitUntil then 闪白 end 这是项目一贯的做法——表现层不反过来改世界状态,否则会污染模拟的确定性(这个坑 03 就记过)。
重建冷却这条值得说一下。加它之前,塔刚塌就立刻重新动工,玩家拆了个寂寞。加 50 秒冷却之后,"拆掉塔"才是一次真正的胜利——玩家能喘口气去处理别的事。
写在最后 今天三个反馈,来自三个完全不同的模块:
玩家原话 我以为的 实际是
1 道基界面滑不动 面板没做滚动 Scroll 的拖动只认鼠标,触摸永远收不到 2 通知往脸上跳 加个仲裁就行 四条通道各画各的,从没合并过 3 天罚没用 数值不够狠 一个提前 return,天罚对叛神系统完全不可见 三条的共同点:代码都在,看起来都在跑。
滚动代码写着 → 触摸收不到 通知代码写着 → 没人管它们叠不叠 天罚代码写着 → 它忘了通知叛神系统 日志 09 的教训是"做了 ≠ 玩家碰得到",10 补了"碰得到 ≠ 看得见 ≠ 看得对"。
今天要再补一句:
看得对 ≠ 真的连上了。
第 3 条最典型:天罚看得对——雷击特效有、音效有、神力扣了、人口死了。玩家看到的一切都正常。但它和叛神系统之间那根线,从来没接上过。
所以给自己加了两条检查项:
① 凡是"多个系统共享一个玩家动作"的地方,都要验证通知链完整。 天罚同时影响:地形/人口(GodHand 自己做)、叛神怨气(OnDivineAct)、通天塔(同前)。 前两个分支各写各的,第三个漏了。
② “末尾统一通知” = 容易被提前 return 跳过。 pcall(require, ...) 那种写在函数最末尾的兜底通知,任何提前 return 都会静默跳过它。 要么在入口处统一通知一次,要么每个分支各自通知。





