【语法算法】
一、一行代码拆解
rotate = angle * 57.29577951——弹道贴图转向的常客数字。57.29577951 就是 180 除以 π:Lua 三角函数全家桶吃弧度,引擎的 setRotation 吃角度,这个魔法数是两个单位制之间的过路费。真正值得拆的不是乘法,是 angle 从哪来:一行 math.atan2(dy, dx)。朝向计算的完整链路,从两个坐标差开始,到贴图旋转结束,中间全是单位制和坐标系的换算陷阱。
二、底层原理:弧度制与屏幕坐标系
Lua 的 math.sin、math.cos、math.atan2 全部以弧度为单位,一整圈是 2π;而引擎 API 的 setRotation 以度为单位,一整圈 360。atan2(dy, dx) 根据纵横坐标差返回指向角,天然把"方向向量"压缩成一个标量。但屏幕坐标系 Y 轴朝下,数学坐标系 Y 轴朝上,同一个 atan2 结果在两套坐标里是镜像的——朝向差出整整一个符号,这就是"怪永远背对着我转"的病根。另外每套引擎的 0 度基准不一样,有的朝右有的朝上,换算前先拿一根箭头试转一圈,比看文档快。
flowchart LR
A["坐标差 dx, dy"] -- "math.atan2(dy, dx)" --> B["弧度 rad"]
B -- "rad * 57.2958" --> C["角度 deg"]
C -- "deg + 基准修正<br/>(Y轴翻转/0度朝向)" --> D["setRotation 最终值"]
B -- "rad / (π/4) 四舍五入" --> E["八方向档位 0~7"]
E -- "查方向贴图表" --> F["GetFrameAnimIndex<br/>取对应帧动画"]
三、正确写法(可直接落地)
-- 方向向量 → 贴图旋转角
function DirToRotation(dx, dy)
local rad = math.atan2(dy, dx)
local deg = rad * 57.29577951 -- 弧度转角度
return deg + 90 -- 本引擎 0 度朝上, 修正基准
end
-- 连续角度 → 八方向档位(传奇类帧动画标配)
-- 0上 1右上 2右 3右下 4下 5左下 6左 7左上
function AngleToDir8(dx, dy)
if dx == 0 and dy == 0 then
return 0 -- 无位移保持原方向, 调用方自己记
end
local rad = math.atan2(dy, dx)
local idx = math.floor((rad + math.pi) / (math.pi / 4) + 0.5)
local map = { 6, 7, 0, 1, 2, 3, 4, 5 } -- atan2 基准到贴图基准的档位映射
return map[(idx % 8) + 1]
end
-- 帧动画索引: 动作 + 方向合成
function GetFrameAnimIndex(act, dir)
return act * 8 + dir -- 每个动作 8 个方向连续排布
end
四、四个必须钉死的细节
五、心法:一个方向只算一次
朝向是高频计算,每帧每个移动体都要来一遍。健康的项目里,方向只会被算一次:移动算出 dx dy,一处算出档位,旋转、贴图、索敌锥形判定全部复用这个结果;不健康的项目里 atan2 满天飞,同一只怪一帧转四次朝向。方向如此,距离也一样——dist 和 dir 一起算一起存,这是一对天生的搭档。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【语法算法】 闭包工厂的本质就这一行: 外层函数接收配置,返回一个闭包——闭包捕获配置,后续调用使用捕获的配置——闭包工厂的…
【语法算法】 闭包工厂的本质就这一行: 外层函数接收配置,返回一个闭包——闭包捕获配置,后续调用使用捕获的配置——闭包工厂的…
【语法算法】 泛型 for 的四种形态就这两行: 泛型 for 的四种形态覆盖了 Lua 所有的遍历需求——从无序遍历到有序…
【语法算法】 string.find 的起始偏移就这一行: 第三个参数 init 是搜索的起始偏移——从字符串的第 init…
【语法算法】 CPU 时间和墙钟时间的分界就这两行: os.clock 返回 CPU 时间——程序实际占用处理器的秒数——o…
【语法算法】 元方法 __unm 的触发就这一行: 对带 __unm 的表做一元负号操作 -t 时,Lua 调用 __unm…