使用Lua實做GUI系統的遊戲實例

在這篇文章裡,將以國外一個非常著名的休閒遊戲 Diner Dash 2 的實例,探討 Lua 的應用功能之一:GUI 系統。自前篇「Scripting系統概論與Lua簡介」對 Lua 的 What(是什麼)與 Why(為什麼使用)有了初步的認識之後,接下來的重點就是瞭解 Lua 的 How(如何使用)

在進入 How 的主題之前,先補充一下 Where(在何處使用)的概念。Lua 的能力十分強大,似乎無所不能,可以完成任何的任務與功能。但是就實際應用層面的考量來說,哪些功能與系統才是真正適合使用 Lua 的部分呢?又要如何區分 C++ 語言與 Lua 語言所能達成的任務呢?

以兩者擅長處理的事項來分類:

  • C++:低階核心、記憶體管理、資源管理、繪圖底層、數學運算、訊息發送。
  • Lua:使用者介面、人工智慧、資料管理、遊戲邏輯、動態行為。

複雜的運算與低階的底層核心,適合使用 C++;而因應設計需求,經常會變動的各種功能系統,例如使用者介面、人工智慧、遊戲邏輯等等就適合使用 Lua 來完成。另外,有安全性考量的功能層面,也應該使用 C++ 撰寫比較合適。

UI Widget Architecture在 Diner Dash 2 這款遊戲中的圖形化使用者介面,也就是常聽到的 GUI(或簡稱為 UI)系統,都是以 Lua 進行開發設計的。首先,需要瞭解一點基本的 GUI 開發概念。在一個完整的 GUI 系統中,需要許多的基本元件,例如常見的按鈕 (Button)、文字 (Text)、圖片 (Picture)、編輯輸入 (Edit)、進度條 (Progress Bar) 等等,這些物件稱為介面元件 (UI Widget);藉由這些基礎的元件,就能夠組合出遊戲中所使用的各種 UI。而將個別的元件集合起來形成一個群組的容器,常稱做框架 (Frame) 或圖層 (Layer)。最後,在架構頂層做為上述這些 UI Widget 的管理者,包含住一至多個 Frame,負責高階功能與介面的元件,則稱做視窗 (Window) 或是對話盒 (Dialog)。

一般常見的 GUI 系統實做方法,是在 C++ 中將各個元件封裝成 Class,例如 CText、CButton、CPicture 等等,加上 CFrame 與 CDialog 類別,分別負責各自的功能,然後再以這些類別創建出來的物件,組合成一個一個的完整介面。這樣的實做方法,在物件導向程式的領域中是很直覺的設計模式,但是也存在著不少缺點;像是每個元件都需要存在個別獨立的 Class,而若要修改或新增元件的功能,即使只是很微小的部分,也必須在 C++ 中撰寫完畢後,重新建置整個程式專案完成後才能夠使用。另外,在處理資料的讀取程序中,也會有相當瑣碎且重複性極高的修改步驟;只要更動了資料的讀取順序或資料型態,同樣非得經過重新建置的程序不可。

Lua,可以讓一切變得不同。以下,開始進入遊戲的實例說明:(閱讀以下內容,需具備基本的 Lua 程式設計能力)

先來看一段使用 Lua 製作 Text 元件的簡短程式碼:

  1. Text  
  2. {  
  3.     font = DialogTitleFont, -- 使用 DialogTitleFont 字型  
  4.     name = "oktitle"-- 元件名稱為 oktitle  
  5.     x = 12, y = 8, -- 元件的位置  
  6.     w = kMax,h = 40, -- 元件的長度與寬度  
  7.     flags = kVAlignCenter + kHAlignLeft, -- 對齊用的旗標  
  8.     label = gDialogTable.title, -- 顯示的文字  
  9. };  

上述這段程式碼,即定義出一個 Text 文字項元件的種種規格;如註解所示,這個元件使用 DialogTitleFont 這個字型,在指定的位置處顯示出 gDialogTable.title 變數的內容。而 DialogTitleFont 這個字型,定義在其他的程式碼中:

  1. DialogTitleFont = {  
  2.     standardFont, -- 又是另一個變數  
  3.     18, -- 字級大小  
  4.     BorderColor -- 字型顏色  
  5. };  
  6.   
  7. standardFont = "fonts/mercurius.mvec"-- 字型使用的實體檔案  
  8. BorderColor = Color(30, 42, 102, 255); -- 顏色  

由以上的程式碼片段可以瞭解,UI 元件的建構,是利用 Lua Function 能夠接受 Table 結構做為函式參數的強大特徵,將所需的參數傳入 Function 中進行處理。如果是沒有特別指定的參數,就直接使用預設值去建立,能夠毫不費力地建構出 Default Parameters 的功能,使得 UI 元件的建構方式變得非常具有彈性與可擴充性。

再來看一個製作 Button 元件的例子:

  1. Button  
  2. {  
  3.     x = kCenter,  
  4.     y = 500,  
  5.     font = StandardButtonFont,  
  6.     graphics = StandardButtonGraphics,  
  7.     name = "back",  
  8.     type = kPush,  
  9.     flags = kHAlignCenter + kVAlignCenter,  
  10.     label = "back",  
  11.     command =  
  12.         function()  
  13.             PopModal();  
  14.         end  
  15. };  
  16.   
  17. StandardButtonGraphics = {  
  18.     "buttons/dialog_button_a1",  
  19.     "buttons/dialog_button_a2",  
  20.     "buttons/dialog_button_a3"  
  21. };  

在 StandardButtonGraphics 這個 Table 裡,定義了 Button 在 Normal、Pushed、Disabled 三個狀態下顯示的圖片。接下來的 type 變數,指定這個 Button 的型態是一般常見的 Push Button 類型;其他還包括了 Radio Button 與 Check Button 類型。而 command 變數,是使用者按下 Button 時所需執行的程序,也就是去呼叫 PopModal() 這個函式。除了一般按下 Button 所觸發的執行程序之外,也能夠很輕易地實做出滑鼠進入 Button 範圍的 Function,或是 Button 切換至其他狀態下所應該觸發的 Function。其他的各種元件也以同樣的方法製作,就能夠迅速而便利地建立起一組 UI 元件與完整的使用者介面。

依照以上 standardFontStandardButtonGraphics 與 BorderColor 變數的定義方法,可以將所有 UI 相關的格式設定,以及與實體檔案相關的變數,全部定義在另外的 Lua 檔案中,便於集中管理、修改與使用。其中的程式碼片段如下:

  1. -- File: style.lua  
  2.   
  3. -- 字型的實體檔案  
  4. standardFont = "fonts/mercurius.mvec";  
  5.   
  6. -- 定義各種顏色  
  7. BlackColor = Color(0, 0, 0, 255);  
  8. BlueColor = Color(16, 225, 226, 255);  
  9. YellowColor = Color(255, 255, 0, 255);  
  10.   
  11. -- 標準按鈕用的圖片  
  12. StandardButtonGraphics = {  
  13.     "buttons/dialog_button_a1",  
  14.     "buttons/dialog_button_a2",  
  15.     "buttons/dialog_button_a3"  
  16. };  
  17.   
  18. -- 小型按鈕用的圖片  
  19. SmallButtonGraphics = {  
  20.     "buttons/dialog_button_a_small_1",  
  21.     "buttons/dialog_button_a_small_2",  
  22.     "buttons/dialog_button_a_small_3"  
  23. };  
  24.   
  25. -- 大型按鈕用的圖片  
  26. LargeButtonGraphics = {  
  27.     "buttons/dialog_button_a_large_1",  
  28.     "buttons/dialog_button_a_large_2",  
  29.     "buttons/dialog_button_a_large_3"  
  30. };  

最後,以遊戲中的一個 Lua 檔案為例,列出建構起一個 Dialog 所需的完整程式碼:

  1. -- File: ok.lua  
  2. require( "scripts/style.lua" );  
  3.   
  4. MakeDialog  
  5. {  
  6.     Bitmap  
  7.     {  
  8.         name = "yesnobackground",  
  9.         image = "backgrounds/popup",  
  10.         x = kCenter,  
  11.         y = kCenter,  
  12.   
  13.         Button  
  14.         {  
  15.             font = StandardButtonFont,  
  16.             graphics = StandardButtonGraphics,  
  17.             close = true,  
  18.             flags = 5,  
  19.             label = "ok",  
  20.             name = "ok",  
  21.             default = true,  
  22.             x = kCenter,  
  23.             y = 250,  
  24.         },  
  25.   
  26.         Text  
  27.         {  
  28.             font = DialogTitleFont,  
  29.             name = "oktitle",  
  30.             x = 12, y = 8,  
  31.             w = kMax, h = 40,  
  32.             flags = kVAlignCenter + kHAlignLeft,  
  33.             label = gDialogTable.title,  
  34.         };  
  35.   
  36.         Text  
  37.         {  
  38.             font = DialogBodyFont,  
  39.             name = "okbody",  
  40.             x = 20, y = 46,  
  41.             w = kMax-20, h = kMax-70,  
  42.             flags = kVAlignCenter + kHAlignCenter,  
  43.             label = gDialogTable.body,  
  44.         };  
  45.     },  
  46. }  

以上這段 Lua 程式碼,製作出由一個 Bitmap、一個 Button 以及兩個 Text 元件所組成的 Dialog 介面。撰寫完成後,在遊戲中看到這個 Dialog 所呈現出來的結構,如圖所示:

Dialog StructureBitmap 元件負責顯示整個 Dialog 的背景圖片;第一個 Text 用來顯示 Dialog 的標題列文字,第二個 Text 則顯示 Dialog 中的主要說明文字;而最後的 Button,就只是用來執行簡單的確認行為。所有的元件建構完成後,就當作 Function 的 Table 參數傳入 MakeDialog() 中創建出 Dialog。

以上文章所提的內容,其實只佔了整個 GUI 系統其中一半的部分:資料描述。也就是將 Lua 當成一種 Data Description Language,用來儲存與讀取遊戲所需的資料。藉由 Lua 的強大威力,能夠讓開發者輕易地進行各種資料寫入與讀取的動作,完全不必費力撰寫繁複瑣碎的 Parse 或 Serialize 程序。

資料處理的部分是一半,而另一半是與功能相關的部分,也就是真正在內部進行GUI 相關行為處理的程序。將資料傳入 Text()、Button()、Picture() 這些函式中之後,裡面到底做了些什麼?如何建構 GUI 系統的資料結構?如何和原來的 C++ 端主程式相結合?如何傳遞滑鼠與鍵盤的輸入訊息?如何真正畫出這些 UI?

下篇待續。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
lua中文教程,原名:programming in lua 目录 版权声明..............i 译序..i 目录iii 第一篇语言.......1 第0章序言.......1 0.1 序言..........1 0.2 Lua使用者................2 0.3 Lua的相关资源............3 0.4 本书的体例.................3 0.5 关于本书...3 0.6 感谢..........4 第1章起点.......5 1.1 Chunks.......5 1.2 全局变量...7 1.3 词法约定...7 1.4 命令行方式.................7 第2章类型和值9 2.1 Nil..............9 2.2 Booleans....9 2.3 Numbers...10 2.4 Strings......10 2.5 Functions.12 2.6 Userdata and Threads.12 第3章表达式..13 3.1 算术运算符...............13 3.2 关系运算符...............13 3.3 逻辑运算符...............13 3.4 连接运算符...............14 3.5 优先级.....15 3.6表的构造..15 第4章基本语法................18 4.1 赋值语句.18 4.2 局部变量与代码块(block)......19 4.3 控制结构语句...........20 Programming in Lua iv Copyright ® 2005, Translation Team, www.luachina.net 4.4 break和return语句......23 第5章函数......24 5.1 返回多个结果值.......25 5.2可变参数..27 5.3 命名参数.28 第6章再论函数................30 6.1 闭包........32 6.2 非全局函数...............34 6.3 正确的尾调用(Proper Tail Calls)...36 第7章迭代器与泛型for....40 7.1 迭代器与闭包...........40 7.2 范性for的语义...........42 7.3 无状态的迭代器.......43 7.4 多状态的迭代器.......44 7.5 真正的迭代器...........45 第8章编译·运行·调试47 8.1 require函数.................49 8.2 C Packages.................50 8.3 错误........51 8.4 异常和错误处理.......52 8.5 错误信息和回跟踪(Tracebacks)....53 第9章协同程序................56 9.1 协同的基础...............56 9.2 管道和过滤器...........58 9.3 用作迭代器的协同...61 9.4 非抢占式多线程.......63 第10章完整示例..............68 10.1 Lua作为数据描述语言使用........68 10.2 马尔可夫链算法.....71 第二篇 tables与objects........75 第11章数据结构..............76 11.1 数组......76 11.2 阵和多维数组.........77 11.3 链表......78 11.4 队列和双端队列.....78 11.5 集合和包.................80 11.6 字符串缓冲.............80 第12章数据文件与持久化..................84 12.1 序列化...86 Programming in Lua v Copyright ® 2005, Translation Team, www.luachina.net 第13章 Metatables and Metamethods...92 13.1 算术运算的Metamethods............92 13.2 关系运算的Metamethods............95 13.3 库定义的Metamethods................96 13.4 表相关的Metamethods................97 第14章环境..103 14.1 使用动态名字访问全局变量...103 14.2声明全局变量........104 14.3 非全局的环境.......106 第15章 Packages.............109 15.1 基本方法...............109 15.2 私有成员(Privacy)................111 15.3 包与文件................112 15.4 使用全局表............113 15.5 其他一些技巧(Other Facilities)...115 第16章面向对象程序设计.................118 16.1 类.........119 16.2 继承.....121 16.3 多重继承...............122 16.4 私有性(privacy)...................125 16.5 Single-Method的对象实现方法127 第17章 Weak表...............128 17.1 记忆函数...............130 17.2 关联对象属性.......131 17.3 重述带有默认值的表...............132 第三篇标准库134 第18章数学库................135 第19章 Table库...............136 19.1数组大小................136 19.2 插入/删除..............137 19.3 排序.....137 第20章 String库..............140 20.1 模式匹配函数.......141 20.2 模式.....143 20.3 捕获(Captures).146 20.4 转换的技巧(Tricks of the Trade)151 第21章 IO库..157 21.1 简单I/O模式..........157 21.2 完全I/O 模式........160 Programming in Lua vi Copyright ® 2005, Translation Team, www.luachina.net 第22章操作系统库........165 22.1 Date和Time............165 22.2 其它的系统调用...167 第23章 Debug库..............169 23.1 自省(Introspective)..............169 23.2 Hooks...173 23.3 Profiles.174 第四篇 C API..177 第24章 C API纵览..........178 24.1 第一个示例程序...179 24.2 堆栈.....181 24.3 C API的错误处理..186 第25章扩展你的程序....188 25.1 表操作.189 25.2 调用Lua函数.........193 25.3 通用的函数调用...195 第26章调用C函数..........198 26.1 C 函数..198 26.2 C 函数库................200 第27章撰写C函数的技巧..................203 27.1 数组操作...............203 27.2 字符串处理...........204 27.3 在C函数中保存状态.................207 第28章 User-Defined Types in C........212 28.1 Userdata.................212 28.2 Metatables..............215 28.3 访问面向对象的数据...............217 28.4 访问数组...............219 28.5 Light Userdata........220 第29章资源管理............222 29.1 目录迭代器...........222 29.2 XML解析...............225
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值