FPS游戏:实现GDI方框透视

FPS游戏可以说一直都比较热门,典型的代表有反恐精英,穿越火线,绝地求生等,基本上只要是FPS游戏都会有透视挂的存在,而透视挂还分为很多种类型,常见的有D3D透视,方框透视,还有一些比较高端的显卡透视,在透视实现难度上,方框透视是最复杂的一种,本教程将学习方框透视的实现算法,并编写通用辅助实现透视效果。

方框透视的原理是通过读取游戏中已知坐标数据,并使用一定算法将自己与敌人之间的距离计算出来,结合GDI绘图函数在窗体上直接绘制图形,直到现在这种外挂依然具有极强的生命力,原因就是其比较通用,算法固定并能够应用于大部分的FPS游戏中。

寻找游戏坐标数据

教程中使用了反恐精英:起源这款FPS游戏作为演示对象,在开始编写方框算法之前我们需要获取一些坐标数据,这些数据是用于计算方框的基础,这里需要使用CE工具依次遍历找到 FOV视场角,本人坐标数据,本人鼠标角度,敌人坐标数据,玩家数量,玩家是否死亡,敌人之间的数组偏移,接下来将带大家把这些基址数据全部都找出来。

找FOV视角: 视场角又称FOV,视场角的大小决定了摄像机的视野范围,简单来说FOV就是屏幕与摄像机之间的夹角,我们可以通过狙击枪的狙击镜来找到游戏的视场角度,当未开镜状态时搜索未知初始化数据(浮点数),开镜后搜索改变的数值(浮点数),依次遍历即可找到该游戏的视场角度,一般的FPS游戏视场角为90度的居多。

1.直接开找,打开CE和游戏,购买一把狙击枪,然后在CE中搜索未知的初始值,注意这里要选择浮点数搜索。

2.回到游戏,打开狙击枪的一倍狙击镜,在CE中搜索变动的数值,接着打开二倍狙击镜,继续搜索变动的数值,最后关闭狙击镜搜索变动的数值,该过程要重复10次左右。

3.此时狙击镜处于关闭状态,直接搜索未变动的数值,然后拔出你的手枪,搜索未变动的数值因为手枪的视野与未开镜状态下的狙击枪是一样的,这样搜索能够尽量排除干扰,从而更精确的筛选到我们所需要的数据。

4.经过了上方的遍历以后,结果已经不多了,我们可以猜测这个角度应该在0-180度之间,所以通过介于两者之间再次筛选一下结果,之后就可以看到有两个90度的角,而且是绿色的基址,一般情况下开发人员默认会将这个角设置为45,75或90度,这样会方便后期的编程,之间的偏差不会太大。

5.回到游戏,右键手动调整狙击镜视野,发现下方地址栏中的数据也会发生变化,我们双击242CDD34这个内存地址,会发现这个基址是由client.dll+2CDD34模块加偏移得到的,至此我们已经知道了这款游戏的视场角是90度,而每次开镜242CDD34这个地址就会随之变化,从而确定该地址就是FOV。

6.那我们该如何通过代码的方式读取到这个游戏当前的FOV数据呢?这里我通过易语言编写并封装了透视模块使用该模块将使透视辅助编写变得简单,后续的内容都会用到这个模块。


找本人坐标数据: 通常情况下(X,Y)坐标的浮动较大不好定位,我们可以找Z坐标因为Z坐标控制人物的高低参数比较好找,首先搜索未知初始值(浮点数)然后跳到箱子上或走向更高的位置搜索增加的数值,回到地面上搜索减少的数值,重复这个过程最后就能找到Z轴的坐标,在游戏中(X,Y,Z)坐标是紧挨着的结构(+0,+4,+8) 找到了Z坐标相应的就可以计算出(X,Y)坐标。

1.打开CE并进入游戏,我们找一个比较平坦的地面,这里所使用的地图是de_dust2,然后使用CE直接搜索未知的初始值搜索类型为4字节即可。

2.接着我们跳到第一个高点上,可以跳到箱子上或者是比当前的地面高一些的地方,然后搜索增加的数值结果如下。

3.上方搜索完成以后,接着我们跳到第二个高点上,然后继续搜索增加的数值搜索结果如下。

4.直接从箱子上跳到地面上,然后搜索减少的数值,搜索完成后不要动,直接在地面上搜索未变动的数值,这样循环不断的排查。

5.经过不断的尝试与排查,我们已经找到了Z轴的坐标地址是242CBE4C,用上方的内存地址每次减去4,即可得到Y与X这两个坐标的内存地址。

直接使用易语言配合透视模块,来读取坐标数据。


找自己鼠标角度: 通常FPS游戏鼠标的准心Y坐标向上抬会减少,鼠标准心向下会增加,不断的遍历(浮点数)就可以搜索到鼠标的准心Y坐标,得到了鼠标的Y坐标之后然后+4就能得到鼠标的X的坐标参数。

1.打开CE进入游戏,将鼠标放置在屏幕的中心位置,直接搜索未知初始化数据(浮点数),然后将游戏鼠标向上微抬,回到CE搜索减少的数值多次向上抬并搜索减少的数值。

2.接着将鼠标逐步向下微压,回到CE然后搜索增加的数值这里要重复十几次,最后不要动鼠标直接搜索未变动的数值即可找到以下结果,这里都是基地址选哪一个都可以。

这里我们选择242CDF9C这个地址,然后在其基础上+4得到X坐标,通过使用易语言编程获取到这两个参数,代码如下所示:


找单个敌人坐标数据: 首先在开始游戏之前通过控制台暂停对方阵营机器人的走动,使用bot_stop 1命令暂停,暂停后搜索未知初始值,然后使用bot_stop 0命令让机器人走两步后马上暂停,搜索变化的数值,开启机器人走动马上暂停,再次搜索变化的数值,不断尝试直到找到正确的数据,只要找到X轴的坐标,可以通过+4,+8的方式得到(Y,Z)的相对偏移。

1.首先开始一局游戏加入一个机器人(按下+号键添加),然后输入bot_stop 1命令让机器人暂停,在CE中搜索未知初始数据

2.输入 bot_stop 0 让机器人运动两步后马上暂停,然后CE中搜索变动的数值这个步骤需要重复多次,最终能够看到有几个非常像坐标的数据,下方的三个标红数据都可以,此处我就直接选择 1CBFFDD8 作为演示对象。

3.将找到的内存地址加入到下方地址栏,然后我们右键选择找出是什么改写了这个地址,记下这个偏移数据([esi+15B8]),和基地址([1CBFE820])后期会用到。

4.直接双击这个内存地址,然后可以看到 server_css.dll+3D24E4 模块地址+偏移地址,然后与15B8相加就能得到X轴的坐标数据。

总结:在15B8的基础上每次递增+4既可得到Y轴与Z轴的坐标地址,最终可以用易语言编程获取单个敌人的坐标数据了。


取当前玩家数量: 玩家数量的查找非常简单,大部分的FPS游戏都有人物统计菜单,按下TAB键则可看到,我们可以通过查看人物数量来查找,第一次搜索1,然后按下+号添加1个机器人搜索2,再次添加一个机器人搜索3,不断遍历即可得到玩家数量。

1.打开CE修改器,进入游戏后,按下TAB键即可看到当前只有自己,我们在CE中搜索1即可。

2.按下大键盘下的+号,然后在CE中输入2点击再次搜索,以此循环,直至找到绿色的基址为止。

总结:这个取玩家数量太简单了,自己找找就是,上方的绿色地址都可以作为判断依据,这里就直接使用 server_css.dll+3D24B8 这个地址了,易语言读取代码如下所示。


判断敌人是否死亡: 取敌人的当前状态,在CS中我们可以搜索敌人的血量,首先添加1个机器人,然后搜索100,打敌人一枪(不要打头)搜索减少的数值,然后搜索未变动的数值,再次打敌人一枪搜索减少的数值,注意不要把敌人打死了就行,不断的遍历最后就能找到我们想要的敌人的血量,通过血量则可判断该地人似否死亡。

1.进入游戏手动添加一个机器人,此时机器人的血量是100,我们直接搜索精确数值100。

2.用手枪打敌人的脚,不要打头!然后直接搜索减少的数值搜索完成以后,直接多次搜索未变动的数值重复2-3次即可找到为数不多的几个地址。

上方找到了四个看似与血量相关的地址数据,我们分别将这几个数据改为100,发现当2CC7754C被改为100时其他的地址也跟着变成了100,说明第三个就是人物的血量。

3.接着在第三个地址处右键选择找出是什么改写了这个地址,可以看到偏移是9c,我们继续搜索 2CC774B0 这个内存地址。

4.此时找到了基地址 server_css.dll+3D24E4 然后加上9C就是当前敌人的血量地址了。

5.我们打死这个敌人,会发现血量变成了1说明这款游戏当人物死亡时,会用1来表示。

总结:知道了这个特性,我们就可以用易语言来判断敌人是否死亡了哈,代码如下:


找敌人之间的数组偏移: 在前面我们已经找到了第一个敌人的数据server_css.dll+3D24E4指向的就是第一个敌人的地址,通过与偏移15B8相加就能得到X坐标,在此基础上加4就能得到Y坐标,显然该游戏并不会将玩家数据放到偏移中,很有可能每个敌人分别占用一个地址,我们可以通过使用内存遍历工具,找到第二个敌人的地址,然后用第2个敌人的地址减去第1个敌人的地址就能得到敌人与敌人之间的差值。

1.首先进入游戏,添加两个机器人并将机器人暂停bot_stop 1,然后在CE工具的内存地址栏中,添加server_css.dll+3D24E4这个内存基址。

2.接着用易语言编写一个乞丐版的基址遍历器,你也可以通过CE进行结构爬行,网上也有很多基址遍历工具可用,我这里为了方便就直接两行代码搞定,代码如下:

3.游戏中保证只有两个机器人,然后运行这段代码,我们知道第一个地址003D24E4是第一个敌人的坐标数据,由于人物的内存矩阵中数据的排列不会偏差太大,这里我们主要找与6.231969801318e-012偏差不太大的内存来分析。

也可以多加机器人,这样能看的更清楚一些

上方结果中可知:地址003D24F4003D24E4浮点数的后缀相同且偏差不大,可以断定这两个就是两个敌人的基址,此时我们用 003D24F4 减去 003D24E4 等于 10 说明10就是敌人与敌人之间的偏移地址,不同的敌人与敌人之间相隔就是10,最后我们直接使用易语言获取到所有敌人的坐标数据:


绘制屏幕方框与屏幕写字: 绘制外部方框就是调用了GDI绘图函数让其在指定的窗口句柄上绘图,我已经将绘制代码封装,直接调用就好这里就不罗嗦了。

分别调用绘制方框与绘制文字,测试效果如下:


方框透视算法分析

在前面的教程中我们已经手动找到了FOV视场角``本人坐标数据``本人鼠标角度``敌人坐标数据``玩家数量``玩家是否死亡``敌人之间的数组偏移这些基址数据,多数情况下类FPS游戏找坐标手法都大同小异,接下来我们将具体分析计算方框的思路,以及实现这些方框绘制算法。

第一象限求角: 假设敌人在第一象限,求鼠标指向与敌人之间的夹角b,可以使用反正切求导。

我们知道自己与敌人的相对(X,Y)距离,可以使用反正切公式求出a角的度数。而我们最终的目的是要求出我们的鼠标指向与敌人之间的夹角b,此时我们可以通过已知的鼠标角度C减去a既可得到b的角度。

第二象限求角: 假设敌人在第二象限,而我们的鼠标依然指向在第一象限,求敌人与X轴之间的夹角度数。

如上图:由于(X,Y)(黑色)是已知条件,我们可以通过X比Y求反正切,即可得到a角的度数,然后与90度相加,即可求出敌人当前坐标位置与X轴之间的夹角度数。

第三四象限: 敌人在第三与第四象限与上图差不多,最终目的就是求敌人的位置与X轴之间的夹角,第三象限应该加180度,第四象限加上270度数。这里就不罗嗦了,很简单的东西。

另外4种特殊情况: 如果敌人在第一象限且与X轴重合,那么敌人与X轴为之间的夹角度数必然为零度,同理如果与Y轴重合的话,那么X轴与敌人之间的夹角度数为90度,以此类推就是这四种特殊情况。

上方的4条象限与特殊情况,如果展开的话一共是8种不同的情况,如下代码就是这八种不同情况,调试下面的这段代码会发现一个缺陷,那就是当我们绕着敌人转圈时,偶尔会出现一个大于180度的角度,这又是两种非常特殊的情况。

特殊情况: 当敌人在第四象限且鼠标角度依然在指向第一象限的情况下,则会出现大于180度的角。

如上图:我们的目标是求鼠标角度与敌人之间的夹角度数,而此时的鼠标指向第一象限,而敌人却在第四象限上,我们用360度减去e角度(e = 敌人坐标与x轴之间的夹角度数),即可得到K角度,用K角度加上M角度,即可得到鼠标与敌人之间的夹角度数,另一种特殊情况敌人与鼠标角度调换位置求角,最终代码如下:

FOV视场角度: 摄像机的作用就是,移动游戏中的场景,并将其投影到二维平面,显示给玩家。

如上图:摄像机与屏幕之间的夹角统称为视场角,游戏中的准星位置到屏幕的边缘是FOV的一半,以屏幕分辨率1024x768为例,当FOV为90度时,则准心与屏幕的垂线构成45度等腰直角三角形,此时的摄像机到屏幕的距离就是一半屏幕长度(1024/2 = 512)的大小。

三维横坐标转屏幕X坐标: 将三维矩阵中的敌人坐标数据,转换为屏幕的X坐标。

如上图:我们需要求出敌人位置的坐标数据,可以使用 (x/y) x (1024/2) 最后还需要加上P的长度,由于窗口的总长度是1024那么我们可以直接除以2得到另一半的长度(512),将敌人位置与另一半长度相加就是敌人投射在屏幕上的X坐标,但是此时我们并不知道(X,Y)的长度,所以需要先求出(X,Y) 如下图所示。

上图中:我们需要求出(X,Y)的距离,此时我们已经知道了M和C的长度,则此时我们可以直接使用勾股定理M的平方 + C的平方 (开方)= Z,得到Z之后,通过 sin a = (x/z) => sin a * z = X 此时我们已经得到的X的长度,接着 cos a =(y/z) => cos a * z = Y 此时我们也得到了Y的长度,最后 (x/y) x 512 + 512 即可得到敌人位置,投射到屏幕上的X坐标。

三维纵坐标转屏幕Y坐标: 三维横坐标搞懂了,这个纵坐标就更简单了,如下图:

上图中:通过tan公式即可推导出d与c的距离,然后将d与c的长度相加,即可得到鼠标指向与敌人位置之间的距离,然后再加上屏幕高度的一半,本游戏屏幕高度为768,所以要加上384即可。

最终屏幕横坐标与纵坐标的转换算法如下所示,最后一点代码不搞了!要搬砖去了!


最后的透视效果如下,此处游戏屏幕必须为1024x768,三维坐标转屏幕坐标算法中已经写死了,其他屏幕尺寸需要自行调整代码中的比值关系与相应数值。

posted @ 2019-10-03 17:40  lyshark  阅读(18616)  评论(7编辑  收藏  举报

loading... | loading...
博客园 - 开发者的网上家园