当前位置:首页 > 编程开发 > 正文

C++ 游戏辅助进阶系列(一):自瞄的数学地基,从两个坐标算出该转多少度

约 4056 字 · 预计阅读 11 分钟
一句话总结:自瞄的核心不是读写内存,是一点三角函数——用自己与目标的两套坐标算出方向向量,再换算成游戏视角的偏航角 yaw 和俯仰角 pitch,本篇把这个最底层的函数写出来并验证,全程在虚构靶场《星陨 Online》上进行。

绘制系列四篇收尾时留了个口子:框架能看见人了,但「看得见」和「打得中」之间还差一个自动转向。这个进阶系列就补这块,依然是四篇:角度数学(本篇)→ 目标选择与平滑 → 写视角与骨骼瞄准 → 速度预判与边界讨论。

设定沿用前作:靶场是虚构的《星陨 Online》(StarFall.exe),所有偏移继续编,练习请用 Pwn Adventure 或自己的程序。绘制系列的代码全部可以直接复用,没看过的话建议先看(一)透明窗口和(二)读内存。

先约定视角的坐标系

不同游戏对视角角的定义五花八门,动手前必须把靶场的约定弄清。星陨靶场采用最常见的 FPS 约定:

  • yaw(偏航角):绕竖直轴左右转,范围 -180° 到 +180°,0° 朝向 +X 方向
  • pitch(俯仰角):抬头低头,范围 -89° 到 +89°,抬头为正
  • 视角数据存放在引擎模块:engine.dll + 0x6ABC30(编的),两个 float,顺序 pitch、yaw

这个约定跟多数 Source 系引擎一致,学一遍到处能用。

数学:两步三角函数

设自己的眼睛位置是 eye(本地玩家坐标 z 加视高 64),目标胸口是 target(脚底 z 加 48)。方向向量:

Vec3 delta = target - eye;   // dx, dy, dz

yaw 是方向向量在水平面上的投影角。画个俯视图,X 朝右 Y 朝上,atan2 天生就是干这个的:

float CalcYaw(const Vec3& delta) {
    return atan2f(delta.y, delta.x) * 57.2957795f;   // 180/π
}

pitch 是垂直面上的角,邻边是水平距离(用勾股),对边是高度差:

float CalcPitch(const Vec3& delta) {
    float horiz = sqrtf(delta.x * delta.x + delta.y * delta.y);
    return atan2f(delta.z, horiz) * 57.2957795f;
}

注意靶场约定「抬头为正」,所以 pitch 直接取正号即可;有些引擎是「低头为正」,那时要加个负号。别背结论,用自己的靶场打一发看视角往哪转,比查资料快。

图 1:yaw 在俯视图里算,pitch 在侧视图里算,各是一次 atan2

归一化:处理跨 ±180° 的坑

算出来的 yaw 是「绝对角」,但当前视角可能是 170°,目标在 -170°。直接拿 340° 去转,视角会绕远路还可能触发角速度检测。正确的做法是求「最短角差」,把差值归一化到 -180° ~ +180°:

float NormalizeAngle(float a) {
    while (a > 180.0f)  a -= 360.0f;
    while (a < -180.0f) a += 360.0f;
    return a;
}

// 当前视角 curYaw,目标角 targetYaw → 需要转的量
float delta = NormalizeAngle(targetYaw - curYaw);

这个函数短小到不像话,但它是整个自瞄里最容易写错的地方。检验方法很土:把当前角设成 179、目标角设成 -179,正确输出是 +2(右转 2 度),写错的版本会输出 -358。写完先跑这组用例再往下走。

组装:CalcAngle 完整版

struct Angles { float pitch, yaw; };

bool CalcAngle(const Vec3& eye, const Vec3& target, Angles& out) {
    Vec3 delta = { target.x - eye.x,
                   target.y - eye.y,
                   target.z - eye.z };
    float horiz = sqrtf(delta.x * delta.x + delta.y * delta.y);
    if (horiz < 1.0f) return false;        // 目标在头顶正上方,水平距离为零

    out.yaw   = atan2f(delta.y, delta.x) * 57.2957795f;
    out.pitch = atan2f(delta.z, horiz)     * 57.2957795f;

    out.yaw   = NormalizeAngle(out.yaw);
    out.pitch = fmaxf(-89.0f, fminf(89.0f, out.pitch));
    return true;
}

在靶场里放两个静止靶位验证:自己站在 (100, 100, 64),目标放 (500, 100, 112)——正右方偏上,算出来 yaw 应为 0、pitch 应约为 +4.4°。对上了,数学层就稳了。

三个值得提前知道的东西

每帧重算,不要缓存角度。视角是连续变化的,上一帧算的角度这一帧就是错的。缓存谁都可以,唯独别缓存角度。

眼睛高度别拍脑袋。视高每个游戏不一样,靶场是 64,错了整条弹道整体偏高或偏低。确定方法很朴素:站平地读一次 z,站箱子上再读一次,差值就是视高修正。

atan2 的参数顺序。是 atan2(y, x) 不是 atan2(x, y),这个错不会报编译警告,只会让你对着 45 度偏差调一下午。上面代码里我特意写全了注释。

下一篇预告

角度会算了,但「打谁」还没定——场上 32 个人总不能全瞄。下一篇写目标筛选:视场角(FOV)内才参与、按角度和血量加权排序,再加一层平滑插值,让转向不一步到位。这些都是「像不像人」的关键,也是这个系列技术含量最高的部分。

目录导航
  • 先约定视角的坐标系
  • 数学:两步三角函数
  • 归一化:处理跨 ±180° 的坑
  • 组装:CalcAngle 完整版
  • 三个值得提前知道的东西
  • 下一篇预告
  • 相关文章:

  • C++ 游戏辅助进阶系列(四):速度预判与边界,给自瞄系列收个尾2026-10-20 00:59:36
  • C++ 游戏辅助进阶系列(三):写视角与骨骼瞄准,把自瞄真正打通2026-10-20 00:59:36
  • C++ 游戏辅助进阶系列(二):目标怎么选,FOV 筛选、权重排序与平滑防抖2026-10-20 00:59:36
  • C++ 游戏辅助绘制系列(四):ImGui 菜单与工程收尾,从 demo 到像样的工具2026-10-20 00:59:36
  • C++ 游戏辅助绘制系列(三):WorldToScreen 投影,把三维坐标钉到屏幕上2026-10-20 00:59:36
  • C++ 游戏辅助绘制系列(二):读内存,把「敌人在哪」变成几个函数调用2026-10-20 00:59:36
  • C++ 游戏辅助绘制系列(一):透明窗口与点击穿透,把画布架在游戏上面2026-10-20 00:59:36
  • 渗透测试报告 — Z-BlogPHP 1.7.5 本地部署实例2026-10-20 00:59:36
  • 发表评论

    ◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。