Screeps 淺談代碼優(yōu)化

screeps 系列教程

代碼優(yōu)化可以說是每一個在 shard3 生活的玩家都要經歷的养盗。這篇文章我們就來分析一下 cpu 消耗的來源和如何進行優(yōu)化。

CPU 消耗的來源

在游戲中届腐,cpu 消耗主要有三個大頭铁坎,分別是:搜索消耗、尋路消耗和固定消耗犁苏。在介紹之前硬萍,我們先來看一下如何在 api 文檔中查看各個接口的性能消耗:以 StructureLab 的文檔為例,下圖中圈出的 藍色方框條目 都是可執(zhí)行的方法(空心的代表繼承自其它原型)围详,這些方法都是有消耗的朴乖。

該原型上的可執(zhí)行方法

如下,我們可以在 api 介紹的右上角找到他的性能消耗短曾,該方法的消耗越大,它的顏色就越重赐劣、橫杠就越多嫉拐。而擁有 A 標志的方法則代表該方法的消耗是固定 0.2 CPU + 代碼執(zhí)行消耗。也就是說魁兼,只要該方法執(zhí)行成功(返回 OK)婉徘,那么它一定會吃掉 0.2 CPU漠嵌。

cpu消耗量
固定 cpu 消耗

所以,在調用某個方法前盖呼,應先檢查它的消耗儒鹿,并由此決定它在你代碼中的調用權重。接下來几晤,我們分別講一下剛才提到的三大消耗:

搜索消耗

搜索消耗的典型例子就是 Room.findRoomPosition.findInRange方法约炎。這兩個方法需要先遍歷房間中的所有對象,篩選出所有符合 FIND_* 常量的同類型對象蟹瘾,然后再遍歷執(zhí)行 filter 來找到最終目標圾浅。由于教程的原因,在新手的代碼中經常會出現(xiàn) Room.find 從而導致消耗掉了大量 cpu憾朴。

內置的搜索緩存

為了節(jié)省消耗狸捕,這種搜索類方法都擁有一個內置緩存,游戲會將對應的 FIND_* 搜索結果緩存下來众雷,也就是說灸拍,在同一 tick 中,如果你執(zhí)行了兩遍 Room.find(FIND_CREEP) 方法砾省,第二次搜索就會自動應用緩存從而節(jié)省消耗鸡岗。

內置緩存

注意,這個緩存會在 tick 開始時清除纯蛾。并且纤房,這個緩存只會保存所有符合 FIND_* 常量的搜索結果,而你使用 filter 帶來的 cpu 消耗是無法避免的翻诉,哪怕你兩次搜索所用的 filter 完全相同炮姨。

在此之外還有一些方法也會產生搜索消耗,例如.look系列方法碰煌,這里不再贅述舒岸,在 api 文檔中自行搜索即可。

除了房間對象搜索之外芦圾,Game.market.getAllOrders 也需要注意蛾派,由于需要檢查市場上的所有訂單,這個方法的 cpu 消耗是巨大的个少,不過注意它的介紹:“該方法支持resourceType內置索引”洪乍,也就是說,在 filter 里攜帶 resourceType 屬性可以大大減少其消耗夜焦。

消耗直接拉滿

尋路消耗

相對于搜索消耗壳澳,尋路帶來的消耗要大的多,幾個比較常見的會產生尋路消耗的方法如下:

  • Creep.moveTo()
  • Room.findPath()
  • RoomPosition.findClosestByPath()
  • RoomPosition.findPathTo()
  • Game.map.findRoute()
  • PathFinder.search()

大體瞅一眼就可以發(fā)現(xiàn)茫经,這幾個方法基本都是非常常用的巷波,哦真糟糕萎津。像是 Creep.moveTo 方法我又不得不用。是的抹镊,如何避免尋路消耗是 Screeps 中的一大重要研究課題★鼻現(xiàn)在常見的方法是將常用的路徑緩存來減少尋路次數(shù)。而對于新手來說垮耳,這里還有種更簡單的方法來節(jié)省尋路消耗:

內置的尋路緩存

Creep.moveTo()同樣包含一個內置緩存颈渊,如下:

你可以通過簡單的提升該值的大小來節(jié)省 cpu 消耗,但是要注意:這個值越高氨菇,你的 creep 反應也就越遲鈍儡炼。如果它之前緩存的路徑上有個無法越過的障礙物,它就會一直卡在那里直到緩存時間結束查蓉。

這里要重點提一下RoomPosition.findClosestByPath方法:

該方法會造成大量的遍歷運算乌询,所以在代碼編寫中你應該少用這種方法,如果要用的話豌研,也請根據(jù)情況指定默認算法或者對其結果進行緩存來減少搜索次數(shù)妹田。

固定消耗

這種消耗幾乎存在游戲世界中的各個角落,它有一個特點:會對游戲世界產生影響鹃共,例如Creep.withdrawCreep.transfer鬼佣,他們會將資源移動到其他位置(產生了影響),所以說這兩種方法就會包含固定消耗霜浴,除此之外還有諸如Creep.harvest晶衷、Tower.attack等等很多很多。

這個消耗是游戲制訂的規(guī)則阴孟,所以說幾乎無法避免晌纫。這個消耗會隨著你的 creep 和建筑數(shù)量的增多而增多,并構成了你每 tick 的基礎消耗永丝。雖然無法避免锹漱,但是我們可以通過提高其每次執(zhí)行的效率來節(jié)省 cpu。如何節(jié)省我們下文再提慕嚷。

代碼執(zhí)行成本

除了上面三個消耗大頭外哥牍,執(zhí)行代碼也會產生一定的消耗,這個幾乎是無法避免的喝检,想要節(jié)省此類消耗需要你對 js 有更深層的了解嗅辣,并且由于這種消耗比較零碎,所以并不推薦刻意的對其進行優(yōu)化挠说,上面三種消耗哪怕你能節(jié)省任何一點澡谭,所帶來的收益都比優(yōu)化代碼執(zhí)行成本要大的多。

這里有一點需要注意的纺涤,游戲的 Memory 內存對象需要每 tick 調用JSON.stringifyJSON.parse進行解析和儲存译暂,內存越大消耗也就越大,所以請節(jié)約內存的使用撩炊。

緩存的種類

在介紹如何優(yōu)化之前外永,我們先來看一下游戲中的緩存種類,已經有很多類似的文章了拧咳,所以我們這里只簡單提一下:

  • 持久化存儲:游戲的Memory對象伯顶,只有這個地方能實現(xiàn)真正可靠的長時間存儲。
  • 半持久存儲:js 的 Global對象骆膝,對象原型都屬于半持久存儲祭衩,這種存儲會在游戲全局重置時被清除,一般存放允許丟失的數(shù)據(jù)阅签。
  • 非持久存儲:直接定義在游戲對象(非原型)上的屬性都屬于非持久存儲掐暮,例如Game.rooms.W1N1.myCustomProp = 123,這種存儲只有本 tick 能訪問到政钟,用來存放 tick 內協(xié)同作業(yè)需要的數(shù)據(jù)路克。

更詳細的分析見下文:

如何優(yōu)化 CPU 消耗

首先,請把 過早的優(yōu)化是萬惡的根源! 這句話重讀三遍并牢牢記住养交。在你的 cpu 消耗大于三分之二之前精算,不要刻意優(yōu)化;在你的房間升到 8 級之前碎连,不要考慮修改房間運營邏輯灰羽;在你的整體框架還可以應對現(xiàn)有需求時,不要考慮對代碼進行重構鱼辙。

在重構時因為突然出現(xiàn)的新需求導致需要重構現(xiàn)有的重構工作廉嚼、因為對游戲了解不夠全面導致重構后的框架變成了更大的屎山,這種糟糕的體驗會讓你不想再打開這個游戲座每,因此棄坑的玩家也大有人在前鹅。所以,請把上面那段文字重讀一遍后再繼續(xù)閱讀下面的內容峭梳。

ok舰绘,我們現(xiàn)在來講一下如何節(jié)省這些消耗。

搜索消耗

首先是搜索消耗葱椭,優(yōu)化搜索消耗主要靠 緩存結果和減少重復搜索 來完成捂寿。例如自己房間內的建筑,你可以在搜索之后將其 id 緩存在Memory或者Global下孵运,之后通過指定的方法或者屬性直接調用秦陋。如果你了解過原型拓展的話,你也可以新建類似 Room.sources 之類的屬性來更加方便的管理這些緩存治笨。這里 提到了如何創(chuàng)建這些緩存驳概。

而像同一 tick 內同個房間的多個 tower 都執(zhí)行了敵人搜索赤嚼,這種就屬于完全無意義的重復搜索,你可以通過在房間下掛載非持久緩存的形式解決這個問題:

// tower 將會執(zhí)行的方法
function towerWork(tower) {
    // 如果之前沒有 tower 搜索過
    if (!tower.room._hasRunTowerFind) {
        const enemy = tower.room.find(FIND_HOSTILE_CREEPS)
        // 處理邏輯 ...
        // 后面的 tower 不再搜索
        tower.room._hasRunTowerFind = true
    }
}

你也可以把搜索結果掛在 Room 對象下來讓房間內的所有 tower 都可以獲取到目標顺又。

尋路消耗

減少尋路消耗的主要方法是 重用尋路結果來減少尋路次數(shù)更卒。例如從 Spawn 到 Source 的路線是固定的;從 Storage 到 Controller 的路線也是固定的稚照。那么就可以將這類路徑存儲到非持久緩存或者 Memory 中來讓 creep 可以一直按照固定路線移動而無需尋路蹂空。你可以使用Creep.moveByPath()或者自行封裝Creep.move()來實現(xiàn) creep 按照指定路線移動的邏輯。

這里提一個醒果录,如果你要把尋路結果保存在 Memory 中的話上枕,請先將搜索結果序列化成字符串的形式進行保存,這樣可以節(jié)省內存空間弱恒。而Room.serializePath方法不支持壓縮PathFinder.search()的搜索結果辨萍,所以如果你在用PathFinder的話就可能需要手寫壓縮方法,或者你也可以直接將結果保存到非持久緩存中返弹。

固定消耗

由于固定消耗無法避免分瘦,所以我們要盡可能的提升每次執(zhí)行的效果。例如 Creep.harvest方法琉苇,雖然一個 creep 只有 5 個WORK就可以采干一個 Source嘲玫。但是我們依舊可以通過繼續(xù)提升WORK的數(shù)量來減少Creep.harvest()的執(zhí)行次數(shù),從而節(jié)省 cpu 消耗并扇。

同理去团,我們也可以通過增大CARRY的數(shù)量來提升每次搬運的資源數(shù)量。提高HEAL的數(shù)量來提升每次治療的效果等等穷蛹。這也是為什么官方更推薦用增加身體部件而不是 creep 數(shù)量的形式來提升效率土陪。

提升身體部件數(shù)量而不是 creep 數(shù)量

不止 creep,建筑也可以用這種方式來提升效率肴熏,例如Link等到存儲滿了之后再發(fā)送鬼雀,Terminal一次交易更多的數(shù)量。在日常開發(fā)時就要考慮到這個問題蛙吏。

好的開發(fā)習慣

這個涉及的范圍就比較大了源哩,大家了解一下就好,對于節(jié)省消耗來說:能重用的地方就不要再次新建變量鸦做、盡可能的少出現(xiàn)循環(huán)嵌套励烦。剩下的就不說太多了,畢竟大家是來玩游戲不是來上班的泼诱。如果確實有興趣的話坛掠,鏈接就在下面:

寫在最后

OK!本文簡單介紹了一下 Screeps 中幾個 cpu 消耗大頭以及如何優(yōu)化它們,并沒有出現(xiàn)多少代碼屉栓,畢竟每個人的架構不同舷蒲,強行宣傳某一種優(yōu)化方法也并不一定適用于所有玩家,還是那句話:過早重構友多、盲目重構只會讓你的游戲體驗更加糟糕阿纤。

了解更多 Screeps 的中文教程?歡迎訪問 Screeps - 中文系列教程夷陋!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市胰锌,隨后出現(xiàn)的幾起案子骗绕,更是在濱河造成了極大的恐慌,老刑警劉巖资昧,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酬土,死亡現(xiàn)場離奇詭異,居然都是意外死亡格带,警方通過查閱死者的電腦和手機撤缴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叽唱,“玉大人屈呕,你說我怎么就攤上這事」淄ぃ” “怎么了虎眨?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長镶摘。 經常有香客問我嗽桩,道長,這世上最難降的妖魔是什么凄敢? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任碌冶,我火速辦了婚禮,結果婚禮上涝缝,老公的妹妹穿的比我還像新娘扑庞。我一直安慰自己,他們只是感情好拒逮,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布嫩挤。 她就那樣靜靜地躺著,像睡著了一般消恍。 火紅的嫁衣襯著肌膚如雪岂昭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音约啊,去河邊找鬼邑遏。 笑死,一個胖子當著我的面吹牛恰矩,可吹牛的內容都是我干的记盒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼外傅,長吁一口氣:“原來是場噩夢啊……” “哼纪吮!你這毒婦竟也來了?” 一聲冷哼從身側響起萎胰,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤碾盟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后技竟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冰肴,經...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年榔组,在試婚紗的時候發(fā)現(xiàn)自己被綠了熙尉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡搓扯,死狀恐怖检痰,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情锨推,我是刑警寧澤攀细,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站爱态,受9級特大地震影響谭贪,放射性物質發(fā)生泄漏。R本人自食惡果不足惜锦担,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一俭识、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧洞渔,春花似錦套媚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至浆熔,卻和暖如春本辐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工慎皱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留老虫,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓茫多,卻偏偏與公主長得像祈匙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子天揖,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內容

  • 作為一個多人在線沙盒游戲夺欲,和別的玩家發(fā)生沖突可以說是不可避免的事情。本文簡單介紹一下在 Screeps 中的防御方...
    HoPGoldy閱讀 5,600評論 0 7
  • 簡介 作為新手玩家在游戲進程中遇到的第一個”BOSS“今膊,很多人會對如何拓展自己的疆域感到無從下手些阅,那么本文就簡單介...
    HoPGoldy閱讀 10,826評論 4 12
  • 為了不讓自己下線時出現(xiàn) creep 都涼了的情況,你的代碼里或多或少都有一個用于控制他們數(shù)量的模塊万细。在教程中,官方...
    HoPGoldy閱讀 8,667評論 9 28
  • 簡介 作為一款代碼主導的游戲纸泄,有代碼的地方就有設計模式赖钞。本文就作為一篇拓展閱讀性質的文章,來簡單介紹一下 Scre...
    HoPGoldy閱讀 7,838評論 16 10
  • 簡介 在游戲的教程中聘裁,我們了解到可以通過游戲給定的基本 api 如Game.creeps Game.spawns等...
    HoPGoldy閱讀 8,447評論 26 24