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" ...
随机推荐
- 《JavaWeb从入门到改行》fileupload,没毛病
目录: » fileupload API > 文件上传的要求 > fileupload组件 » 上传细节的代码演示 » 项目案例-上传头像并显示 fileupload API 文 ...
- CF17E Palisection
题意 给定一个长度为n的小写字母串.问你有多少对相交的回文子串(包含也算相交) 相交的回文子串个数 \(mod\ 51123987\) Sol 求相交的回文子串不太好求 考虑用总数减去不相交的回文串个 ...
- css3+javascript实现翻页幻灯片
先上效果图 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <tit ...
- CentOS6.5(3)----设置自己安装的程序开机自动启动
CentOS6.5系统下设置自己安装的程序开机自动启动 方法1. 把启动程序的命令添加到 /etc/rc.d/rc.local 文件中,比如设置开机启动 mysqld: #!/bin/sh # # T ...
- Leetcode算法比赛---- Lexicographical Numbers
问题描述 Given an integer n, return 1 - n in lexicographical order. For example, given 13, return: [1,10 ...
- mysql性能问题小解 Converting HEAP to MyIsam create_myisa
安定北京被性能测试困扰了N天,实在没想法去解决了,今天又收到上级的命令说安定北京要解决,无奈!把项目组唯一的DBA辞掉了,现在所以数据库的问题都得自己来处理:( 不知道上边人怎么想的.而且更不知道怎安 ...
- Android 图文混排 通过webview实现并实现点击图片
在一个开源项目看到是用的webview 实现的 1. 这是在asset中的一个模板html <html> <head> <title>News Detail< ...
- Word 最后一页无法删除-解决办法
Word 最后一页无法删除-解决办法 制服 word 最后一页无法删除 今天在做一个简历的时候,编辑 word 文档的时候,最后一页空白页怎么也删不掉,百度了很多方法之后,只有一个可行,记录一下. 1 ...
- 关于App的cpu/内存/流量 /电量的方法(GT工具)
https://mp.weixin.qq.com/s?__biz=MzUzNTQxMzMzMg==&mid=2247484376&idx=1&sn=651e9cf22801b5 ...
- [翻译] ZFTokenField
ZFTokenField 本人视频教程系类 iOS中CALayer的使用 效果图: iOS custom view that let you add token view inside like ...