跳轉到內容

Godot 遊戲引擎最佳化指南/最佳化

50% developed
來自華夏公益教科書

遊戲執行在擁有有限資源和 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()

[編輯 | 編輯原始碼]

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_parent().get_parent(),而不是 get_node('../../')

[編輯 | 編輯原始碼]

如果嘗試獲取節點的父節點(或上級節點),請不要使用 get_node('../../'),因為它比直接使用 get_parent().get_parent() 慢 37%。當然,看起來更亂,但是如果你需要更快的程式碼,這就是你想要的方式。

使用最新的穩定版本

[編輯 | 編輯原始碼]

較新的 Godot 穩定 版本具有更多最佳化,並且往往比舊版本效能更高。



Godot 遊戲引擎指南

入門 [編輯]
安裝
什麼是節點?
程式設計
資源和匯入
訊號和方法
你的第一個遊戲
使它工作
除錯
輸入
物理
儲存和載入
多人遊戲
使它看起來更好
UI 皮膚
動畫
高階幫助
伺服器(單例)
平臺特定
最佳化
加密
匯出
外掛
雜項
有用連結
作者和貢獻者
列印版本


<-- 上一頁 返回頂部 下一頁 -->

華夏公益教科書