創作內容

6 GP

從RMMV做的遊戲遊戲來理解chrome的js debugger(至少可以)怎麼用

作者:♙♲⚙\~O_O~/⚙♲♙│2021-04-26 00:33:21│巴幣:12│人氣:125
不重要的第一列字


為何撰寫本文:以後看到"好像會寫程式"但非資工本科的跑去寫有用到前端js的遊戲,然後有除錯(debug)障礙的,就貼這個給他看


事前準備

0. 一個支援http伺服器的程式,例如 python
(可參考:https://developer.mozilla.org/en-US/docs/Learn/Common_questions/set_up_a_local_testing_server#running_a_simple_local_http_server)
1. 一個RPG Maker MV(RMMV)做的遊戲(例子中使用幾乎空白的專案),支援網頁遊玩或沒有打包加密(RM版有很多RMMV)
2. 一個有javascript debugger功能的瀏覽器(例子中使用chrome),並且可以正常遊玩 1. 中準備的遊戲
3. 沒有其他軟體了


事前需學會的操作步驟

1. 在 "事前準備1." 中的遊戲目錄中,打開http伺服器,像這樣
2. 打開瀏覽器依據設定的ip(localhost 或 127.0.0.1 或 其他連得到你開http伺服器的電腦的ip或網址)及port(用python預設是8000)進入遊戲
3. 在瀏覽器中,遊戲的那個分頁中,按下F12
4. 確定你會在按下F12後出現的是視窗中,切換到下列tab: Console,Source,Performance



從例子中學習如何使用


例子:找出產生存檔的函式或流程,以便自行保存存檔內容

若多次遊玩或開啟無痕模式,應該會注意到存檔大概跟cookie有關。
已知使用瀏覽器遊玩RMMV遊戲時,使用下圖方式清空cookie會將存檔刪除

此時可以確定存檔是存於 cookie 或 localStorage

在 Console 中分別輸入 document.cookie 及 localStorage 後按 Enter

看到 cookie 是空的,而 localStorage 有東西,可確定存檔是儲存在 localStorage

接著我們去查 localStorage 的用法


得知 localStorage.setItem(鍵值,內容) 可以設定 localStorage

思考方向:想要知道 localStorage.setItem 在什麼時候被使用、被誰使用

localStorage.setItem 是個 native code ,沒辦法改它

但還是可以攔截它,可透過在RMMV中常見的修改函式的方式:


接著,在DevTools位關閉的情況下,在遊戲中存檔,儲存時DevTools視窗應該會跳出來:

可以在DevTools左下見到 DataManager.saveGame ,使用滑鼠點它一下,會自動跳到那個函式


我們在函式中看到 try...catch ,並且是從 backup 再執行 localStorage.setItem ,可能是在測試能不能正常存檔。但沒關係,我們這裡當作看不出來,稍微筆記一下這個位置後,按 黃色底的"Debugger paused"上面藍色的"慢播放"符號(),等待下一次真的儲存紀錄:

看來就是這裡了,左下角看到 StorageManager.saveToWebStorage 後用滑鼠點一下它,接著回 Console 分別輸入 key 和 data 後按 Enter ,以查看他們的值:

嗯,很長,看不懂。沒關係,key看起來還算看得懂,先記下這個位置。

往前到 DataManager.saveGameWithoutRescue

json 看起來是從這邊建立的,一樣到 Console 看一下

好像看得懂

回 DataManager.saveGameWithoutRescue 看一下它的源頭: this.makeSaveContents
接著回 Console 輸入 this.makeSaveContents 後,用滑鼠點一下如圖中紅框內有字的地方

跳至該函式位置: DataManager.makeSaveContents


因為整個 DataManager.makeSaveContents 看起來沒有修改現有資料,回到 Console 放心執行看看其回傳值是什麼

看起來就是一堆遊戲變數組起來的東西

按下藍色"慢播放"按鈕,等等看還有沒有執行其他次 localStorage.setItem

發現又停在 StorageManager.saveToWebStorage 中,但 savefileId 不同

往前到 DataManager.saveGameWithoutRescue ,發現有一個 globalInfo 變數,並且是從 this.loadGlobalInfo() 來的


從名稱猜測 this.loadGlobalInfo 應該不會改變資料,執行 this.loadGlobalInfo() 看看結果

看到疑似遊玩時間之類的東西

猜測: this.loadGlobalInfo 負責存檔選單列表; this.makeSaveContents 負責存檔內容

嘗試猜測:在沒有存檔的地方(例如存檔2),如同 DataManager.saveGameWithoutRescue 中做的事情一樣,把 json 存到一個位置,並在相應的位置寫入 this.makeSavefileInfo 的資訊


在最後 return true 的那列,左邊數字點一下設定中斷點後按一下藍色慢播放


接著看看 this.loadGlobalInfo() 會回傳什麼


為什麼沒有剛剛放進去的2呢? 回到前面我們的 localStorage.setItem 是這樣定義的

在執行原本的函式前中斷,所以當我們執行完 this.saveGlobalInfo 後又被原本的東西蓋掉了

慶幸的是存檔內容沒有被蓋掉,於是我們再執行一次設定 globalInfo 的流程,並且查看資料是否正常


看起來都沒問題了,按下右邊一條橫橫的東西取消中斷(避免中斷又跳回來),再按下藍色慢播放,回到遊戲中看看存檔清單變成什麼
(如果發現一直在存檔,莫驚莫荒莫害怕,再按一次你存檔時按的最後一顆鍵就可以了)


哇嗚,存檔變成2個了

於是我們知道:
當下存檔內容 = JsonEx.stringify(this.makeSaveContents());
存檔清單資訊 = this.loadGlobalInfo();
存檔清單資訊[n]代表第n的存檔的資訊

看來有機會隨時存檔了呢!



寫完一個例子就累了
起床後繼續寫第二個例子



例子:找出效能瓶頸

在RMMV的遊戲中,預設第1次按下F2,會在左上角顯示FPS,再按一次則會變成顯示每幀延遲,再按一次則關閉,若再按一次則回到顯示FPS,如此循環。

正常遊戲情況下,延遲應該是低的,如下圖

可以見到延遲看起來算是平穩,並且不高(以FPS60為基準,延遲應是16ms或以下)

但如果遊戲作者做了奇怪的事情,或是某個插件並不如你所想的可以那樣使用,導致延遲有不明高峰,如下圖

該怎麼除錯呢?就算中斷執行,也不知道它執行多久

這時就用到了 Performance 那個 tab ,它可能在 ">>" 裡如下圖


(以下請一氣呵成,即整個看完再動作,除非你想讓紀錄變很肥)
DevTools切換到 Performance 後把圖中紅框處的按鈕按下去,開始錄製執行紀錄


接著,回到遊戲中,重複剛剛會造成高延遲的動作

確認到高延遲後回到 Performance 按下 Stop ,應該會出現類似下圖

可以看到有多種顏色的長方形的圖
(以上請一氣呵成,即整個看完再動作,除非你想讓紀錄變很肥)

如果延遲高峰的那幀只有1個的話,會長得像下圖

就它特別寬

用滑鼠滾輪在他身上往上滾,集中視線

每個長方形是 1 個函式,執行時間(開始到結束)接近的相同遞迴層的相同函式會合併成 1 個長方形,如下圖


接著開始找到底是底下哪一格害的,可能的情況有但不限於:
1. 該函式本身執行過久。在RMMV中常見於 CanvasRenderingContext2D.prototype.drawImage ,一個用於將既有圖片畫在其他地方的函式
2. 某個函式多次被呼叫,且回傳內容相同或不會改變其他變數與成員
3. 就是要算這麼久,認了,放棄這個功能吧

而在上面的例子中,可以看到一些 Game_BattlerBase.traits 比它下方的方塊的長度長了不少

(此為合成兩次選取方塊的圖片)
再近一步確認 Self time 也是與 Total time 接近,故可以由此開始調查起因

選好該方塊後點選下方 "Function" 的右邊有底線的那個東西,如圖中紅框處所示

DevTools 會跳至 Source 該函式的位置


然後發現他只有呼叫一個函式,接著 filter ,從格式上看應該是 Array.prototype.filter ,八成是東西太多, filter 太慢。但我們不確定,因此回到 Performance 點選下一層的函式

然後我們又回來了,看來它是原本的型態或是沒有被繼承後蓋掉


再往下看到

從名稱來看,難道是角色裝備嗎? 好像有了個方向,在 traits 設個中斷點觀察看看吧

設定完中斷點,再操作一次, DevTools 跑了出來

使用單步執行慢慢看這些函式到底會回傳什麼


看來會是個陣列


那到底發生什麼問題呢?


陣列長度7,應該不會慢,下一個


陣列長度65550,太長了,就決定是你了

所以問題就是跑了好幾次陣列長度 65550 的 Array.prototype.filter
結案

那麼怎麼辦呢? 當然是改爆它!
裝備總不會一直換吧? 既然如此就把結果記起來,換過再重算
這個部分讀者自己去改,想看結果的可以前往下方網址欣賞
https://home.gamer.com.tw/creationDetail.php?sn=5119357



你應該從上面的兩個例子中的流程注意什麼東西:
1. 一個如何攔截某個函式的方法。
2. Call Stack 很好用。不要一開始就整個程式碼的每個地方慢慢修改後,執行看看該改哪裡,很累。
3. 中斷之後執行的東西相當於在中斷點插入那段程式碼。
4. 除了在程式中執行 "debugger;" 之外,也可以在 DevTools 的 Source 中按那排數字(行號?列號?我都稱它line number)。
5. 中斷時的 scope 是看你選的 call stack 中的位置,預設 scope 是中斷點的 scope 。
6. 藍色慢播放按鈕右邊那一排的功能,自己去試試看。
7. 選取 Performance 錄完的結果中的方塊,可以直接飛到函式位置(但native code則無法)。
8. 看完文章有問題要提出,直接給出更正確的寫法或讓文章作者思考該怎麼修改。當然如果作者不覺得那邊有問題的話,就一定不會改,所以還需要說服作者。
9. 作者當每個人都有裝python,而且會用command line (sh / tcsh / bash / cmd.exe / powerhell.exe)執行python,然後還說是給非資工本科看的,有夠矛盾
10. 示範圖片中的網域並不存在,請不要浪費時間



寫完第二個例子又累了




可能有些人會有疑問:為什麼不在 Source 中按 esc 使用 Console ?
因為畫面太小
是因為搜尋時按 ESC 會取消搜尋,反而不能叫出 Console ,很煩;而叫出來的 Console 在按一下 ESC 之後又會自己縮回去,要是在搜尋,我就把搜尋狀態清掉了,要是我有捲動到其他位置,下次搜尋會自己亂跳,很煩。

好了,有人要找我去工作嗎?沒有的話我要去學gdb怎麼用了

引用網址:https://home.gamer.com.tw/TrackBack.php?sn=5132444
Some rights reserved. 姓名標示-非商業性 2.5 台灣

相關創作

同標籤作品搜尋:chrome js debugger|RMMV

留言共 0 篇留言

我要留言提醒:您尚未登入,請先登入再留言

6喜歡★agold404 可決定是否刪除您的留言,請勿發表違反站規文字。

前一篇:【RMMV】(影片dem... 後一篇:【筆記】用Firebas...

追蹤私訊切換新版閱覽

作品資料夾

rogerjian喜歡像素風格的人
哈囉看更多我要大聲說10小時前


face基於日前微軟官方表示 Internet Explorer 不再支援新的網路標準,可能無法使用新的應用程式來呈現網站內容,在瀏覽器支援度及網站安全性的雙重考量下,為了讓巴友們有更好的使用體驗,巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現和功能。
屆時建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業系統版本才可使用)

face我們了解您不想看到廣告的心情⋯ 若您願意支持巴哈姆特永續經營,請將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學】