# 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 物件。這樣的設計使得程式碼更加模組化和易於理解,同時也提供了更好的可擴展性和重用性。