cocos2d-x开发: 一切应该从配置文件读取开始
想要做一款完整的游戏,应该从配置文件读取开始。cocos2d-x本身提供了UserDefault来操作xml格式的配置文件,准确的说配置这模块引擎开发者已经考虑到了.但是xml格式包含大量无关的格式信息,对于开发者直接使用编辑器操作也不是很友好.我期望的配置文件应该具备两个特点,第一:格式简单.这是我一贯的*nix风格的要求,我喜欢只管的#注释以及key-value的配置方式.可能唯一需要考虑的是这样简单的配置方式会不会因为业务扩展而显得功能不足.这一点是可以放心的,那些都是业务逻辑相关的,选择就会很多.至少现在不是应该考虑的范畴.第二:对开发者友好.什么叫做友好? 说白了,按照我的理解就是很容易可以定位配置信息的位置,容易修改,对编辑器依赖不高.我们随便用个notepad++/subl之类的文本编辑器就可以直接阅读和修改.
好了,基于上面的考虑,ini格式无疑是比较好的选择.我最终选用了ini格式作为配置文件首选.所以就写了一个c++类来解析和操作ini配置.代码如下:
#ifndef __xy_INICache_INCLUDE__
#define __xy_INICache_INCLUDE__
#include <map>
#include <vector>
#include <string>
namespace xy
{
class INICache
{
public:
INICache();
virtual ~INICache();
static INICache* createFromFile(const std::string& filePath);
static INICache* createFromStream(const std::stringstream& sstream);
bool loadFromFile(const std::string& filePath);
bool loadFromStream(const std::stringstream& sstream);
void flush(const std::string& filePath);
std::string getFilePath() const { return this->M_filePath_; }
bool isSectionExist(const std::string& section) const;
bool deleteSection(const std::string& section);
void getSectionNames(std::vector<std::string>& sectionVector);
void setString(const std::string& section, const std::string& key, const std::string& value);
std::string getString(const std::string& section, const std::string& key, const std::string& defaultValue);
void setInteger(const std::string& section, const std::string& key, int value);
int getInteger(const std::string& section, const std::string& key, int defaultValue);
void setDouble(const std::string& section, const std::string& key, double value, int pre);
double getDouble(const std::string& section, const std::string& key, double defaultValue);
void setBoolean(const std::string& section, const std::string& key, bool value);
bool getBoolean(const std::string& section, const std::string& key, bool defalutValue);
protected:
void trimString(std::string& buffer, const std::string& trim, bool isAll);
void parseLine(const std::string& buffer);
std::string parseSection(const std::string& buffer, const std::string& leftTag, const std::string& rightTag);
bool stringToBoolean(const std::string& buffer, bool defaultValue); typedef std::map<std::string, std::string> KVtype;
typedef std::map<std::string, KVtype> SKVtype; std::string M_filePath_;
std::string M_currentSection_;
SKVtype propsMap_;
};
}
#endif//__xy_INICache_INCLUDE__
实现部分的代码如下,如果需要看的,可以自行查看.
#include "INICache.h"
#include <fstream>
#include <sstream>
namespace xy
{
INICache::INICache()
{
}
INICache::~INICache()
{
if(!this->propsMap_.empty()) { this->propsMap_.clear(); }
}
INICache* INICache::createFromFile(const std::string& filePath)
{
INICache* pINICache = new INICache;
if(!pINICache->loadFromFile(filePath))
{
delete pINICache;
return NULL;
}
return pINICache;
}
bool INICache::loadFromFile(const std::string& filePath)
{
std::ifstream fin(filePath.c_str());
if(!fin.is_open()) { return false; } std::string lineBuffer;
while(!fin.eof())
{
std::getline(fin, lineBuffer);
this->trimString(lineBuffer, std::string(" "), true);
this->parseLine(lineBuffer);
}
fin.close();
this->M_filePath_ = filePath;
return true;
}
INICache* INICache::createFromStream(const std::stringstream& sstream)
{
INICache* pINICache = new INICache;
if(!pINICache->loadFromStream(sstream))
{
delete pINICache;
return NULL;
}
return pINICache;
}
bool INICache::loadFromStream(const std::stringstream& sstream)
{
std::string lineBuffer;
std::istringstream isstream(sstream.str());
while(!isstream.eof())
{
std::getline(isstream, lineBuffer);
this->trimString(lineBuffer, std::string(" "), true);
this->parseLine(lineBuffer);
}
return true;
}
void INICache::flush(const std::string& filePath)
{
std::ofstream fout(filePath.c_str());
if(!fout.is_open()) { return; }
std::string buffer;
SKVtype::iterator skv_iter;
KVtype::iterator kv_iter;
for(skv_iter = this->propsMap_.begin(); skv_iter != this->propsMap_.end(); ++skv_iter)
{
fout << std::endl;
fout << std::string("[") << skv_iter->first << std::string("]") << std::endl;
for(kv_iter = skv_iter->second.begin(); kv_iter != skv_iter->second.end(); ++kv_iter)
{
fout << kv_iter->first << std::string("=") << kv_iter->second << std::endl;
}
}
fout.close();
}
bool INICache::isSectionExist(const std::string& section) const
{ return (this->propsMap_.find(section) != this->propsMap_.end()); }
bool INICache::deleteSection(const std::string& section)
{
if(!section.empty()) { return false; }
SKVtype::iterator skv_iter = this->propsMap_.find(section);
if(skv_iter != this->propsMap_.end())
{
this->propsMap_.erase(skv_iter);
return true;
}
return false;
}
void INICache::getSectionNames(std::vector<std::string>& sectionVector)
{
if(!sectionVector.empty()) { sectionVector.clear(); }
SKVtype::iterator skv_iter = this->propsMap_.begin();
for(; skv_iter != this->propsMap_.end(); ++skv_iter)
{ sectionVector.push_back(skv_iter->first); }
}
void INICache::setString(const std::string& section, const std::string& key, const std::string& value)
{
if(!section.empty() && !key.empty())
{ this->propsMap_[section][key] = value; }
}
std::string INICache::getString(const std::string& section, const std::string& key, const std::string& defaultValue)
{
if(!section.empty() && !key.empty())
{
SKVtype::iterator skv_iter = this->propsMap_.find(section);
if(skv_iter != this->propsMap_.end())
{
KVtype::iterator kv_iter = skv_iter->second.find(key);
if(kv_iter != skv_iter->second.end())
{ return kv_iter->second; }
}
}
return defaultValue;
}
void INICache::setInteger(const std::string& section, const std::string& key, int value)
{
std::stringstream sstream;
sstream << value;
this->setString(section, key, sstream.str());
}
int INICache::getInteger(const std::string& section, const std::string& key, int defaultValue)
{
std::string tmp = this->getString(section, key, std::string(""));
std::stringstream sstream;
sstream << tmp;
sstream >> defaultValue;
return defaultValue;
}
void INICache::setDouble(const std::string& section, const std::string& key, double value, int pre)
{
std::stringstream sstream;
if(pre) { sstream.precision(pre); }
sstream << value;
this->setString(section, key, sstream.str());
}
double INICache::getDouble(const std::string& section, const std::string& key, double defaultValue)
{
std::string tmp = this->getString(section, key, std::string(""));
std::stringstream sstream;
if(!tmp.empty())
{
sstream << tmp;
sstream >> defaultValue;
}
return defaultValue;
}
void INICache::setBoolean(const std::string& section, const std::string& key, bool value)
{ this->setInteger(section, key, value ? :); }
bool INICache::getBoolean(const std::string& section, const std::string& key, bool defaultValue)
{
std::string tmp = this->getString(section, key, std::string(""));
if(!tmp.empty()) { return this->stringToBoolean(tmp, defaultValue); }
return defaultValue;
}
void INICache::trimString(std::string& buffer, const std::string& trim, bool isAll)
{
if(buffer.empty()) { return; }
while(buffer.find(trim) == )
{
buffer.erase(, trim.length());
if(!isAll) { break; }
}
while(!buffer.empty() && (buffer.rfind(trim) == (buffer.length() - trim.length())))
{
buffer.erase(buffer.length() - trim.length(), trim.length());
if(!isAll) { break; }
}
}
void INICache::parseLine(const std::string& buffer)
{
if(buffer.empty()) { return; }
switch (buffer[])
{
case '#':
case '%':
return;
case '[':
{
std::string section = this->parseSection(buffer, std::string("["), std::string("]"));
this->trimString(section, std::string(" "), true);
if(!section.empty()) { this->M_currentSection_ = section; }
}
return;
default:
{
if(buffer.find(std::string("=")) != std::string::npos && !this->M_currentSection_.empty())
{
std::string key = this->parseSection(buffer, std::string(""), std::string("="));
this->trimString(key, std::string(" "), true);
std::string value = this->parseSection(buffer, std::string("="), std::string(""));
this->trimString(value, std::string(" "), true);
if(!key.empty()) { this->propsMap_[this->M_currentSection_][key] = value; }
}
}
return;
}
}
std::string INICache::parseSection(const std::string& buffer, const std::string& leftTag, const std::string& rightTag)
{
std::string ret;
if(!buffer.empty())
{
std::string::size_type pos_begin = , pos_end = ;
if(!leftTag.empty())
{
pos_begin = buffer.find(leftTag);
if(pos_begin == std::string::npos) { return ret; }
pos_begin += leftTag.size();
} else { pos_begin = ; }
if(!rightTag.empty())
{
pos_end = buffer.find(rightTag, pos_begin);
if(pos_end == std::string::npos) { return ret; }
ret = buffer.substr(pos_begin, pos_end - pos_begin);
} else { ret = buffer.substr(pos_begin, std::string::npos); }
}
return ret;
}
bool INICache::stringToBoolean(const std::string& buffer, bool defaultValue)
{
if(buffer.empty()) { return defaultValue; }
std::stringstream sstream;
sstream << buffer;
int tmp = ;
sstream >> tmp;
return (buffer.compare(std::string("true"))== || tmp > );
}
}
INICache.cc
这里主要是说一下我做的这些接口的思路. 读取涉及到的get/set这些方法,是基本需求.提供了两个读取Ini buffer的方法,一个是loadFromFile(const std::string& filePath),这个接口主要是应对客户端从本地读取配置文件信息,而loadFromStream(const std::stringstream& sstream)接口则是作为预留,从buffer直接读取Ini配置,为什么会做这样一个接口的预留呢? 可以一起看一下getSectionNames这个接口方法,返回的是key值的集合.
我是这样考虑的,c++这部分的接口尽量的简单,实现基本的功能,尽量避开业务逻辑的操作.要什么,我就提供基本接口,除非是在lua那边实现效率不高,或者是复杂,才会考虑提供完整的功能.因为紧接着下面会做更新模块的功能,这部分至少会依赖读取配置中一条关于资源版本的信息.而从远程Http服务器下载资源的时候也还是需要做很多版本的检测处理,如果是增量更新的话,需要按照版本的先后顺序依次下载.这就是要对key值进行按照需求排序. 本来在c++这部分做很简单的,我只要用std::sort函数,提供一个compare函数就好了。可是这部分是和具体的更新业务逻辑关联的,所以我不应该在c++这部分实现,所以就只是提供给你获取集合的方法.然后自己去处理.
绑定这部分就没什么好说的了. getSectionNames loadFromStream createFromStream这三个函数接口需要skip掉,结合前面的更新需求,需要的话,另外绑定接口,传入lua callback,在C++这边调用getSectionNames接口就好了,具体细节在后面写到更新的时候自然就会写出来了.我写了一个测试:
---------------------------------------------------------------------
-- @Author 小岩
-- @Created on 2014-12-26 21:29
-- @Brief INI解析测试
---------------------------------------------------------------------
INICacheTestCase = class("INICacheTestCase", TestsCase)
-----------------------------------------------------------
-- 测试解析INI文件
function INICacheTestCase:run()
local iniCache = xy.INICache:createFromFile("src/Tests/INICache/INICache_conf.ini")
if iniCache == nil then
Logger.Error(" cannot get local conf file! ")
end
Logger.Info("load file succ!")
Logger.Info("%s", iniCache:getString("Section", "Key", "Failed"))
Logger.Info("%d", iniCache:getInteger("Section", "Integer", "-1"))
Logger.Info("%f", iniCache:getDouble("Section", "Double", "-1"))
local boolean = iniCache:getBoolean("Section", "Boolean", false)
if boolean == true then
Logger.Info("%s", "boolean == true")
else
Logger.Info("%s", "boolean == false")
end
end
---------------------------------------------------------------------
-- End Of Lua File
---------------------------------------------------------------------
需要注意的问题是,INICache读取文件后,将配置文件信息一直都是保存在map中的,所以不要在不同的地方对同一份配置文件做多次读取操作,这样的话,将配置再次持久化到设备的时候,配置信息就会错掉.所以最好是提供配置的单例操作方式.
cocos2d-x开发: 一切应该从配置文件读取开始的更多相关文章
- [spring源码学习]二、IOC源码——配置文件读取
一.环境准备 对于学习源码来讲,拿到一大堆的代码,脑袋里肯定是嗡嗡的,所以从代码实例进行跟踪调试未尝不是一种好的办法,此处,我们准备了一个小例子: package com.zjl; public cl ...
- C 构造一个 简单配置文件读取库
前言 最近看到这篇文章, json引擎性能对比报告 http://www.oschina.net/news/61942/cpp-json-compare?utm_source=tuicool 感觉技术 ...
- C# 配置文件读取与修改(转)
C# 配置文件读取与修改 配置文件在很多情况下都使用到, 配置文件分为两种 一种是应用程序的配置文件, 一种是web的配置文件. 两种配置文件最大的区别是web的配置文件更新之后会实时更新, 应用 ...
- Aooms_微服务基础开发平台实战_003_配置文件与简单的web环境搭建
一.前言 本篇文章介绍两个重点 (1) 工程核心配置文件application.yml (2) 如何在一个标准的的SpringCloud工程上构建起一个基本的web结构 二.配置文件applicati ...
- Java配置文件读取中文乱码问题
背景 这是我之前在做的用友服务对接开发,昨天领导拿给财务测试时告诉我有乱码,当时我第一想法是用友那边的编码格式有问题,因为还在做其他任务,我说等问一下用友他们用的什么编码格式我们这边改一下,然后今天早 ...
- ASP.Net Core 5.0 MVC 配置文件读取,Startup 类中ConfigureServices 方法、Configure 方法的使用
配置文件读取 1. 新建FirstController控制器 在appsettings文件内容替换成以下代码 { "Position": { "Title": ...
- VS2012中,C# 配置文件读取 + C#多个工程共享共有变量 + 整理using语句
(一) C# 配置文件读取 C#工程可以自动生成配置文件,以便整个工程可以使用设置的配置进行后续的处理工作. 1. 首先,右键工程文件-->Properties -->settings-- ...
- C# 配置文件读取与修改
C# 配置文件读取与修改 配置文件在很多情况下都使用到, 配置文件分为两种 一种是应用程序的配置文件, 一种是web的配置文件. 两种配置文件最大的区别是web的配置文件更新之后会实时更新, 应用 ...
- java Spring使用配置文件读取jdbc.properties
Spring使用配置文件读取jdbc.properties 在beans.xml中加入两个必须的bean [html]<bean id="propertyConfigurer" ...
随机推荐
- Mac(OS X)中Git安装与GitHub基本使用
GitHub是一个面向开源及私有软件项目的托管平台.开源代码库以及版本控制系统,因为只支持 Git 作为唯一的版本库格式进行托管,故名 GitHub.通常在Windows下使用GitHub的教程是非常 ...
- Angular中EventEmitter不是泛型类型
今天在做angular发射数据时,报错:EventEmitter不是泛型类型, 当时第一点就想到是不是没有引入,一看引入了 后面看api文档发现引入错了,其实是引入下面的 可能是之前快捷输入时,IDE ...
- BZOJ2960:跨平面
题面 BZOJ Sol 对该平面图的对偶图建图后就是最小树形图,建一个超级点向每个点连 \(inf\) 边即可 怎么转成对偶图,怎么弄出多边形 把边拆成两条有向边,分别挂在两个点上 每个点的出边按角度 ...
- 教程:让你的表单升级到CSS3和HTML5客户端验证
今天我们一起来看看如何创建一个实用并且功能强大的表单,表单使用如今最热门的技术HTML5和css3来创建,并且可以通过HTML5进行客户端验证. 查看预览下载附件 第一步:策划表单功能 首先,我们得为 ...
- React 入门实例教程[阮一峰的网络日志] (分享)
作者: 阮一峰 https://github.com/ruanyf/react-demos 转自:http://www.ruanyifeng.com/blog/2015/03/react.html 对 ...
- Angular js部分关键字的理解
模板:动态模板,是动态的,直接去处理DOM的,而不是通过处理字符串模版(静态模板) mvc:核心思想实现"数据管理-数据模型Model.应用逻辑-控制器Controller.数据表现-视图V ...
- Ubuntu 安装 PhpMyAdmin 图文教程
Ubuntu 安装 PhpMyAdmin 管理 MySQL 数据库 PhpMyAdmin 是一个用 PHP 编写的软件工具,可以通过 web方式控制和操作 MySQL 数据库.通过 phpMyAdmi ...
- 《APP移动终端决胜之道视觉设计艺术》学习笔记
1.20-2.9 1.合理的层级化2.信息的整合(短信收发件箱),信息的整合就像创建文件夹,可以将相关的东西放在一起,以便于使用者搜索与查找3.(微信聊天界面)相比之下使用了对话框图形的界面,元素更加 ...
- js API列表
// 主要是ES的API和一小部分浏览器的API. // 新加入标准的API有可能是浏览器事实上早已实现的. // ECMAScript目前是每年都会发布新版本(目前已经相对稳定,每年都会又增 ...
- django从1.7升级到1.9后 提示:RemovedInDjango110Warning
Django项目,把django从1.7升级到1.9后,大量报错.需要做如下修改. 1,修改urls.py: 在django1.9里,urls的配置不再支持字符串型的路由.需要先import,然后直接 ...