# CSO LUA 自訂義UI物件 當你想要在CSO LUA中建立更多元的UI物件時,官方僅提供了UI.Box和UI.Text兩個選項。但是,如果你希望有更多自定義的UI物件,你可以透過手刻Class物件來實現。這樣一來,你就能夠根據自己的需求來客製化UI物件了! ## Lua 中的物件導向 Lua 是一種輕量級的語言,沒有內建的 class 概念,但可以通過其他方式模擬類似的行為,例如使用表(table)和元表(metatable)來實現對象導向的編程。如果對此不太熟悉,可以參考一些線上資源,比如[這個網站](https://opensourcedoc.com/lua-programming/object/)。 ## 文字陰影 UI 物件 假設我們要創建一個自定義的文字陰影 UI 物件。首先,讓我們看一下如果不使用 class 的話,程式碼會是怎麼樣的: ```lua= TextShadow = UI.Text.Create() TextShadow:Set({ text = "哈囉", font = "small", align = "left", x = 2, --<< 偏移2點 y = 2, --<< 偏移2點 width = 100, height = 30, r = 255, g = 255, b = 255 }) Text = UI.Text.Create() Text:Set({ text = "哈囉", font = "small", align = "left", x = 0, y = 0, width = 100, height = 30, r = 255, g = 255, b = 255 }) ``` 這樣的程式碼看起來相當冗長且不易維護。現在,讓我們看看如何使用 class 來實現同樣的功能。 ### 定義 TextShadow Class 1. `TextShadow = {}`:這一行創建了一個名為 `TextShadow` 的空表,用於存儲自定義物件的方法和屬性。 2. `function TextShadow:Create()`:這一行定義了一個叫做 `Create` 的方法,該方法用於創建新的 `TextShadow` 物件。使用 : 語法定義的方法隱式地將物件自身作為第一個參數(類似於其他語言中的 this 或 self)。 3. 在 `Create` 方法中: - 首先,創建了一個名為 `newObject` 的新表,用於存儲 `TextShadow` 物件的屬性和方法。 - 在 `newObject` 表中,設置了一個叫做 `SetArg` 的表,其中包含了一系列的屬性,用於設置陰影效果的參數。這些參數包括了文本內容 (text)、字體 (font)、對齊方式 (align),,位置(x 和 y),大小 (width 和 height),顏色 (r、g、b 和 a) 以及偏移量 (offset)。 - 接著,創建了一個 `UI` 表,其中包含了兩個名為 `back` 和 `front` 的屬性,這些屬性分別表示陰影的背景和前景,並且使用了 `UI.Text.Create()` 方法來創建相應的文本 UI 物件。 - 最後,設置了一個名為 `visible` 的屬性,表示該陰影是否可見,並將其設置為預設值 `true`。 總的來說,這段程式碼創建了一個具有自定義屬性和方法的 `TextShadow` 物件,用於表示文本的陰影效果。通過這種方式,可以方便地創建。 > 來自 ChatGPT 說明 ```lua= -- 定義物件 TextShadow = {} function TextShadow:Create() -- 自訂義物件內容並給預設值,內容怎樣都行。 local newObject = { SetArg = { text = "", font = "small", align = "left", x = 0, y = 0, width = 0, height = 0, r = 255, g = 255, b = 255, a = 255, offset = 2 }, UI = { back = UI.Text.Create(), front = UI.Text.Create() }, visible = true } return setmetatable(newObject, {__index = TextShadow}) end ``` 上述程式碼定義了一個 TextShadow Class,其中包含了一個 Create 方法用於創建新的 TextShadow 物件。該物件包含了一個 SetArg 表,用於存儲設定參數,一個 UI 表用於存儲 UI 元件,以及一個 visible 屬性用於表示陰影是否可見。 ### 更新 UI 接下來我們在定義一個Update函式,來更新我們的UI要怎麼設定。 1. `function TextShadow:Update()`:這一行定義了一個叫做 `Update` 的方法,該方法用於更新 `TextShadow` 物件的 UI。 2. `local arg = self.SetArg`:這一行將 `self.SetArg` 表示的陰影設置參數儲存在本地變數 `arg` 中,以便於後續使用。 3. `local setting = {...}`:這一行創建了一個名為 `setting` 的表,其中包含了陰影效果的設定。對於每個 UI 元素(back 和 front),設置了相應的位置(x 和 y)、顏色(r、g、b 和 a)等參數。 4. `for k,v in pairs(self.UI) do`:這一行使用 `pairs` 函數遍歷 `self.UI` 表中的所有元素,其中 k 是鍵(back 或 front),而 `v` 則是相應的值(UI.Text 物件)。 5. 在迴圈中,創建了一個名為 `temp` 的表,用於存儲更新後的 UI 參數。這些參數包括了位置 (x 和 y),大小 (width 和 height),顏色 (r、g、b 和 a),以及文本相關的屬性(text、font、align)。 6. `v:Set(temp)`:這一行調用了 UI.Text 物件的 `Set` 方法,並將更新後的參數 `temp` 傳遞給該方法,從而更新了 UI 的屬性。 總的來說,這段程式碼是用來更新 TextShadow 物件的 UI,並根據設置的參數來更新相應的屬性,例如位置、大小、顏色等。這樣一來,可以在程式執行過程中動態地改變陰影效果的外觀。 > 來自 ChatGPT 說明 ```lua= -- 更新UI function TextShadow:Update() local arg = self.SetArg local setting = { back = {x = arg.offset, y = arg.offset, r = 0, g = 0, b = 0, a = arg.a}, front = {x = 0, y = 0, r = arg.r, g = arg.g, b = arg.b, a = arg.a} } for k,v in pairs(self.UI) do local temp = { text = arg.text, font = arg.font, align = arg.align, x = arg.x + setting[k].x, y = arg.y + setting[k].y, width = arg.width, height = arg.height, r = setting[k].r, g = setting[k].g, b = setting[k].b, a = setting[k].a } v:Set(temp) end end ``` 上述程式碼定義了一個 Update 方法,用於更新 TextShadow 物件的 UI。該方法根據設定參數更新 UI 的各個屬性,包括位置、大小、顏色等。 ### 其他必需的函式 1. `function TextShadow:Set(args)`:這一行定義了一個名為 `Set` 的方法,該方法用於設置陰影效果的屬性。首先,它檢查傳入的參數是否為一個表,如果是的話,則調用 `setArgs` 函數將該表中的值設置到 `self.SetArg` 中。最後,它調用了 `self:Update()` 方法來更新 UI。 2. `function TextShadow:Get()`:這一行定義了一個名為 `Get` 的方法,該方法用於取得陰影效果目前的設置值。它調用了 `clone` 函數來複製 `self.SetArg` 表,以防止直接返回引用。 3. `function TextShadow:Show()`:這一行定義了一個名為 `Show` 的方法,該方法用於顯示陰影效果的 UI。它調用了 `deepCall` 函數來遞迴地調用 UI 表中每個元素的 `Show` 方法,以顯示相應的 UI。最後,將 `self.visible` 設置為 true,表示陰影效果已經顯示。 4. `function TextShadow:Hide()`:這一行定義了一個名為 `Hide` 的方法,該方法用於隱藏陰影效果的 UI。它同樣調用了 `deepCall` 函數來遞迴地調用 `UI` 表中每個元素的 `Hide` 方法,以隱藏相應的 UI。最後,將 `self.visible` 設置為 false,表示陰影效果已經隱藏。 5. `function TextShadow:IsVisible()`:這一行定義了一個名為 `IsVisible` 的方法,該方法用於返回陰影效果是否可見的狀態。它簡單地返回 `self.visible` 的值,以表示陰影效果目前是否處於顯示狀態。 總的來說,這些方法為 TextShadow 物件提供了設置、取得、顯示和隱藏陰影效果的功能,並提供了一個方法來查詢陰影效果目前是否可見。 > 來自 ChatGPT 說明 ```lua= -- 設置 function TextShadow:Set(args) if type(args) == "table" then setArgs(self.SetArg, args) end self:Update() end -- 取得設定值 function TextShadow:Get() return clone(self.SetArg) end -- 顯示 function TextShadow:Show() deepCall(self.UI, "Show") self.visible = true end -- 隱藏 function TextShadow:Hide() deepCall(self.UI, "Hide") self.visible = false end -- 返回顯示狀態 function TextShadow:IsVisible() return self.visible end ``` 上述程式碼定義了一些必需的函式,包括設置、取得設定值、顯示、隱藏和返回顯示狀態等功能。這些函式使得 TextShadow 物件更加易用和靈活。 ### 輔助函式 1. `local function setArgs(data, args)`:這是一個局部函數,用於更新表中的值。它接受兩個參數,`data` 表示待更新的表,`args` 表示包含新值的表。該函數通過遍歷 `data` 表中的每個鍵,檢查是否存在相應的 args 表中的值,如果是的話,則將 `data` 表中的值更新為 `args` 表中的對應值。如果鍵對應的值是一個表,則遞迴調用 `setArgs` 函數以處理嵌套的表。 2. `local function deepCall(table, funcName, args)`:這是一個局部函數,用於在表中深層遞迴地調用指定的函式。它接受三個參數,`table` 表示待遍歷的表,`funcName` 表示要調用的函式名稱,`args` 表示要傳遞給該函式的參數。該函數遍歷 `table` 表中的每個元素,如果元素是一個表,則遞迴調用 `deepCall` 函數以處理嵌套的表;如果元素是一個 userdata,則調用該 userdata 對象的指定函式,並傳遞參數 `args`。 3. `local function clone(table)`:這是一個局部函數,用於深層複製表。它接受一個表作為參數,並返回該表的深層複製。該函數遍歷原始表中的每個鍵值對,如果值是一個表,則遞迴調用自身以處理嵌套的表;否則,將鍵值對直接複製到新的表中。 這些輔助函數提供了對表的操作,使得程式碼更加模組化和易於理解。通過這些函數,可以更方便地處理複雜的數據結構和執行深層遞迴操作。 > 來自 ChatGPT 說明 ```lua= -- 更新設定值 local function setArgs(data, args) for k in pairs(data) do if type(data[k]) == type(args[k]) then if type(data[k]) == "table" then setArgs(data[k], args[k]) else data[k] = args[k] end end end end -- 深層調用,調用指定函式 local function deepCall(table, funcName, args) for _,v in pairs(table) do if type(v) == "table" then deepCall(v, funcName, args) elseif type(v) == "userdata" then v[funcName](v, args) end end end -- 複製表(table) local function clone(table) local temp = {} for k,v in pairs(table) do if type(v) == "table" then temp[k] = clone(v) else temp[k] = v end end return temp end ``` 上述程式碼定義了三個輔助函式,分別是更新設定值的 setArgs、深層調用的 deepCall 和複製表的 clone。這些函式用於處理表的操作,使得程式碼更加模組化和易於理解。 ### 完整代碼 ```lua= -- 更新設定值 local function setArgs(data, args) for k in pairs(data) do if type(data[k]) == type(args[k]) then if type(data[k]) == "table" then setArgs(data[k], args[k]) else data[k] = args[k] end end end end -- 深層調用,調用指定函式 local function deepCall(table, funcName, args) for _,v in pairs(table) do if type(v) == "table" then deepCall(v, funcName, args) elseif type(v) == "userdata" then v[funcName](v, args) end end end -- 複製表(table) local function clone(table) local temp = {} for k,v in pairs(table) do if type(v) == "table" then temp[k] = clone(v) else temp[k] = v end end return temp end -- 定義物件 TextShadow = {} function TextShadow:Create() -- 自訂義物件內容並給預設值,內容怎樣都行。 local newObject = { SetArg = { text = "", font = "small", align = "left", x = 0, y = 0, width = 0, height = 0, r = 255, g = 255, b = 255, a = 255, offset = 2 }, UI = { back = UI.Text.Create(), front = UI.Text.Create() }, visible = true } return setmetatable(newObject, {__index = TextShadow}) end -- 更新UI function TextShadow:Update() local arg = self.SetArg local setting = { back = {x = arg.offset, y = arg.offset, r = 0, g = 0, b = 0, a = arg.a}, front = {x = 0, y = 0, r = arg.r, g = arg.g, b = arg.b, a = arg.a} } for k,v in pairs(self.UI) do local temp = { text = arg.text, font = arg.font, align = arg.align, x = arg.x + setting[k].x, y = arg.y + setting[k].y, width = arg.width, height = arg.height, r = setting[k].r, g = setting[k].g, b = setting[k].b, a = setting[k].a } v:Set(temp) end end -- 設置 function TextShadow:Set(args) if type(args) == "table" then setArgs(self.SetArg, args) end self:Update() end -- 取得設定值 function TextShadow:Get() return clone(self.SetArg) end -- 顯示 function TextShadow:Show() deepCall(self.UI, "Show") self.visible = true end -- 隱藏 function TextShadow:Hide() deepCall(self.UI, "Hide") self.visible = false end -- 返回顯示狀態 function TextShadow:IsVisible() return self.visible end -- 測試 local screen = UI.ScreenSize() local center = {x = screen.width / 2, y = screen.height / 2} text = TextShadow:Create() text:Set({ text = "哈囉 你好~", font = "medium", align = "center", x = 0, y = center.y, width = screen.width, height = 50, r = 0, g = 128, b = 255, offset = 5 }) ``` 上述是完整的程式碼範例,展示了如何使用 Class 物件來建立自定義的文字陰影 UI 物件。這樣的設計使得程式碼更加模組化和易於理解,同時也提供了更好的可擴展性和重用性。