上一篇的文章中说到了一些个人习惯的东西以及一些简单的项目配置,这一篇文章我们来进一步完善一些东西.首先,打开编译以后的客户端执行,会看到一大堆的fileutils加载luac文件的提示,在终端显示一大堆,挺烦人的,我做的第一件事就是去修改他.这个很简单,有两种做法,第一种是在c++部分添加调用
1 cocos2d::FileUtils::getInstance()->setPopupNotify(false);
当然也可以在lua部分添加,
1 cc.FileUtils:getInstance():setPopupNotify(false)
两种做法都很简单,根据自己选择.我选择是在AppDelegate.cpp中添加.暂时没有在c++部分添加自己的模块,因为现在还不需要,后面需要的时候会再说.我将新项目中的其他lua文件全部删除了,res下面的所有资源也都删除了,修改了main.lua中的源码,只留下了引擎启动初始化glview部分的代码,这样就足够了,无论是开发一款什么样子的项目,我们都是从这里开始的.修改后的源码如下,很多同学不用看,只是为了保持我写文章的原始目的,尽量说明我的思路.
1 -- 小岩<757011285@qq.com> 2 -- 2015-5-25 13:10 3 cc.FileUtils:getInstance():addSearchPath("res") 4 cc.FileUtils:getInstance():addSearchPath("src") 5 6 require "cocos.init" 7 local fw = require "fw.init" 8 9 function __G__TRACKBACK__(msg) 10 print("lua error: " .. tostring(msg) .. "\n") 11 print(debug.traceback()) 12 return msg 13 end 14 15 local function main() 16 collectgarbage("collect") 17 -- avoid memory leak 18 collectgarbage("setpause", 100) 19 collectgarbage("setstepmul", 5000) 20 21 -- initialize director 22 local director = cc.Director:getInstance() 23 local glview = director:getOpenGLView() 24 if nil == glview then 25 glview = cc.GLViewImpl:createWithRect("HelloLua", cc.rect(0,0,900,640)) 26 director:setOpenGLView(glview) 27 end 28 29 glview:setDesignResolutionSize(480, 320, cc.ResolutionPolicy.NO_BORDER) 30 31 --turn on display FPS 32 director:setDisplayStats(true) 33 34 --set FPS. the default value is 1.0/60 if you don't call this 35 director:setAnimationInterval(1.0 / 60) 36 37 end 38 39 local status, msg = xpcall(main, __G__TRACKBACK__) 40 if not status then 41 error(msg) 42 end
好的,可能很多人做到这里,就会思考并开始写代码了.我像这样应该还是缺少很多东西的,尤其是我们做过一些项目后就知道,有很多基础的模块和功能还是需要的,实现这个过程就像是盖房子打地基,我就花费了半天的时间来做这件事情.我先是构思了一下,然后稍作修改了一下项目的目录结构.修改后的目录结构如下:
-- res
-- src
-- cocos
-- fw
-- ini.lua
-- util
-- fileutil.lua
-- platform.lua
-- convert.lua
-- oop
-- ini.lua
-- csv.lua
-- kv.lua
-- classickv.lua
-- game
-- main.lua
好的下面我来解释一下这个目录.我添加了fw目录作为项目通用的模块,这样做是有好处的,如果以后项目需要的话,做的好,我完全可以直接把这个模块拿过去,稍作修改甚至是不用修改就可以胜任了,也就是积累我们自己的代码.当然,鱿鱼经验受限,我们会一次又一次的重构这个模块,就像我,不停的去修改去完善,让它在某些方面更合理.其实昨天我主要是添加了三个方面的功能:
1.我将cc.FileUtils里面的结构全部倒入了我写的fileutil中,并添加了部分自己的功能,源码如下:
1 -- 小岩<[email protected]> 2 -- 2015-5-25 3 local platform = require "fw.util.platform" 4 5 local fu = {} 6 local cc_fileutils_ = cc.FileUtils:getInstance() 7 8 function fu.is_dir(filename) 9 return string.byte(filename, -1) == 47 10 end 11 12 function fu.is_absolute_path(filename) 13 return cc_fileutils_:isAbsolutePath(filename) 14 end 15 16 function fu.get_writable_path(filename) 17 if filename then 18 return platform.get_writable_path() .. filename 19 end 20 return platform.get_writable_path() 21 end 22 23 function fu.get_full_path(filename) 24 return cc_fileutils_:fullPathForFilename(filename) 25 end 26 27 function fu.get_string(filename) 28 return cc_fileutils_:getStringFromFile(filename) 29 end 30 31 function fu.write_content(filename, content, mode) 32 mode = mode or "w+b" 33 local file = io.open(filename, mode) 34 if file then 35 if file:write(content) == nil then 36 return false 37 end 38 io.close(file) 39 return true 40 else 41 return false 42 end 43 end 44 45 function fu.is_exists(filename) 46 if fu.is_dir(filename) then 47 return cc_fileutils_:isDirectoryExist(filename) 48 end 49 return cc_fileutils_:isFileExist(filename) 50 end 51 52 function fu.make_dir(dirname) 53 return cc_fileutils_:createDirectory(dirname) 54 end 55 56 function fu.remove(filename) 57 if fu.is_dir(filename) then 58 return cc_fileutils_:removeDirectory(filename) 59 end 60 return cc_fileutils_:removeFile(filename) 61 end 62 63 function fu.rename_file(dir, old, new) 64 return cc_fileutils_:renameFile(dir, old, new) 65 end 66 67 function fu.get_file_size(filename) 68 return cc_fileutils_:getFileSize(filename) 69 end 70 71 return fu
2.添加了跨平台部分的文件存储处理,主要是处理writablepath.这个在platform.lua中,很快我就会写到增量动态更新模块,我期望在windows下面存储的路径就是res/,而在android下面则是writablepath.所以这个修改也是为了这部分功能先行的.源码如下:
1 -- 小岩<[email protected]> 2 -- 2015-5-25 3 local p = {} 4 5 local OS_NAME = 6 { 7 "WINDOWS", 8 "LINUX", 9 "MAC", 10 "ANDROID", 11 "IPHONE", 12 "IPAD", 13 "BLACKBERRY", 14 "NACL", 15 "EMSCRIPTEN", 16 "TIZEN", 17 "WINRT", 18 "WP8", 19 } 20 21 local arch_code = cc.Application:getInstance():getTargetPlatform() 22 local arch_name = OS_NAME[arch_code+1] 23 24 p.arch_code = arch_code 25 p.arch_name = arch_name 26 27 function p.get_writable_path() 28 if p.arch_code == cc.PLATFORM_OS_WINDOWS then 29 return "res/" 30 else 31 return cc.FileUtils:getInstance():getWritablePath() 32 end 33 end 34 35 return p
3.处理各种配置文件,我暂时添加了四种,k-v有两种,ini格式,csv格式,准确的说是三种,不过我提供的classickv是支持lua语法的,因为本质上就是使用luastring返回的函数生成的,所以可以直接在其中使用lua语法,如果是使用sublime,可以直接ctrl+shift+p, 然后输入set synatax:lua去直接编辑配置文件.不过很快我就会再次介绍这种使用lua语法的好处,用在启动引擎的各种参数上面再好不过了.下面我就一一给出这四个文件.不过首先得看convert.lua 这个其实就是各加载到内存的配置字符串,解析成为对应的lua table.
1 -- 小岩<[email protected]> 2 -- 2015-5-25 3 local c = {} 4 5 c.t = 6 { 7 kv = "key-value format", 8 ini = "section-key-value format", 9 csv = "export csv format", 10 classickv = "classic key-value format", 11 } 12 13 local function lua_split(str, reps) 14 local str_list = {} 15 string.gsub(str, '[^' .. reps ..']+', function(w) 16 table.insert(str_list, w) 17 end) 18 return str_list 19 end 20 21 function c.c2t(content, type, ...) 22 if type == c.t.kv then 23 return c.c2t_kv(content, ...) 24 elseif type == c.t.ini then 25 return c.c2t_ini(content, ...) 26 elseif type == c.t.csv then 27 return c.c2t_csv(content, ...) 28 elseif type == c.t.classickv then 29 return c.c2t_kv(content) 30 end 31 return "unsupport-format" 32 end 33 34 function c.c2t_kv(content, sep, l_sep) 35 sep = sep or "=" 36 l_sep = l_sep or "\r\n" 37 38 local ret = {} 39 for _, line in ipairs(lua_split(content, l_sep)) do 40 local k,v = line:match('^([%w_]+)%s-' .. sep .. '%s-(.+)$') 41 if k then 42 if not v then 43 v = "" 44 end 45 if tonumber(v) then 46 v =tonumber(v) 47 elseif v == "true" then 48 v = true 49 elseif v == "false" then 50 v = false 51 end 52 ret[k] = v 53 end 54 end 55 return ret 56 end 57 58 function c.c2t_classickv(content) 59 return loadstring("return {" .. content .. "}")() 60 end 61 62 function c.c2t_ini(content, sl_sep, sr_sep, kv_sep, l_sep) 63 sl_sep = sl_sep or "[" 64 sr_sep = sr_sep or "]" 65 kv_sep = kv_sep or "=" 66 l_sep = l_sep or "\r\n" 67 68 local ret = {} 69 local sec 70 for _, line in ipairs(lua_split(content, l_sep)) do 71 local section = line:match('^%' .. sl_sep .. '([^%]]+)%' .. sr_sep .. '$') 72 if section then 73 sec = section 74 ret[sec] = ret[sec] or {} 75 end 76 77 local k,v = line:match('^([%w_]+)%s-' .. kv_sep .. '%s-(.+)$') 78 if k and v then 79 if tonumber(v) then 80 v = tonumber(v) 81 elseif v == "true" then 82 v = true 83 elseif v == "false" then 84 v = false 85 end 86 87 if sec then 88 ret[sec][k] = v 89 end 90 end 91 end 92 93 return ret 94 end 95 96 function c.c2t_csv(content, sep, l_sep) 97 sep = sep or "," 98 l_sep = l_sep or "\r\n" 99 100 local ct = lua_split(content, l_sep) 101 local headers = lua_split(ct[1], sep) 102 table.remove(ct, 1) 103 104 local ret = {} 105 local n = 1 106 for _, line in ipairs(ct) do 107 local vks = lua_split(line, sep) 108 ret[n] = {} 109 for j =1, #headers do 110 ret[n][headers[j]] = vks[j] 111 end 112 n = n + 1 113 end 114 115 return ret 116 end 117 118 function c.c2s(t, type, ...) 119 if type == c.t.kv then 120 return c.c2s_kv(t, ...) 121 elseif type == c.t.ini then 122 return c.c2s_ini(t, ...) 123 elseif type == c.t.csv then 124 return c.c2s_csv(t, ...) 125 elseif type == c.t.classickv then 126 return c.c2s_kv(t) 127 end 128 return "unsupport-format" 129 end 130 131 function c.c2s_kv(t, sep, l_sep) 132 sep = sep or "=" 133 l_sep = l_sep or "\r\n" 134 local content = "" 135 for k, v in pairs(t) do 136 content = content .. tostring(k) .. sep .. tostring(v) .. l_sep 137 end 138 return content 139 end 140 141 function c.c2s_ini(t, sl_sep, sr_sep, kv_sep, l_sep) 142 sl_sep = sl_sep or "[" 143 sr_sep = sr_sep or "]" 144 kv_sep = kv_sep or "=" 145 l_sep = l_sep or "\r\n" 146 147 local content = "" 148 for s, kv in pairs(t) do 149 content = content .. sl_sep .. tostring(s) .. sr_sep .. l_sep 150 for k, v in pairs(kv) do 151 content = content .. tostring(k) .. kv_sep .. tostring(v) .. l_sep 152 end 153 end 154 return content 155 end 156 157 function c.c2s_csv(t, sep, l_sep) 158 sep = sep or "," 159 l_sep = l_sep or "\r\n" 160 161 local content = "" 162 local header = "" 163 for index, kv in ipairs(t) do 164 for k, v in pairs(kv) do 165 if index == 1 then 166 header = header .. tostring(k) .. sep 167 end 168 content = content .. tostring(v) .. sep 169 end 170 content = content .. l_sep 171 end 172 173 return l_sep .. header .. l_sep .. content 174 end 175 176 return c
1 -- 小岩<[email protected]> 2 -- 2015-5-26 3 local fu = require "fw.util.fileutil" 4 local convert = require "fw.util.convert" 5 local kv = class("kv") 6 7 function kv:ctor(filename, ...) 8 self.filename_ = filename 9 self.extra_cfg_ = {...} 10 self:init_with_file() 11 end 12 13 function kv:copy() 14 return clone(self) 15 end 16 17 function kv:init_with_file() 18 assert(self.filename_ and type(self.filename_) == "string", 19 string.format("ini assert error: init_with_file() - invalid param %s", self.filename_)) 20 local file_content = fu.get_string(self.filename_) 21 self.props_ = convert.c2t(file_content, convert.t.kv, unpack(self.extra_cfg_)) 22 end 23 24 function kv:get(k) 25 assert(k and type(k) == "string", 26 string.format("ini assert error: get() - invalid param %s", k)) 27 return self.props_[k] 28 end 29 30 function kv:update(k, nv) 31 local ov = self.props_[k] 32 self.props_[k] = nv 33 return ov 34 end 35 36 function kv:save_2_path(new_filepath) 37 local old_filepath = self.filename_ 38 local content = convert.c2s(self.props_, convert.t.kv, unpack(self.extra_cfg_)) 39 if fu.write_content(new_filepath, content) then 40 self.filename_ = new_filepath 41 return old_filepath 42 end 43 return nil 44 end 45 46 function kv:save() 47 local content = convert.c2s(self.props_, convert.t.kv, unpack(self.extra_cfg_)) 48 if fu.write_content(self.filename_, content) then 49 return true 50 end 51 return false 52 end 53 54 return kv
1 --小岩<[email protected]> 2 --2015-5-26 3 local fu = require "fw.util.fileutil" 4 local convert = require "fw.util.convert" 5 local classickv = class("classickv") 6 7 function classickv:ctor(filename) 8 self.filename_ = filename 9 self:init_with_file() 10 end 11 12 function classickv:copy() 13 return clone(self) 14 end 15 16 function classickv:init_with_file() 17 assert(self.filename_ and type(self.filename_) == "string", 18 string.format("ini assert error: init_with_file() - invalid param %s", self.filename_)) 19 20 local file_content = fu.get_string(self.filename_) 21 self.props_ = convert.c2t(file_content, convert.t.classickv) 22 end 23 24 function classickv:get(k) 25 assert(k and type(k) == "string", 26 string.format("ini assert error: get() - invalid param %s", k)) 27 return self.props_[k] 28 end 29 30 function classickv:update(k, nv) 31 local ov = self.props_[k] 32 self.props_[k] = nv 33 return ov 34 end 35 36 function classickv:save_2_path(new_filepath) 37 local old_filepath = self.filename_ 38 local content = convert.c2s(self.props_, convert.t.classickv) 39 if fu.write_content(new_filepath, content) then 40 self.filename_ = new_filepath 41 return old_filepath 42 end 43 return nil 44 end 45 46 function classickv:save() 47 local content = convert.c2s(self.props_, convert.t.classickv) 48 if fu.write_content(self.filename_, content) then 49 return true 50 end 51 return false 52 end 53 54 return classickv
-- 小岩<[email protected]> -- 2015-5-25 local fu = require "fw.util.fileutil" local convert = require "fw.util.convert" local ini = class("ini") function ini:ctor(filename, ...) self.filename_ = filename or nil self.extra_cfg_ = {...} self:init_with_file() end function ini:copy() return clone(self) end function ini:init_with_file() assert(self.filename_ and type(self.filename_) == "string", string.format("ini assert error: init_with_file() - invalid param %s", self.filename_)) local file_content = fu.get_string(self.filename_) self.props_ = convert.c2t(file_content, convert.t.ini, unpack(self.extra_cfg_)) end function ini:get(s, k) assert(s and type(s) == "string", string.format("ini assert error: get() - invalid param %s", s)) assert(k and type(k) == "string", string.format("ini assert error: get() - invalid param %s", k)) if self.props_[s] then return self.props_[s][k] else return nil end end function ini:update(s,k,nv) if not self.props_[s] then self.props_[s] = {} end local ov = self.props_[s][k] self.props_[s][k] = nv return ov end function ini:save_2_path(new_filepath) local old_filepath = self.filename_ local content = convert.c2s(self.props_, convert.t.ini, unpack(self.extra_cfg_)) if fu.write_content(new_filepath, content) then self.filename_ = new_filepath return old_filepath end return nil end function ini:save() local content = convert.c2s(self.props_, convert.t.ini, unpack(self.extra_cfg_)) if fu.write_content(self.filename_, content) then return true end return false end return ini
1 -- 小岩<[email protected]> 2 -- 2015-5-25 3 local fu = require "fw.util.fileutil" 4 local convert = require "fw.util.convert" 5 local csv = class("csv") 6 7 function csv:ctor(filename, ...) 8 self.filename_ = filename 9 self.extra_cfg_ = {...} 10 self:init_with_file() 11 end 12 13 function csv:copy() 14 return clone(self) 15 end 16 17 function csv:init_with_file() 18 assert(self.filename_ and type(self.filename_) == "string", 19 string.format("ini assert error: init_with_file() - invalid param %s", self.filename_)) 20 21 local file_content = fu.get_string(self.filename_) 22 self.props_ = convert.c2t(file_content, convert.t.csv, unpack(self.extra_cfg_)) 23 end 24 25 function csv:get(n, k) 26 if self.props_[n] then 27 return self.props_[n][k] 28 else 29 return nil 30 end 31 end 32 33 function csv:update(n, k, nv) 34 if not self.props_[n] then 35 return nil 36 end 37 local ov = self.props_[n][k] 38 self.props_[n][k] = nv 39 return ov 40 end 41 42 function csv:save_2_path(new_filepath) 43 local old_filepath = self.filename_ 44 local content = convert.c2s(self.props_, convert.t.csv, unpack(self.extra_cfg_)) 45 if fu.write_content(new_filepath, content) then 46 self.filename_ = new_filepath 47 return old_filepath 48 end 49 return nil 50 end 51 52 function csv:save() 53 local content = convert.c2s(self.props_, convert.t.csv, unpack(self.extra_cfg_)) 54 if fu.write_content(self.filename_, content) then 55 return true 56 end 57 return false 58 end 59 60 return csv
我在每一个针对配置文件的操作oop实现table中都提供了从文件读取以及持久化到文件的方法.不过如果使用lua sytax方式的classic.那么在持久化的时候并不能如期的解析table,这也是当初我没有注意到的问题,不过后来我想了一下,如果是需要持久化的数据,那么我都会使用基本数据类型.所以这个不会影响我的使用.好的,另外一点需要注意的是,读取文件的接口我并没有使用lua的io接口,这是因为lua默认还是有环境变量的,不能够读取apk压缩包中的配置,所以我还是使用cc.fileutils中的接口.
好的,早上总结了一下昨天完成的内容. 接下来考虑实现那一块先.