游戲自動化測試-局內戰斗


一場競技游戲,我們在這里稍微划分一下,將其分為局外界面和局內戰斗。舉個例子,比如LOL,在大廳、組隊、選英雄界面這些都是局外界面。當我們等待讀完長長的進度條,進入到游戲,聽到"咚咚咚咚咚"的聲音后英雄出現到我們眼前,這個時候,你就已經到了局內戰斗的部分里了,也就是我們今天要討論的內容。

1. 概述

本文主要對局內戰斗的自動化測試進行講解。包含如何實現戰斗自動化的流程和腳本運行本身框架進行介紹,讀者應具備一些python的編程經驗和UI自動化的開發經驗。

對於游戲的UI自動化這里采用poco的unitysdk形式進行實現。稍微介紹一下poco。這是一個類似appium的客戶端UI自動化框架,利用界面元素來進行UI自動化操作。

官方文檔:

https://poco-chinese.readthedocs.io/zh_CN/latest/source/doc/integration.html

元素來源於UITree,UITree源於接入unitysdk的游戲客戶端。通過與外部掛載agent進行socket通信,從而將使用unity引擎游戲的UITree返回給外部供自動化相關。

UITree里面包含對應Tree中元素的所有屬性信息,其中包含元素的position屬性(位置信息),這也是我們得以鎖定對應元素進行操作的核心依據。

通過pocosdk獲取游戲客戶端UI層信息,對UI信息中的具體內容做對應處理。

最后反饋對應數據給AI,由AI進行對應決策,將決策信息返回給本地后執行對應的UI自動化操作代碼,從而完成決策,通過大量決策的實施,完成整局的實時操作。

再引入多輪重開檢測機制,進而完成局內戰斗的自動化對戰。

一些簡單的演示:

在unity項目中,需要在unity中安裝Poco的SDK

Poco調用方法

import time
from poco.drivers.unity3d import  UnityPoco

poco = UnityPoco()

poco('btn_start').click()
time.sleep(1.5)

shell = poco('shell').focus('center')
for star in poco('star'):
    star.drag_to(shell)
time.sleep(1)

assert poco('scoreVal').get_text() == "100","score correct."
poco('btn_back',type = 'Button').click()

## Poco調用舉例

poco = UnityPoco()    #1、通過接口調用unity中的pocosdk,SDK對整個ui樹進行遍歷,將dump后的json信息傳出以供調用。

poco('btn_start').click()    #2、在得到的UI樹中找到‘btn_start’的元素位置信息,通過adb進行點擊操作。

2. 具體流程

流程描述:

通過每次對當前UI數據的檢查,判斷其在局內還是局外。局外會進入到局內對局,處於局內則會檢測當前局內狀態,將當前的局內UI信息做整合,提取指定信息發送給AI決策服務,再根據AI的返回來進行對應的UI自動化代碼操作。

3. 實現過程

3.1 UI層數據獲取

  • 使用poco.freeze,利用里面的agent dump把當前的UI樹保存下來(因為要用到root節點,root節點下的內容無法用children獲取)。

  • 其他的數據使用poco.freeze調用正常的poco方法進行提取即可。

3.2 指定數據提取

  • 確認好AI一共需要我們提供哪些數據,這些數據能不能從游戲的UI信息中讀取出來。確認好后,我們就可以開始進行對應項的提取了。

  • 要注意提取的UI元素是不是在特定UI中才會有,他能不能被容易的獲取,也就是對提取指定信息的難易度和是否需要條件構建進行判斷。

  • 以自走棋游戲舉例,對於棋子的提取會復雜一些,因為沒有對應棋盤上每個格子的坐標,只有放在根目錄下的美術資源。這部分的數據一個是需要存起來自己寫方法提取,一個是要根據里面屬性包含的信息判斷其在場上還是在手牌。

目前這一部分的判斷經歷過三個階段:

  • 靠pos屬性判斷。

  • 靠對pocosdk接入unity的c++源碼進行修改添加的rawpos屬性進行判斷。

  • 靠這兩者綜合判斷,保證不會有判斷錯的情況出現 。

3.3 數據接口請求過程

請求數據示范樣本:

{"round_drawscount": 0,
"round_actionscount":2,
"round":17,"level":6,
"experience":2,
"money":33,
"blood":73,}

也就是包含一些基本玩家信息的json數據。

AI接口返回數據:

{'eventId': ACTION_UPGRADE}{'eventId': ACTION_DRAW}{'eventId': ACTION_PICK,'params': {'draw_cardIndex': 0}}{'eventId': ACTION_DROP,'params': {"hand_cardIndex":0}}{'eventId': ACTION_SWAP, 'params':{"hand_cardIndex":0,"play_listIndex":1}}{'eventId': ACTION_SWAP, 'params':{"hand_cardIndex":2,"play_listIndex":1}}{'eventId': ACTION_SWAP, 'params':{"hand_cardIndex":0,"play_listIndex":2}}

大致是一些游戲AI的決策內容,通過不同的事件ID告知你應該做什么樣的事情。這里是以目前的自走棋游戲為例進行展示。

具體返回內容就根據實際的業務需求去制定了。

接口須知:

  • 所有發送的參數需要向開發人員確認。

  • 會存在后期的變動維護,例如傳參要求和增刪參數。

  • 要注意發送的數據是經過處理為標准格式。

3.4 返回決策實施

決策實施這里以自走棋類游戲的操作為例:

  • 刷棋盤:保證商店處於打開狀態后刷新。

  • 買棋子:保證商店處於打開后購買對應索引位置棋子。

  • 升人口:隨時都會有的按鈕,直接用點就可以。

  • 上下交換棋子:根據對應index有無棋子來判斷對應上下棋子的決定。

  • 賣棋子:點擊對應index位置,再點擊sell按鈕。

#對應返回eventID執行不同操作

        eventid = self.reqs_data["eventId"]
        if eventid == 0:
            self.round_over([])
        elif eventid == 1:
            self.read_chessbook()
        elif eventid == 2:
            self.refresh()
        elif eventid == 3:
            self.buy_chess(self.reqs_data["params"]["draw_cardIndex"])
        elif eventid == 4:
            self.sell_chess(self.reqs_data["params"]["hand_cardIndex"])
        elif eventid == 5:
            self.up_down_change(self.reqs_data["params"])

3.5 腳本運行框架介紹

其實這個框架歸類的比較簡單,就四個大塊:

(1)runner:包含觸發檢測的觸發器,用於傳入指定包名,選擇對應服務器等操作進行持續集成相關操作;包含動作調用,用來執行對應返回值的操作;包含日志記錄,記錄過程中的游戲運行日志和adb日志等;包含屏幕錄制,記錄游戲過程中的運行視頻,提交視頻數據給UI檢測接口判斷有無UI異常出現(多為花屏白屏和其他明顯UI異常情況)。

(2)datas:游戲運行數據的記錄文件,包含日志的存放和游戲對應戰斗結果的存放(例如當前回合,本局戰斗的排名勝負)。

(3)common_cls:一些公共模塊,包含與AI的通信,數據的發送和返回;包含UI的通用操作,如自動安裝對應版本游戲客戶端后啟動游戲,過關新手引導,生成新賬號等操作;包含通過對AI決策調用對應UI操作方法的函數,也就是動作函數。

(4)requirements:整個工程是通過python完成的,這個就是幫你一鍵安裝所有所需庫的文件,其中涉及到了一些對poco框架的二次開發,需要本地修改后使用。

大致的runner流程:

確保你的設備已與電腦開通調試連接,再啟動poco與adb的本地通信,對當前UI進行判斷是否在局內,局內走提取指定信息,發送給ai進行決策,再執行AI的決策。通過每次檢測有無戰斗結束進行重開實現多輪流程,runner的流程圖與具體實現流程圖基本一致。

3.6 相關優化問題

關於黑名單: 比較大型的游戲,局內戰斗的元素和UI的復雜程度也是很高的,保存一次UI樹的信息也有2M以上的數據,而獲取時間會隨着UI復雜度的上升而上升。從最終效果來看,戰斗從前期到后期,獲取時間會由1秒到4秒逐漸上升,這樣的速度是不能滿足實現業務需求的,因此建議從poco的unitySDK上添加黑名單機制,以提高獲取UITree的效率。

建議利用freeze函數: 多次提取會重復的獲取UI元素浪費時間,目前已經解決這個問題可以只獲取一次就提取出所有參數。目前較大型游戲因局內戰斗UI結構復雜,獲取一次UI信息在一秒到三秒內不等。

UI層操作時間問題: 需要避的坑有兩個 (1)minicap截圖:這個要關掉,實際會采用錄屏行為,不需要截圖。截圖服務啟動會消耗時間且截圖本身也消耗性能 (2)adb本地socket通信建立:確保一次建立,這是poco click swipe等方式進行指令的必要條件,需要提前自己啟動起來,讓他們時刻保持通信,這樣會大大降低單次操作時間。現在單次實行操作的時間已經可以控制在最快0.2秒最慢0.8秒。

獲取UITree時間問題: 通過觀察發現,UITree的獲取時間跟界面的復雜程度是正相關的。一個比較簡單的局外界面可以在0.5秒內就獲取完畢,而較為復雜的局內戰斗就要約2秒多的時間。對於實際操作而言,這樣的過程都太長了。目前探索出的解決方法有兩個:(1)利用黑名單,(2)只dump一次,更新后的數據不做重新獲取,而是采用利用預期結果的數據本地維護。

有關poco的c++層sdk修改文章請嘗試自行百度..

3.7 注意事項

(1)獲取一次UI信息的時間會隨着UI復雜度的提升而提升,局內較為復雜,到后期尤甚。通常在兩秒多三秒左右,實際使用要根據項目做具體優化。

(2)處理獲取出的原始數據到提取對應數據會用到0.2秒左右時間。

(3)操作間要保證存在足夠時間間隔,一個是利於等待操作后UI確實刷新,一個是保證UI切換展示出內容需要時間 。

(4)雙擊功能需要觀察能不能實現現有客戶端的操作(adb和poco double click都不行),有的客戶端要求雙擊的兩次點擊時間間隔太短,不一定能通過自動化實現。

4. 結論

本文主要描述了通過局內調用AI的方式實現自動打游戲。利用這個框架可以進行局內的多輪流程檢測(甚至上分),從而幫助測試同學免去重復去測局內戰斗的皮肉之苦,同時可以記錄過程視頻,交由AI或其他api判斷有無花屏白屏的情況,adb報錯,unity報錯信息都可以進行實時收集,接下來我們也會對局外的AI探索性遍歷展開研究,請大家持續關注我們,並指出我們的不足。


PS:

我們是行者AI,我們在“AI+游戲”中不斷前行。

如果你也對游戲感興趣,對AI充滿好奇,那就快來加入我們(hr@xingzhe.ai)。


免責聲明!

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



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