跳到正文

相机滚动 ​

屏幕只有 256 像素宽,关卡有 1600 像素;相机决定把哪一段世界搬到屏幕上。

translate 反向平移 ​

love.graphics.translate(dx, dy) 不移动相机,它移动之后所有绘制的原点。想让相机右移 100 像素,就要把世界画在 -100,所以传负值。

lua
love.graphics.push()
love.graphics.translate(-math.floor(camX), 0)
level:render()
love.graphics.pop()

push 与 pop 必须成对。少了 pop,平移会累积到下一帧,分数和血条会越画越偏。平移只作用于世界内容。

跟随玩家 ​

把相机左边缘放在玩家中心左边半屏处。玩家宽 16,中心在 x + 8,于是减去 VIRTUAL_WIDTH / 2 - 8。

lua
camX = player.x - (VIRTUAL_WIDTH / 2 - 8)
camX = math.max(0,
    math.min(TILE_SIZE * mapWidth - VIRTUAL_WIDTH, camX))
变量含义本例
camX相机左边缘的世界坐标0–1344
VIRTUAL_WIDTH视口宽度256
mapWidth地图宽度,单位格100
backgroundX背景偏移camX / 3 % 256

夹取两端:左边不小于 0,右边不超过地图宽度减视口宽度。少了夹取,起点会露出地图外的空白,终点会走出关卡边界。

先 math.min 再 math.max,顺序写反会得到负数。玩家宽高变化时,公式里的 8 要跟着改成新的半宽。相机位置每帧重算,不用增量累加;两个边界相等时相机固定不动。

取整与视差 ​

math.floor(camX) 是必须的。push 把 256×144 放大到窗口,最近邻采样遇到小数偏移时,相邻像素的取整结果不一致,地面接缝会闪。绘制精灵前也要 math.floor(x),否则角色仍会抖。

用 push 库时这一点更明显:它先按整数倍放大,剩余边距用额外偏移补齐,小数偏移会被放大成可见的错位。

背景用 camX / 3 做视差:相机走 3 像素,背景走 1 像素,远景显得更远。背景图宽 256,取模后循环拼接,第二张画在 +256 处。只在部分设备上闪,多半是窗口尺寸不是整数倍。

跟随方式 ​

锁定中心最简单,但画面一直在动,容易晕。死区更稳,玩家能看到前方;平滑跟随过渡柔和,代价是输入反馈变慢。

方式做法特点
锁定中心相机等于玩家坐标减半屏简单,画面持续移动
死区越过边界才推相机稳定,兼顾前方视野
平滑每帧向目标插值柔和,像素取整后会一顿一顿

像素游戏通常不用平滑。math.floor 之后,缓慢的插值会被量化成阶梯式跳动,反而比直接跟随更明显。死区阈值先用 40% 和 60%。

定参数时按这个顺序:先定虚拟分辨率,再选跟随方式,用地图边界夹取,平移前取整,最后调背景视差。UI 始终留在平移之外。

相机边界与关卡起点 ​

关卡最左端 camX 被夹在 0,玩家此时并不在屏幕中心,这是正常的。不要为了让玩家居中而在关卡左边补空白格:空白会参与碰撞和生成,玩家可能直接掉下去。起点镜头固定是正常现象。

同理,关卡右端要留出至少一屏宽度,否则走到终点时镜头停住,玩家会顶着屏幕边缘走。相机每帧只读玩家位置,不要用增量累加。玩家的夹取和相机的夹取是两套边界。

相机只影响绘制,不进入碰撞、生成或存档。需要知道某个对象在屏幕上的位置时,用 worldX - camX 现算。位置计算都用浮点,绘制前才取整。

缩放与像素 ​

push 把 256×144 放大到窗口。放大倍数不是整数时,一个世界像素对应一个半屏幕像素,任何非整数偏移都会让像素粗细不均。

固定窗口比例,或选一个能整除的分辨率,能避开大部分接缝问题。整数倍放大加黑边是像素游戏最稳的方案,代价是屏幕利用率。缩放倍数固定后,接缝问题基本消失。

想满屏又不失真,选 1280×720 这种 5 倍窗口。窗口大小固定后,取整的收益才稳定;缩放倍数尽量用整数。