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

C++ 游戏辅助进阶系列(二):目标怎么选,FOV 筛选、权重排序与平滑防抖

约 3864 字 · 预计阅读 10 分钟
一句话总结:自瞄的第二层是「选择与克制」——视场角内才参与、按角度与血量加权排序、再用平滑系数把一步到位的转向拆成多帧,本篇把选目标的完整决策树写成代码,参数怎么调也一并给出。

上一篇解决了「该转多少度」,本篇解决「转给谁」和「怎么转」。这两件事合起来决定了一个东西:像不像人。数学谁都会写,筛选和克制的分寸才是工程里的真功夫。

先把不合格的候选人筛掉

遍历绘制系列第二篇的实体快照,一个目标要过四道门槛才有资格被瞄准:

  1. 活着:m_lifeState == 0 且血量大于 0
  2. 是敌人:m_iTeamNum 与本地玩家不同
  3. 在视场角内:与当前视角的夹角小于 FOV 上限
  4. 可见数据有效:坐标不是 (0,0,0) 这类异常值

前两条是硬条件,一条不满足直接跳过。第三条是本篇重点,第四条常被忽略:快照线程偶尔会读到半截数据,坐标全零的「幽灵目标」会让数学函数算出莫名其妙的角度,加一行判空省两小时调试。

FOV 筛选:两种算法,选一种

方法 A:屏幕空间法。复用第三篇的 W2S,把目标投影到屏幕,算它与屏幕中心的像素距离,超过 屏幕高度 × tan(FOV/2) 就丢弃。直观、好调试(画个圈就能看),但目标在屏幕外时投影会算错,需要额外的背面剔除逻辑。

方法 B:角度空间法。直接在角度域比较,不依赖投影。两个方向向量的夹角用点积公式:cos θ = (a·b) / (|a| × |b|)。代码:

float AngleBetween(const Angles& a, const Angles& b) {
    float dp = cosf(a.pitch * 0.01745f) * cosf(b.pitch * 0.01745f)
             * cosf(a.yaw * 0.01745f  - b.yaw * 0.01745f)
             + sinf(a.pitch * 0.01745f) * sinf(b.pitch * 0.01745f);
    return acosf(fmaxf(-1.0f, fminf(1.0f, dp))) * 57.2957795f;
}

我更推荐 B:不依赖屏幕分辨率,FOV 语义是纯粹的「视角锥」,参数调起来更有物理直觉。5° 的 FOV 意味着只瞄几乎正对的目标,30° 就是「甩到大概方向就算中」。

图 1:视角锥内的目标才参与评分,角距越小分越高;锥外直接出局

评分排序:角距为主,血量为辅

锥内可能同时有两三个目标,选哪个?主流做法是「角距最小优先」——离准星最近的目标,转向代价最小、动作最自然。想更激进可以加权:

struct Candidate { int index; float score; };

// score 越小越优先
float Score(float angDist, float hp, float dist) {
    return angDist * 2.0f              // 角距权重最高
         + (hp / 100.0f) * 10.0f       // 血少的略微优先(补枪逻辑)
         + dist * 0.001f;              // 距离做极弱的 tie-breaker
}

权重没有标准答案,只有场景偏好。竞技服里角距权重拉满是主流;打 bot 练习时把血量权重加上,能把残血目标优先清掉。我的建议是从纯角距开始跑,感觉不顺再一项一项加权重——三个权重一起调,你永远不知道是哪个在作妖。

平滑:把一步到位拆成多帧

选好目标,拿到了需要转的角差 delta,最原始的写法是直接把当前视角设成目标角。这个效果你必须亲手看一次才会理解为什么没人这么用:视角瞬间吸附,人跟上了锁,人一死视角弹回原位——机械感扑面而来,观感上就是「我不是我在玩」。

解法是平滑:每帧只转角差的一小部分。

// smooth = 1 时退化为锁死;越大越「人」
void ApplySmoothing(float& cur, float target, float smooth) {
    float step = NormalizeAngle(target - cur) / smooth;
    cur = NormalizeAngle(cur + step);
}

// 渲染循环里
ApplySmoothing(curYaw,   targetAng.yaw,   g_smooth);   // g_smooth 取 5~8
ApplySmoothing(curPitch, targetAng.pitch, g_smooth);

smooth 取值的手感大致是:1 = 锁死,3 = 跟随很快但看得出吸附,5–8 = 多数人觉得「顺滑」的区间,15 以上基本是「慢慢飘过去」,只适合大 FOV 的娱乐模式。配合上一篇说的 30Hz 快照,快照间隔内的抖动会被平滑天然抹平,这是读画解耦的第二个红利。

参数组合的起步建议

三个参数互相纠缠,单独调容易调晕。给一组能直接跑的起点:FOV 8°、smooth 6、纯角距评分。跑顺之后按需求动:想只瞄近距离贴脸的,FOV 砍到 4°;想让优先级更偏向残血,把血量权重从 10 拉到 25。每改一个参数打十组对照,别一次改三个。

下一篇预告

选好了人、算好了平滑路径,还剩收尾的动作:把新视角写回游戏。下一篇上 WriteProcessMemory,处理引擎模块的视角偏移,顺便解决「瞄身体还是瞄头」——读骨骼矩阵拿头部世界坐标,弹道从胸口升级到爆头线。

目录导航
  • 先把不合格的候选人筛掉
  • FOV 筛选:两种算法,选一种
  • 评分排序:角距为主,血量为辅
  • 平滑:把一步到位拆成多帧
  • 参数组合的起步建议
  • 下一篇预告
  • 相关文章:

  • C++ 游戏辅助进阶系列(四):速度预判与边界,给自瞄系列收个尾2026-10-20 01:02:47
  • C++ 游戏辅助进阶系列(三):写视角与骨骼瞄准,把自瞄真正打通2026-10-20 01:02:47
  • C++ 游戏辅助进阶系列(一):自瞄的数学地基,从两个坐标算出该转多少度2026-10-20 01:02:47
  • C++ 游戏辅助绘制系列(四):ImGui 菜单与工程收尾,从 demo 到像样的工具2026-10-20 01:02:47
  • C++ 游戏辅助绘制系列(三):WorldToScreen 投影,把三维坐标钉到屏幕上2026-10-20 01:02:47
  • C++ 游戏辅助绘制系列(二):读内存,把「敌人在哪」变成几个函数调用2026-10-20 01:02:47
  • C++ 游戏辅助绘制系列(一):透明窗口与点击穿透,把画布架在游戏上面2026-10-20 01:02:47
  • 发表评论

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