跳到正文

容易混淆的问题 ​

这套代码里有好几组名字相近、职责不同的东西:判定盒与攻击盒、图块索引与像素、相机偏移与房间偏移。分不清它们的边界,改一处就会连带坏另一处。判断方法只有一条:看这个值由谁创建、在哪个阶段被读取。

判定:谁能打谁 ​

概念语义生命周期常见误用
hitbox受击方的身体常驻拿它当武器判定
hurtbox攻击方的武器只在攻击状态状态退出后还在算伤害
AABB求交算法每次调用以为盒子自带碰撞逻辑

剑盒是 hurtbox,敌人身体是 hitbox,判定写成 entity:collides(swordHitbox)。方向反过来写也能跑,但语义就乱了:尸体不会还手,武器不该受伤。

Hitbox 类只有四个字段,没有任何方法。它不参与更新,也不进房间列表,状态一退出就随状态对象一起被丢弃。

  • 扫房间实体是攻击方的活,受击方只负责 damage
  • 无敌 1.5 秒挂在玩家身上,与挥剑动画的长度无关
  • 剑盒每帧都能命中,敌人血量大于 1 时会出现多段伤害

坐标:四层偏移 ​

名称归属作用
实体 x、y实体房间内像素坐标,碰撞用它
renderOffsetX/Y房间让 22×11 地图在屏幕居中
adjacentOffsetX/Y房间把下一间房画到屏幕外一屏
cameraX/Y地牢交给 translate 平移画布

cameraX 与 adjacentOffsetX 经常被当成一回事。相机挪的是整张画布,房间偏移只作用于那一间房,切换时两者同时朝反方向走,屏幕里才出现"旧房左移、新房滑入"。按 384×216 算,地图 352×176,水平偏移 16、垂直偏移 20,这段差值不进任何判定,只影响绘制。

lua
local tx = math.floor((px - MAP_RENDER_OFFSET_X) / TILE_SIZE) + 1
local ty = math.floor((py - MAP_RENDER_OFFSET_Y) / TILE_SIZE) + 1

反查时先减偏移、再除格子、最后加一;绘制时先减索引、再乘格子、最后加偏移。两头都差一个 1,顺序反了就整体错一格。

时间:谁在第几秒变 ​

机制改的是什么何时生效
Timer.tween(1, …)字段数值时长走完后回调
stateMachine:change行为对象立刻
coroutine.yield执行位置调用方再次调用时
shifting 标志更新开关finishShifting

撞门那一刻只是把 shifting 置真、启动补间,currentRoom 要等 1 秒后 finishShifting 才换。所以"第几秒进新房间"的答案永远是补间结束时,而不是碰撞那一帧。

lua
local gen = coroutine.wrap(function()
    coroutine.yield { phase = 'visit' }
    coroutine.yield { phase = 'done' }
end)
local step = gen()      -- visit

yield 把值交回调用方并挂起,调用栈原样保留;return 直接结束函数,第二次调用只会从头再来。这是生成器能单步调试、而普通循环不能的原因。

  • 补间被打断,finishShifting 不执行,shifting 会永久为真
  • currentRoom 换掉之后才关旧门,否则门会留在新房间里
  • 协程只能往前走,没有回退,重新生成要新建一个句柄

数据:写的和跑的不一样 ​

数据运行期实际行为说明
ENTITY_DEFS[key]全局共享,只读改它就是改所有同类实体
Entity.animations每个实体新建一套createAnimations 造的
solid没有任何地方读取挡路要自己写
defaultState只决定出生帧之后看 state
looping = false被 or true 吃掉单次播放靠状态退出
TILE_EMPTY合法编号 19越界索引才会得到 nil
onCollide每帧都调站在开关上会重复触发
onConsume只该调一次拾取逻辑要自己移除对象

出现"动画不停""罐子挡不住人""只播一次却循环"这类现象,先怀疑数据没被读取,而不是逻辑写错。onCollide 与 onConsume 混用也是同一类问题:前者每帧都来,后者本意只有一次。逐个核对引用点最快:

  • 用 print_r(ENTITY_DEFS['player']) 确认表结构
  • 在 Entity:init 打印 #self.animations,确认动画真的建出来
  • 搜 solid 和 looping 的所有出现位置,看有没有读取方
  • 把 love.graphics.rectangle('line', …) 加回 render,看盒子画在哪
  • 改共享定义表会让所有同类实体一起变,改实例字段只影响一个
  • 数据里出现的每个字段都要能找到读取它的代码,找不到就是死数据,删掉比留着安全