Godot 遊戲引擎最佳化指南/最佳化
遊戲執行在擁有有限資源和 CPU 能力的機器上。手機或電腦在你的遊戲開始卡頓之前只能做這麼多。不過,有一些很好的方法可以解決這個問題。
嘗試減少執行的程式碼量。提高遊戲 FPS 的最佳方法是停止執行不需要執行的程式碼。例如,你不需要在每幀計算物件的碰撞,或者如果你根本不會與該物件發生碰撞,就更不需要了。對於經常更新的物件,嘗試將更新延遲到下一幀,並使用最近的更新嘗試。
當遊戲在每幀進行復雜的計算時,遊戲就會變得相當卡頓。嘗試在使用計算結果之前將其儲存在一個變數中。呼叫 get_node() 也會造成一點延遲,因此也嘗試將它們快取到一個變數中。
避免這種情況
var direction func _process(delta): var turret = $Turret direction = Vector2.UP.rotated(turret.rotation)
首選這種方式
var direction onready var turret = $Turret func _process(delta): direction = Vector2.UP.rotated(turret.rotation)
之後的程式碼示例比之前快 1%。聽起來可能不多,但將這種最佳化應用到所有程式碼中就會累積起來。具有許多 get_node() 或 get_parent()呼叫的較大函式比事先將它們快取到變數中會造成更大的延遲。
沒有必要進行不必要的數學運算。在迴圈之前快取保持不變的計算結果。
這很慢
var alloy_strength = 1.0 var alloy_thickness = 5.0 var alloy_layers = 15.0 for robot in army: robot.armour = pow(alloy_strength * alloy_thickness, alloy_layers) + robot.native_armour
這更快
var alloy_strength = 1.0 var alloy_thickness = 5.0 var alloy_layers = 15.0 # Here, we're calculating the base armour rating of robots outside of the loop var base_armour = pow(alloy_strength * alloy_thickness, alloy_layers) for robot in army: robot.armour = base_armour + robot.native_armour
只訪問一次或兩次的小陣列是可以忽略的。但是對於經常訪問的較大陣列,效能差異會累積起來。
# 0.115443 seconds for x in 1000: for y in 1000: var element = my_array[y][x] #... # 0.108107 seconds for x in 1000: for y in 1000: var element = my_array[y * 1000 + x] #... # 0.062938 seconds, about 45% faster than the first example for i in 1000000: var element = my_array[i] #...
透過使用 Godot 的原生迭代器而不是自己建立迭代器,你可以獲得更大的效能提升
# 0.048952 seconds, almost 60% faster than the first example for x in 1000: for element in my_array[x]: #... # 0.047986 seconds for element in my_array: #...
你可能並不總是能夠做到這一點,但值得記住。在使用陣列時需要注意的主要事項是
- 一維陣列比多維陣列更快
- 單迴圈比巢狀迴圈更快
- 使用迭代器訪問陣列元素(例如,
for element in array)比使用索引(例如,for i in array.size())更快。
每當你從陣列中移除或新增元素時,都需要重新索引後面的所有元素,這意味著從陣列開頭新增/移除元素比從陣列末尾新增/移除元素需要重新索引更多的元素。
在從陣列中移除或新增元素時,優先使用 pop_back() 和 push_back() 或 append()。避免使用 pop_front() 和 push_front()。
以下三個經驗法則可以幫助你決定應該使用陣列還是字典。
print() 函式很慢。在匯出遊戲時儘量不要使用它。相反,建立自己的日誌記錄函式並自動載入它。示例
extends Node onready var log = File.new() func _ready(): log.open("res://log.txt", File.WRITE) func _exit_tree(): log.close() func log_message(message, source): var current_time = OS.get_time() log.write_line("%s (%s:%s:%s): %s" % [source, current_time.hour, current_time.minute, current_time.second, message])
使用 Godot 4.x 中的 靜態型別 GDScript
[編輯 | 編輯原始碼]使用靜態型別帶來的收益非常高,介於 50% 到 150% 之間。簡而言之,原因是 Godot 在確定所有內容的型別以執行正確的邏輯時付出了更少的努力。
如果嘗試獲取節點的父節點(或上級節點),請不要使用 get_node('../../'),因為它比直接使用 get_parent().get_parent() 慢 37%。當然,看起來更亂,但是如果你需要更快的程式碼,這就是你想要的方式。
較新的 Godot 穩定 版本具有更多最佳化,並且往往比舊版本效能更高。
