#Cocos2dx手遊開發#11重構Lua端UserDefault類
序言
Cocos2dx引擎為我們提供了 cc.UserDefault 類,用於本地資料儲存。在C++端的UserDefault類提供了6種資料型別的讀寫介面, 每種型別對應有讀介面和寫介面,其中的讀介面還有一個過載。
這需要記住相當多的API,是一個記憶的負擔。我們的問題是:
能不能有更簡潔的介面?
於是,基於此問題,在不改變UserDefault類的介面基礎上,在Lua指令碼中增加一個新類 ud ,用於提供更簡潔的key-value資料結構的儲存解決方案。
關於UserDefault類的原始碼分析,可請參見此文章
#Cocos2dx+Lua原始碼#UserDefault類
大綱
本文將從以下要點進行說明
- 我們希望以怎樣的方式在本地讀寫資料;
- 解決方案的設計;
- 解決方案的實現;
- 測試API介面;
1. 我們希望以怎樣的方式在本地讀寫資料
我們希望是僅僅使用2個介面來實現我們的讀寫操作:
寫入操作
ud:setValueForKey(key, value)
我們要求
- key是string型別的非空字串
- value可以是nil、boolean、number、string四種型別,當value是nil型別時,我們希望這是一個delete操作
- 我們不希望寫入過於複雜的資料型別,導致系統複雜度的增加。
讀取操作
ud:getValueForKey(key, default)
我們要求
- key是string型別的非空字串
- 當key有儲存資料的時候,返回儲存的資料
- 當key沒有儲存資料的時候,返回default
2. 解決方案的設計
我們假設關鍵字
key = "TestKey"
我們為其分配兩個新的關鍵字
type_key = "TestKey_TYPE_KEY"
和
value_key = "TestKey_VALUE_KEY"
寫入操作
當我們呼叫
ud:setValueForKey("TestKey", "John")
xml表中,會出現這樣的資料結構
<?xml version="1.0" encoding="UTF-8"?>
<userDefaultRoot>
<TestKey_TYPE_KEY>S</TestKey_TYPE_KEY>
<TestKey_VALUE_KEY>John</TestKey_VALUE_KEY>
</userDefaultRoot>
其中
TestKey_TYPE_KEY節點儲存的S 標記為String型別
TestKey_VALUE_KEY節點儲存的這個型別對應的值John
當我們呼叫
ud:setValueForKey("TestKey", false)
xml表中,會出現這樣的資料結構
<?xml version="1.0" encoding="UTF-8"?>
<userDefaultRoot>
<TestKey_TYPE_KEY>B</TestKey_TYPE_KEY>
<TestKey_VALUE_KEY>false</TestKey_VALUE_KEY>
</userDefaultRoot>
其中
TestKey_TYPE_KEY節點儲存的B 標記為Bool型別
TestKey_VALUE_KEY節點儲存的這個型別對應的值false
當我們呼叫
ud:setValueForKey("TestKey", 99)
xml表中,會出現這樣的資料結構
<?xml version="1.0" encoding="UTF-8"?>
<userDefaultRoot>
<TestKey_TYPE_KEY>I</TestKey_TYPE_KEY>
<TestKey_VALUE_KEY>99</TestKey_VALUE_KEY>
</userDefaultRoot>
其中
TestKey_TYPE_KEY節點儲存的I 標記為Integer型別
TestKey_VALUE_KEY節點儲存的這個型別對應的值99
當我們呼叫
ud:setValueForKey("TestKey", 88.88)
xml表中,會出現這樣的資料結構
<?xml version="1.0" encoding="UTF-8"?>
<userDefaultRoot>
<TestKey_TYPE_KEY>D</TestKey_TYPE_KEY>
<TestKey_VALUE_KEY>88.88</TestKey_VALUE_KEY>
</userDefaultRoot>
其中
TestKey_TYPE_KEY節點儲存的D 標記為Double型別
TestKey_VALUE_KEY節點儲存的這個型別對應的值88.88
當我們呼叫
ud:setValueForKey("TestKey", nil)
xml表中
TestKey_TYPE_KEY節點和TestKey_VALUE_KEY節點都將被刪除。
讀取操作
當我們呼叫
ud:getValueForKey("TestKey", default)
若xml表中 有 TestKey_TYPE_KEY節點,那麼它一定是合法的資料,一定能夠取到TestKey_VALUE_KEY的正確值。
若xml表中 沒有 TestKey_TYPE_KEY節點,則返回default值
3. 解決方案的實現
-- 首先定義兩個key值的轉換函式
local function __typeKey(key)
return string.format("%s_TYPE_KEY", key)
end
local function __valueKey(key)
return string.format("%s_VALUE_KEY", key)
end
-- 再定義一個從判斷其儲存資料型別的介面
local function __cppType(value)
local lua_value_type = type(value)
local cpp_value_type
if lua_value_type == "string" then
cpp_value_type = "S"
elseif lua_value_type == "boolean" then
cpp_value_type = "B"
else
assert(lua_value_type == "number")
if value%1 == 0 then
cpp_value_type = "I"
else
cpp_value_type = "D"
end
end
return cpp_value_type
end
-- 定義類
local XXUserDefault = class(" XXUserDefault")
-- 定義類的寫入介面
function XXUserDefault:setValueForKey(key, value)
local key_type = type(key)
local value_type = type(value)
assert(key ~= "" and key_type == "string")
if (value_type == "string" or value_type == "number" or value_type == "boolean") then
local _type_key = __typeKey(key)
local _value_key = __valueKey(key)
local _cpp_value_type = __cppType(value)
local _record_cpp_value_type = cc.UserDefault:getInstance():getStringForKey(_type_key)
if _record_cpp_value_type == "" then
-- 空的, 說明從來沒有賦值過
cc.UserDefault:getInstance():setStringForKey(_type_key, _cpp_value_type)
_record_cpp_value_type = _cpp_value_type
end
if _record_cpp_value_type ~= _cpp_value_type then
-- 兩個型別不一樣
cc.UserDefault:getInstance():setStringForKey(_type_key, _cpp_value_type)
end
if _cpp_value_type == "S" then
cc.UserDefault:getInstance():setStringForKey(_value_key, value)
elseif _cpp_value_type == "B" then
cc.UserDefault:getInstance():setBoolForKey(_value_key, value)
elseif _cpp_value_type == "I" then
cc.UserDefault:getInstance():setIntegerForKey(_value_key, value)
elseif _cpp_value_type == "D" then
cc.UserDefault:getInstance():setDoubleForKey(_value_key, value)
end
elseif (value_type == "nil") then
-- 清空工作
cc.UserDefault:getInstance():deleteValueForKey(__typeKey(key))
cc.UserDefault:getInstance():deleteValueForKey(__valueKey(key))
end
end
-- 定義類的讀取介面
function XXUserDefault:getValueForKey(key, default)
assert(type(key) == "string" and key ~= "", "[ERROR] key must be of type string and not empty!")
local _cpp_value_type = cc.UserDefault:getInstance():getStringForKey(__typeKey(key))
if _cpp_value_type == "S" then
return cc.UserDefault:getInstance():getStringForKey(__valueKey(key))
elseif _cpp_value_type == "B" then
return cc.UserDefault:getInstance():getBoolForKey(__valueKey(key))
elseif _cpp_value_type == "I" then
return cc.UserDefault:getInstance():getIntegerForKey(__valueKey(key))
elseif _cpp_value_type == "D" then
return cc.UserDefault:getInstance():getDoubleForKey(__valueKey(key))
else
assert(_cpp_value_type == "")
return default
end
end
-- xx.convert.singleton是將類轉化為單例的函式
return xx.convert.singleton(XXUserDefault)
4. 測試API介面
我們需要測試在TestKey對應各種取值情況下,附帶各種預設引數時,返回值是否正確。
-- 定義列印幫助類和基礎的資料型別
local convert_ret_to_print_string = function(ret)
if type(ret) == "string" then
return ("'"..ret.."'")
end
return tostring(ret)
end
local function printRet(ret)
print("type of ret:", type(ret), " value of ret:", convert_ret_to_print_string(ret))
end
local function printJudge(bSame)
print("----> ", bSame == true and "OK" or "ERROR")
end
local test_list = {
nil,
false,
true,
100,
88.88,
"",
"default",
}
local function test(origin_value)
for i = 1, 7 do
local default_value = test_list[i]
local ret = xx.ud:getValueForKey('TestKey', default_value)
if origin_value ~= nil then
if ret ~= origin_value then
return false
end
else
if ret ~= default_value then
return false
end
end
end
return true
end
local function test_all()
for j=1, 7 do
local _value = test_list[i]
xx.ud:setValueForKey('TestKey', _value)
if not test(_value) then
return false
end
end
return true
end
printJudge(test_all())
——>
----> OK
測試通過!
總結
我們使用了一種更加優雅的訪問方式,雖然增加了本地資料儲存量,但由於它並非是一種頻繁讀寫的資料庫,且儲存量並不會很大,於是,追求訪問方式的優雅度,是我們最關注的,對於這樣的結果,我個人表示很滿意。
相關文章
- Lua遊戲開發(一)---Lua語言遊戲開發
- 【Lua】VSCode 搭建 Lua 開發環境VSCode開發環境
- Laravel 高效開發模板 (重構)Laravel
- lua 開發環境搭建開發環境
- 使用重構件(Codemod)加速JavaScript開發和重構JavaScript
- CoCos2dx開發:中文亂碼
- PHP手遊開發(手遊專案)PHP
- 【遠端檔案瀏覽器】Unity+Lua開發除錯利器瀏覽器Unity除錯
- Lua遊戲開發(三)---Lua語言學習(上)遊戲開發
- 程式碼重構:類重構的 8 個小技巧
- 營運型手遊開發、測試、正式的三階段開發架構架構
- Nginx+Lua開發環境搭建Nginx開發環境
- Java 最全工具類(後端開發必備)Java後端
- 禮物模組是直播類app開發的重點APP
- 安裝Nginx+Lua開發環境Nginx開發環境
- 基於ArkUI框架開發-ImageKnife渲染層重構UI框架
- 頁面重構WEEX開發問題總結
- oracle 11g線上重構表Oracle
- 11gRAC二節點重構
- Eclipse的Lua開發外掛 LDT地址Eclipse
- win10搭建cocos2dx開發環境怎麼安裝_win10搭建cocos2dx開發環境的步驟Win10開發環境
- 全端開發之“兩端開發策略”
- cocos2dx原始碼:背景層封裝類原始碼封裝
- 遊戲開發原理——手遊開發團隊與成本遊戲開發
- 螞蟻金服 mPaaS 模組化開發與架構重構深度解析架構
- 透過例子學習Lua(3)----Lua資料結構(轉)資料結構
- [Lua遊戲AI開發指南] 筆記零 - 框架搭建遊戲AI筆記框架
- apisix~lua外掛開發與外掛註冊API
- 11月份手遊買量市場分析:三國類、傳奇類遊戲投放力最大遊戲
- 前後端高效協作開發的11條建議後端
- [成都11-15k]PHP後端開發《在家辦公》PHP後端
- 重構遺留程式碼(11):終結篇
- Laravel Vue 前後端分類模組化外掛化開發疑惑LaravelVue後端
- 快速實現無人車遠端控制開發——實踐類
- 手遊《桃源鄉》11月26日安卓端計費刪檔測試安卓
- toLua中Lua呼叫C#中的類C#
- 基於ngx_lua模組的waf開發實踐
- 使用 Nginx + Lua(OpenResty)開發高效能Web應用NginxRESTWeb