FPS 游戲實現GDI透視 (三角函數)


FPS游戲可以說一直都比較熱門,典型的代表有反恐精英,穿越火線,絕地求生等,基本上只要是FPS游戲都會有透視掛的存在,而透視掛還分為很多種類型,常見的有D3D透視,方框透視,還有一些比較高端的顯卡透視,在透視實現難度上,方框透視是最復雜的一種,本教程將學習方框透視的實現算法,並編寫通用輔助實現透視效果。

方框透視的原理是通過讀取游戲中已知坐標數據,並使用一定算法將自己與敵人之間的距離計算出來,結合GDI繪圖函數在窗體上直接繪制圖形,直到現在這種外掛依然具有極強的生命力,原因就是其比較通用,算法固定並能夠應用於大部分的FPS游戲中。

下面的這種繪制方式,是最笨的辦法了,但是卻最通用,只需要能讀取到游戲的坐標,即可完成繪制方框,相比於D3D的注入方式,該方式也更加穩定,水平有限大佬請繞過吧,只能理解到這里了。

反恐精英下載地址:鏈接:https://pan.baidu.com/s/1U4-E9-xNIoHOyLg5aP_l7w 提取碼:yupq
透視模塊下載地址:鏈接:https://pan.baidu.com/s/1h-9CXW8yPggcgKtOUYAsiA 提取碼:vg9v

尋找游戲坐標數據

教程中使用了【反恐精英:起源】這款經典FPS游戲作為演示對象,在開始編寫方框算法之前我們需要獲取一些坐標數據,這些數據是用於計算方框的基礎,這里需要使用CE工具依次遍歷找到 【FOV視場角】【本人坐標數據】【本人鼠標角度】【敵人坐標數據】【玩家數量】【玩家是否死亡】【敵人之間的數組偏移】接下來老司機將帶大家把這些基址數據全部都找出來。滴滴滴 ~ 上車!!

找FOV視角: 視場角又稱FOV,視場角的大小決定了攝像機的視野范圍,簡單來說FOV就是屏幕與攝像機之間的夾角,我們可以通過狙擊槍的狙擊鏡來找到游戲的視場角度,當未開鏡狀態時搜索未知初始化數據(浮點數),開鏡后搜索改變的數值(浮點數),依次遍歷即可找到該游戲的視場角度,一般的FPS游戲視場角為90度的居多。

1.直接開找,打開CE和游戲,購買一把狙擊槍,然后在CE中搜索【未知的初始值】,注意這里要選擇浮點數搜索。這把槍是M700當年用這把槍打CF槍槍爆頭很刺激,回想當年殺紅眼的樣子現在還記憶猶新。

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,三維坐標轉屏幕坐標算法中已經寫死了,其他屏幕尺寸需要自行調整代碼中的比值關系與相應數值。

這里在插入一個知識點,就是關於敵人血量的顏色顯示問題,要實現以下效果也很簡單。

只需要做一個判斷即可了。


番外篇(代碼的改進)

上方的代碼,雖然可以在XP系統上繪制出來,但是如果在win10上會出現卡頓和繪制方框頻繁閃爍的問題,這是因為我們的方框直接繪制在了游戲的緩沖區上了,該區域的刷新頻率與我們自身頻率不符導致的,解決的辦法是,自己創建一個窗口,然后將窗口設置為透明狀態,並將其附加到游戲上面,在自己的窗口中繪制,即可解決這個問題,透明窗體代碼如下。

只需要在上方代碼基礎上,改進一下,將繪制過程放到程序自身窗體中即可解決問題。

最后關於菜單的繪制,可以使用IMGUI.ec 這個模塊來完成,也可以自己繪制菜單。

imgui C語言原版,下載地址: https://github.com/ocornut/imgui 下載好以后,在文件的example有些案例,其中就包括DX9的繪制方法。編譯后運行起來看看。

Imgui.ec 模塊也可實現窗體的自繪,菜單自會。大致代碼如下這段代碼,是有問題的,想要在CS起源中使用需要改下即可。

最終可實現動態菜單,這個使用了D3D繪制,所以屏幕上不會出現閃爍的情況。

CSGO 同樣可是使用的菜單,該方法通用於各種游戲中,外部D3D就是這個樣子,內部的話,不通用。需要劫持函數。

為透視增加血條和距離,需要引入D3D透視模塊,只是在上面模塊基礎上封裝的,這種模塊網上也很多,很好找到的。

另外,上方演示的CS起源版本是低版本的,高版本的一個我也找了一下游戲的基址數據(CS 起源V87版),上方透視代碼不需要改變,只需要替換基地址后即可實現高版本算法透視,將前面功能整合起來,最終形成一個完整輔助。

關注我,下次帶大家來找CSGO這款游戲的透視矩陣,並演示如何使用外部D3D繪制完整的方框。


原創:寫教程不易,又是畫圖又是求角,轉載請加出處,您添加出處是我創作的動力!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM