2010-04-21 209 views
21
  1. 在每個遊戲循環迭代中調用main.lua腳本 - 它是好還是壞的設計?它如何影響性能(相對)?Lua,遊戲狀態和遊戲循環

  2. 維持遊戲狀態從a。 C++主機程序或b。來自Lua腳本或c。從兩個並同步他們?

(在話題上的問題:Lua and C++: separation of duties

(我投票給每一個答案,最好的回答將被接受。)

+2

「(我投票給每一個答案。最佳答案將被接受。)」的是「合法」?如果我回答「西紅柿」怎麼辦? – 2010-04-27 13:11:18

+5

我可以爲我想要的一切投票。這是我的用戶特權。如果你的回答如此紅潤多汁,我會投票(下)。 – 2010-04-27 15:51:21

+1

@topright我刪除了這一行,因爲它確實沒有增加任何問題,只是看起來很奇怪。 – Earlz 2010-04-27 16:16:34

回答

9

lua最好的事情是它有一個輕量級的虛擬機,並且在塊被預編譯運行後,它們在虛擬機中實際上是相當快的,但仍然沒有C++代碼那麼快,而且我也沒有認爲每個渲染框架調用lua將是一個好主意。

我會把遊戲狀態放在C++中,並在lua中添加可以達到並修改狀態的函數。基於事件的方法幾乎更好,在lua中應該完成事件註冊(最好只在遊戲開始時或在特定的遊戲事件中,但每分鐘不超過幾次),但實際事件應該由C++代碼。用戶輸入也是事件,並且它們通常不會發生在每一幀中(除了可能是MouseMove,但應該小心使用它)。你處理用戶輸入事件的方式(無論你是在lua中處理所有事情(比如按下哪個按鍵等),或者是否有例如鍵盤上每個按鍵的單獨事件(在極端情況下)都取決於你的遊戲,重新嘗試(基於回合的遊戲可能只有一個事件處理程序適用於所有事件,RTS應該有更多的事件,並且應該小心處理FPS(主要是因爲移動鼠標將在每一幀發生))。你擁有的各種事件越少,你必須在lua中編寫代碼(這會提高性能),但是如果你需要處理的「真實事件」實際上是由更多獨立的「編程級事件」引發的這可能實際上會降低性能,因爲lua代碼需要更復雜)

或者,如果性能真的很重要,那麼實際上可以提高lu一個虛擬機通過添加新的操作碼(我已經看到一些公司這樣做,但主要是爲了使編譯後的lua塊更難反編譯),這實際上不是一件難事。如果你有一些lua代碼需要做很多次的事情(比如事件註冊,事件運行或者改變遊戲狀態),你可能需要在lua虛擬機中實現它們,因此代替多個getglobalsetglobal操作碼他們只需要一個或兩個(例如,您可以創建一個帶有0-255和0-65535參數的SETSTATE操作碼,其中第一個參數描述要修改的狀態,第二個參數描述該狀態的新值。當然,這隻適用於最多255個事件,最多2^16個值的情況,但在某些情況下可能已足夠,而且這隻需要一個操作碼就意味着代碼運行得更快)。如果你打算混淆你的lua代碼(雖然對於瞭解lua內部工作的人來說並不多),這也會使反編譯更加困難。每幀運行幾個操作碼(大約30-40個頂點)不會嚴重影響您的性能。但是,如果你需要做複雜的事情,lua虛擬機中的30-40個操作碼不會讓你感覺太遠(根據表達式,一個簡單的if-then-else可能需要10-20個或更多的操作碼)。

25

我的基本規則Lua是 - 或任何腳本語言在遊戲中 -

  • 任何發生在每一幀:C++
  • 異步事件 - 用戶輸入 - LUA
  • 同步遊戲引擎的活動 - LUA

基本上,任何代碼多數民衆贊成稱爲在> 33-100Hz(取決於幀速率)的C++ 我嘗試調用腳本引擎< 10Hz。

基於任何一種實際度量?不是真的。但它確實在設計中劃分了一條分界線,C++和lua任務清晰地描述了 - 沒有前面的描述,每幀lua任務將會增長,直到他們每幀處理陷入困境 - 然後沒有明確的修剪指南。

+0

但用戶輸入可能發生在每一幀。 – 2010-04-21 22:00:55

+0

它可以。但通常情況下不會。 – 2010-04-22 05:07:50

+0

在你的方法中維護遊戲狀態的是什麼 - C++? – 2010-04-24 10:02:07

5

恕我直言lua腳本是針對特定行爲的,如果您每秒調用一次lua腳本60次,肯定會損害性能。

Lua腳本經常將行爲和遊戲引擎邏輯(GUI,Items,Dialogs,遊戲引擎事件等)中的特定事件分開。例如,Lua的一個很好的用法是觸發爆炸(粒子FX),如果遊戲角色走到某個地方,那麼在引擎中硬編碼該事件的輸出將是一個非常難看的選擇。儘管如此,讓引擎觸發正確的腳本將是一個更好的選擇,將引擎的特定行爲解耦。

我會建議,儘量保持你的遊戲狀態在一個部分,而不是升級保持狀態在兩個地方(Lua和引擎)同步的複雜度水平,添加線程到那個,你最終會有一個非常醜陋的混亂。把事情簡單化。 (在我的設計中,我主要使用C++保持遊戲狀態)

祝您的遊戲順利!

+0

謝謝,祝你好運! – 2010-04-25 20:13:01

+0

鏈接現已停止 – paulm 2016-08-15 11:28:39

+0

WebArchive擁有它:http://web.archive.org/web/20080415093214/http://www.devmaster.net/articles/lua/lua1.php – Quentin 2016-08-15 13:13:19

4

我不喜歡C++。但我喜歡比賽。

我的方法可能有點不典型:我盡我所能在Lua中,只有C++中的絕對最小值。遊戲循環,實體等都是在Lua中完成的。我甚至在Lua中完成了一個QuadTree實現。 C++處理圖形和文件系統的東西,以及與外部庫的接口。

這不是基於機器的決定,而是基於程序員的決定;我在Lua中比在C++中輸出代碼要快得多。所以我花了我的程序員週期的新功能,而不是節省電腦週期。我的目標機器(過去3年的任何筆記本電腦)都能夠很輕鬆地應對這一數量的Lua。

Lua的佔地面積非常小(如果您不知道,請看luaJIT)。我說過,如果我發現了一個瓶頸(我還沒有),我將分析這個遊戲,以便找到緩慢的部分,然後我將該部分翻譯成C++ ...只有當我可以用Lua找到解決方法。

+0

謝謝,非常有用的評論。到目前爲止你在Lua上寫過多少個項目? – 2010-04-25 16:30:19

+0

我的榮幸。我不是一個專業的遊戲程序員,我把它作爲一種愛好,而我沒有一個團隊。所以我的項目很小。我最引以爲傲的是盧阿遊戲引擎PÄSSION。我爲它做了幾個演示,但沒有什麼大的。 – kikito 2010-04-25 21:17:11

+0

嗨,我聽說過LOVE和PASSION引擎。 – 2010-04-26 22:07:44

2

大部分的性能都會通過Lua和C++之間的綁定而丟失。一個函數調用實際上需要被包裝,並重新包裝,並且通常像這樣幾次。純Lua或純C++代碼通常比混合代碼(用於小型操作)要快

話雖如此,我個人沒有看到任何性能命中運行Lua中每一幀

通常腳本編寫好高級。 Lua中已在著名的遊戲被用於機器人雷神之錘3),併爲用戶界面(魔獸的世界)。在高層使用Lua微線程非常方便:協程可以節省很多(與真正的線程相比)。例如一段時間只運行一次Lua代碼。

4

關於1的表現:如果main.lua不改變,與lua_loadfileloadfile一旦加載它,保存到返回函數的引用,然後調用它需要的時候。

6

我在我一直在努力的遊戲中第一次使用Lua。我的應用程序的C++端實際上持有指向每個遊戲狀態實例的指針。一些遊戲狀態在C++中實現,一些在Lua中實現(例如「遊戲玩法」狀態)。

更新和主應用程序循環存在於事物的C++端。我公開了允許Lua VM在運行時爲應用程序添加新遊戲狀態的函數。

我還沒有遇到任何緩慢的問題,即使在資源有限的硬件上運行(帶集成視頻的Atom處理器)。 Lua函數每幀調用。在我的應用程序中最昂貴的(按時間)操作是渲染

在Lua中完全創建新狀態的能力是我在該項目上做出的最佳決策之一,因爲它允許我在不重新編譯整個事件的情況下自由添加部分遊戲。

編輯:我正在使用Luabind,我讀過的表現一般比其他綁定框架,當然還有Lua C API要慢。

+0

非常有趣, 謝謝。 – 2010-04-27 16:41:40

5
  1. 你可能不希望在每一幀重複執行整個的Lua腳本,因爲任何足夠複雜的遊戲都會有自己的行爲的多個遊戲對象。換句話說,除非你有多個處理大型遊戲行爲的特定部分的小腳本,否則Lua的優勢將會消失。您可以使用lua_call函數在腳本中調用任何適當的lua例程,而不僅僅是整個文件。

  2. 這裏沒有理想的答案,但絕大多數遊戲狀態通常存儲在遊戲引擎(即C++)中。你只需向Lua透露Lua做出你已經分配給Lua的決策。

您需要考慮哪種語言適合哪種行爲。 Lua對於高級控制和決策非常有用,而C++對於面向性能的代碼非常有用。 Lua對於遊戲中需要調整而無需重新編譯的部分特別有用。例如,所有魔術常數和變量都可以進入Lua。不要試圖在不屬於它的地方使用Lua,即圖形或音頻渲染。

1

我想投入我的兩分錢,因爲我堅信這裏給出了一些不正確的建議。在上下文中,我在一個大型遊戲中使用Lua,這個遊戲既涉及密集的3D渲染,也涉及強化的遊戲邏輯模擬。我已經比我更喜歡Lua和性能...

請注意,我要專門討論LuaJIT,因爲您要使用LuaJIT。它是即插即用的,所以如果你可以嵌入Lua,你可以嵌入LuaJIT。如果不是額外的速度,那麼你會需要它,然後爲automagic外部函數接口模塊(require'ffi'),這將允許你直接從Lua調用你的本地代碼,而無需觸摸Lua C API(95 %+個案)。

  1. 這是完美的罰款在60Hz到調用Lua(我叫它在VR爲90Hz ..)。問題在於你必須小心地做到這一點。正如別人提到的那樣,只加載腳本至關重要。然後,您可以使用C API訪問您在該腳本中定義的函數,或者將腳本本身作爲函數運行。我推薦前者:對於一個相對簡單的遊戲,你可以通過定義像onUpdate(dt),onRender(),onKeyPressed(key),onMouseMoved(dx,dy)等函數來獲得。你可以在適當的時候調用它們來自C++中的主循環。或者,你實際上可以讓你的整個主循環在Lua中,而是調用你的C++代碼來實現性能關鍵的例程(我這樣做)。這對於LuaJIT FFI來說尤其容易。

  2. 這是一個非常難的問題。這將取決於你的需求。你能否輕鬆擊敗遊戲狀態?太好了,把它放在C++端,並通過LuaJIT FFI訪問。不確定遊戲中會出現什麼狀態/想要快速創建原型?將它保存在Lua中沒有任何問題。也就是說,直到你開始談論一個包含1000個對象的複雜遊戲,每個對象都包含非平凡的狀態。在這種情況下,混合是要走的路,但要弄清楚如何在C++和Lua之間分裂狀態,以及如何武斷地說兩者之間的狀態(特別是在perf-critical例程中)是一門藝術。讓我知道你是否想出了一個防彈技術:)與其他一切一樣,一般的經驗法則是:通過性能關鍵途徑的數據需要在本機端。例如,如果您的實體具有您更新每個框架的位置和速度,並且您擁有數千個所述實體,則需要在C++中執行此操作。但是,您可以使用Lua在這些實體的頂部分層存儲(庫存不需要每幀更新)。

現在,再多註釋一下,我想把它們都作爲一般性的FYI和其他一些答案作爲迴應。

  1. 基於事件的方法是,在一般情況下,任何遊戲的性能是至關重要的,但是那更加倍的書面lua系統。我說在@ 60hz上叫Lua是完美的。但它是而不是完美的罰款是在所有Lua中的每幀中運行大量遊戲對象的嚴格循環。你可能會在C++中用宇宙中的所有東西浪費地調用update()(儘管你不應該這樣做),但是在Lua中這樣做會很快耗盡這些寶貴的毫秒數。相反,正如其他人所提到的,你需要將Lua邏輯視爲「被動」 - 通常這意味着要處理一個事件。例如,不要檢查一個實體是否在Lua的另一個框架的範圍內(我的意思是,這對一個實體來說很好,但是一般來說,當你擴大遊戲規模時,你不需要這麼想)。相反,告訴你的C++引擎在兩個實體相距一定距離時通知你。這樣,Lua成爲遊戲邏輯的高級控制者,調度高級命令並響應高級事件,而不是執行微不足道的遊戲邏輯的低級數學研究。

  2. 請謹慎對待「混合代碼」緩慢的建議。 Lua C API輕巧且快速。包裝Lua使用的函數在最糟糕的情況下非常容易(如果你花一些時間來理解Lua與C接口如何與虛擬棧等接口,你會注意到它是專門爲最小化調用開銷而設計的),充其量只需要簡單的(不需要包裝)和100%的性能(感謝,LuaJIT FFI!)在大多數情況下,我發現Lua和C(通過LJ FFI)的仔細混合優於純粹的Lua。即使是矢量操作,我認爲LuaJIT手冊在某處提到了將代碼保存到Lua的例子,我發現在通過Lua-> C調用執行時會更快。最終,當涉及到對性能敏感的代碼片段時,不要相信任何人或任何其他任何東西,除了您自己的(謹慎的)性能測量。

  3. 大多數小型遊戲都可以通過遊戲狀態,核心循環,輸入處理等完全脫身,完全在Lua中完成,並在LuaJIT下運行。如果您正在構建一個小型遊戲,請考慮「根據需要轉移到C++」方法(懶惰編譯!)在Lua中編寫時,當您確定一個明確的瓶頸時(並已測量它以確保它是罪魁禍首) ,把那一點移到C++並且在路上。如果你正在編寫一個包含複雜邏輯,高級渲染等1000多個複雜對象的大型遊戲,那麼你將從更多的前期設計中受益。

祝您好運:)

+1

這些有用的見解和匹配我自己的學習曲線整合Lua與ObjC/C++引擎。我最後還採用了混合方法,它使用異步事件來交流引擎 - > Lua,並使用原生Lua->引擎調用性能敏感的操作。我甚至進一步研究並整合了RxLua以獲得完整的功能反應型遊戲邏輯(主要是因爲出於好奇,但實際上結果非常好)。需要多一點時間才能開始設置,但以後會很快彌補時間。 – w0utert 2017-05-09 07:59:45

+0

@ w0utert有趣的,沒有聽說過RxLua,感謝領先者 - 將檢查出來。 – 2017-05-10 01:33:52