---
# System prepended metadata

title: CSO LUA 小技巧

---

# CSO LUA 小技巧

[toc]

## 玩家座標的使用

這可能是你的寫法，看起來沒毛病，但這會讓程式碼很攏長。

```lua=
x = {}
y = {}
z = {}

function Game.Rule:OnPlayerConnect(player)
   x[player.index] = player.position.x
   y[player.index] = player.position.y
   z[player.index] = player.position.z
end
```

這是比較理想的寫法，一個變數直接儲存XYZ。

```lua=
playerPosition = {}

function Game.Rule:OnPlayerConnect(player)
    playerPosition[player.index] = player.position
end
```

舉個例子，像是KZ的存讀點功能。

```lua=
playerPosition = {}

function Game.Rule:OnPlayerSignal(player, signal)
    if signal == 1 then
        -- 當玩家按下1，儲存座標
        playerPosition[player.index] = player.position
    
    elseif signal == 2 then
        -- 當玩家按下2，讀取座標
        player.position = playerPosition[player.index]
    end
end
```

## 大量且重複性高的EntityBlock創建

這我很常用在KZ裡面的跳板，10個關卡，每一關可能有10個板子，那就要創建100個座標和函式。

```lua=
playerPosition = {}

block_1 = Game.EntityBlock.Create({x = 1, y = 1, z = 1})
function block_1:OnTouch(player)
     playerPosition[player.index] = player.position
end

block_2 = Game.EntityBlock.Create({x = 2, y = 2, z = 2})
function block_2:OnTouch(player)
     playerPosition[player.index] = player.position
end

block_3 = Game.EntityBlock.Create({x = 3, y = 3, z = 3})
function block_3:OnTouch(player)
     playerPosition[player.index] = player.position
end

...無限循環
```

只要把座標全部都彙整進一個變數，利用迴圈來跑就不用再寫無意義的函式。

```lua=
blocks = {
    {x = 1, y = 1, z = 1},
    {x = 2, y = 2, z = 2},
    {x = 3, y = 3, z = 3}
}

for k,v in pairs(blocks) do
    local block = Game.EntityBlock.Create(v)
    function block:OnTouch(player)
         playerPosition[player.index] = player.position
    end
end
```

## 動態玩家排名系統

直接利用Lua內建函式`table.sort`幫我們處立即可。

```lua
-- list: 欲排序的表(table)
-- comp: 自定義排序方式(function)
table.sort(list [, comp])
```

```lua=
playerRank = {
    {player = player, time = 15}, -- 玩家一
    {player = player, time = 17}, -- 玩家二
    {player = player, time = 30}, -- 玩家三
    {player = player, time = 10}, -- 玩家四
    {player = player, time = 13}, -- 玩家五
}

-- comp: 自訂義排序，將表裡面的time屬性進行比較，時間快的排越前面。
function sort(a, b)
    return a.time < b.time
end

-- 執行排序功能
table.sort(playerRank, sort)

--[[
結果:
    playerRank = {玩家四, 玩家五, 玩家一, 玩家二, 玩家三}
]]
```

## 多個玩家的SyncValue優化

想要把Game多個玩家的資料傳送到UI，你可能會這麼做。
這也沒毛病，但如果寫到24人，實在是太攏長了。

```lua=
-- game.lua
InitialLevel = 1

sync_PlayerLevel_1 = Game.SyncValue.Create("sync_PlayerLevel.1")
sync_PlayerLevel_2 = Game.SyncValue.Create("sync_PlayerLevel.2")
sync_PlayerLevel_3 = Game.SyncValue.Create("sync_PlayerLevel.3")

function Game.Rule:OnPlayerConnect(player)
    if player.index == 1 then
        sync_PlayerLevel_1.value = InitialLevel
    elseif player.index == 2 then
        sync_PlayerLevel_2.value = InitialLevel
    elseif player.index == 3 then
        sync_PlayerLevel_3.value = InitialLevel
    end
end
```

```lua=
-- ui.lua
sync_PlayerLevel_1 = UI.SyncValue.Create("sync_PlayerLevel.1")
function sync_PlayerLevel_1:OnSync()
   -- do something 
end
sync_PlayerLevel_2 = UI.SyncValue.Create("sync_PlayerLevel.2")
function sync_PlayerLevel_2:OnSync()
   -- do something 
end
sync_PlayerLevel_3 = UI.SyncValue.Create("sync_PlayerLevel.3")
function sync_PlayerLevel_3:OnSync()
   -- do something 
end
```

只要改成這樣，程式碼就會變得簡潔、易懂。

```lua=
-- game.lua
InitialLevel = 1
sync_PlayerLevel = {}

for i = 1, 24 do
    sync_PlayerLevel[i] = Game.SyncValue.Create("sync_PlayerLevel." .. i)
end

function Game.Rule:OnPlayerConnect(player)
    sync_PlayerLevel[player.index].value = InitialLevel
end
```

```lua=
-- ui.lua
sync_PlayerLevel = UI.SyncValue.Create("sync_PlayerLevel." .. UI.PlayerIndex())
function sync_PlayerLevel:OnSync()
   -- do something 
end
```

## 數字轉貨幣格式

> [來源](https://gist.github.com/Ivaar/a95d9e565bd9a0e2036179c0c8cdee58#file-currencies-lua-L18)
```lua=
function comma_value(n)
    local left,num,right = string.match(n,'^([^%d]*%d)(%d*)(.-)$')
    return left..(num:reverse():gsub('(%d%d%d)','%1,'):reverse())..right
end

print(comma_value(10000000))   --    10,000,000
print(comma_value("10000000")) --    10,000,000
print(comma_value(123456789))  --   123,456,789
print(comma_value(1234567890)) -- 1,234,567,890
```

## 秒數轉換時間格式

這個可以轉換成: 小時:分鐘:秒 `00:00:00`

```lua=
function SecondsToTime(sec)
    local hour    = math.floor(sec / 3600)
    local minute  = math.floor(sec / 60) - (hour * 60)
    local seconds = math.floor(sec % 60)
    return string.format("%02d:%02d:%02d", hour, minute, seconds)
end

print(SecondsToTime(0))    -- 00:00:00
print(SecondsToTime(100))  -- 00:01:40
print(SecondsToTime(1280)) -- 00:21:20
print(SecondsToTime(3600)) -- 01:00:00
```

這個可以轉換成: 分鐘:秒.毫秒 `00:00.00`

```lua=
function SecondsToTime(sec)
    local minute  = math.floor(sec / 60)
    local seconds = math.floor(sec % 60)
    return string.format("%02d:%05.2f", minute, seconds)
end

print(SecondsToTime(0.661))     -- 00:00:00.66
print(SecondsToTime(100.185))   -- 00:01:40.19
print(SecondsToTime(1280.1698)) -- 00:21:20.17
print(SecondsToTime(3600.9541)) -- 01:00:00.95
```

## UI漸層顏色

```lua
-- colorA:   (table)A顏色 {r = 255, g = 255, b = 255, a = 255}
-- colorB:   (table)B顏色 {r = 255, g =   0, b = 255, a = 255}
-- boxCount: (number)產生A~B之間的漸變顏色數量
GetGradient(colorA, colorB, boxCount)
```

```lua=
function GetGradient(colorA, colorB, boxCount)
    colorA.a = colorA.a or 255
    colorB.a = colorB.a or 255

    local temp = {}
    for i = 1, boxCount do
        local rAvg = colorA.r + math.floor((colorB.r - colorA.r) * i / boxCount)
        local gAvg = colorA.g + math.floor((colorB.g - colorA.g) * i / boxCount)
        local bAvg = colorA.b + math.floor((colorB.b - colorA.b) * i / boxCount)
        local aAvg = colorA.a + math.floor((colorB.a - colorA.a) * i / boxCount)
        table.insert(temp, {r = rAvg, g = gAvg, b = bAvg, a = aAvg})
    end
    return temp
end

print(GetGradient({r=255,g=255,b=255}, {r=255,g=0,b=255}, 6))
--[[
output
    {
        {r=255,g=212,b=255},{r=255,g=170,b=255},{r=255,g=127,b=255},
        {r=255,g= 85,b=255},{r=255,g= 42,b=255},{r=255,g=  0,b=255}
    }
]]
```