大家好,又见面了,我是你们的朋友全栈君。
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
偏差不太大的内存来分析。
也可以多加机器人,这样能看的更清楚一些
上方结果中可知:地址003D24F4
与003D24E4
浮点数的后缀相同且偏差不大,可以断定这两个就是两个敌人的基址,此时我们用 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的一半,以屏幕分辨率1024×768为例,当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即可。
最终屏幕横坐标与纵坐标的转换算法如下所示,最后一点代码不搞了!要搬砖去了!
最后的透视效果如下,此处游戏屏幕必须为1024×768,三维坐标转屏幕坐标算法中已经写死了,其他屏幕尺寸需要自行调整代码中的比值关系与相应数值。
完全原创:写教程不易,又是画图又是求角,转载请加出处,您添加出处是我创作的动力!
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/154766.html原文链接:https://javaforall.cn