一个单机棋盘式半即时解谜RPG的开发与反思、2

接上篇

在本系列第一篇文章中,我介绍了这个项目的Json、Lua相关设计,这篇文章中将会对基础库RJCBLib进行详细介绍。我将相关的头文件给出以供参考。

RJCBLib库包含功能(2016-3-6):

GenericValue : 通用类型。

Singleton<> : 提供单例支持。

ObjectManager<> : 提供对象管理功能,继承自Sigleton<>。

JsonBuffer : 提供对Json数据的管理,结合了cocos2d中提供的RapidJson,继承自ObjectManager<>。

LuaObject : 实现了对Lua的支持,继承自此类可以使用Lua功能。

KVObject : 以Key/Value的方式管理对象属性,可以直接解析RapidJson对象。

Matrix<> : 通过矩阵的方式管理对象。

基本定义

typedef const char* const FUNC_RETURN_TYPE;
typedef const char* FUNC_RETURN_TYPE_W;
FUNC_RETURN_TYPE FUNC_RETURN_VALUE_SUCCESS = "SUCCESS";
FUNC_RETURN_TYPE FUNC_RETURN_VALUE_FAILURE = "FAILURE";
FUNC_RETURN_TYPE FUNC_RETURN_VALUE_FOUND = "FOUND";
FUNC_RETURN_TYPE FUNC_RETURN_VALUE_NOT_FOUND = "NOT FOUND";
FUNC_RETURN_TYPE FUNC_RETURN_VALUE_EXISTED = "EXISTED";

//将某个类型转换为字符串,存在效率问题,不建议在帧中使用
template<typename T>
inline std::string to_string(const T &arg) { std::stringstream ss;ss << arg; std::string t;ss >> t;return t; }

//对常用类型进行了封装,你需要提前知道这个类的实例究竟该解释为什么类型
struct GenericValue
{
    GenericValue()
    {
    }
    GenericValue(std::string String)
    {
        this->String = String;
    }
    GenericValue(int Int)
    {
        this->Int = Int;
    }
    GenericValue(bool Bool)
    {
        this->Bool = Bool;
    }
    GenericValue(double Double)
    {
        this->Double = Double;
    }
    std::string String;
    int Int;
    bool Bool;
    double Double;
};

定义了函数返回值、GenericValue是对基本类型的封装。

单例支持

通过模板实现了单例:

template<typename ClassSelfType>
class Singleton
{
public:
    static ClassSelfType* getInstance()
    {
        if (RJCBLib::Singleton<ClassSelfType>::instance == nullptr)
        {

            RJCBLib::Singleton<ClassSelfType>::instance = new ClassSelfType();
            dynamic_cast<RJCBLib::Singleton<ClassSelfType>*>(RJCBLib::Singleton<ClassSelfType>::instance)->init();
        }
        return RJCBLib::Singleton<ClassSelfType>::instance;
    }
    ~Singleton()
    {}

protected:
    Singleton()
    {}
    virtual void init()//如果被单例的类override了这个函数,那么它将会在第一次创建单例的时候被调用
    {

    }
private:

    static ClassSelfType* instance;//单例的对象指针

    template<typename classSelfType>
    class Automatic//自动释放类,待测试
    {
    private:
        Automatic() { 

        }
        ~Automatic() { 

            delete RJCBLib::Singleton<classSelfType>::instance;
        }
    };
    static Automatic<ClassSelfType> automatic;
    static int a;
};
template<typename ClassSelfType> ClassSelfType *RJCBLib::Singleton<ClassSelfType>::instance = nullptr;

如何使用:

class A : public RJCBLib::Sigleton<A>
{

};
--------------
A* instance = A::getInstance();

在继承这个模板的时候将继承类A自身传入这个模板,会为A生成一个基类Sigleton<A>A的单例被创建时会调用虚函数Sigleton<A>::init,可以在A中重写这个虚函数以实现初始化的相关工作。Automatic内部类实现了单例的自动释放。

对象管理

继承自Sigleton<>cocos2d::Map<>

template<typename ClassSelfType, typename KeyType, typename ValueType_MustBePointerToRef>
class ObjectManager : private cocos2d::Map<KeyType, ValueType_MustBePointerToRef>, public RJCBLib::Singleton<ClassSelfType>
{
public:
    //获取被管理KV的数量
    SSIZE_T size()
    {
        return Map<KeyType, ValueType_MustBePointerToRef>::size();
    }
    //清空所有KV
    RJCBLib::FUNC_RETURN_TYPE clear()
    {
        Map<KeyType, ValueType_MustBePointerToRef>::clear();
        return RJCBLib::FUNC_RETURN_VALUE_SUCCESS;
    }
    //插入一个KV,K应未被使用过
    RJCBLib::FUNC_RETURN_TYPE insert(KeyType key, ValueType_MustBePointerToRef value)
    {
        if (this->has(key) == RJCBLib::FUNC_RETURN_VALUE_FOUND)
        {
            return RJCBLib::FUNC_RETURN_VALUE_EXISTED;
        }
        Map<KeyType, ValueType_MustBePointerToRef>::insert(key, value);
        return RJCBLib::FUNC_RETURN_VALUE_SUCCESS;
    }
    //获取指定K的V
    RJCBLib::FUNC_RETURN_TYPE get(KeyType key, ValueType_MustBePointerToRef &outValue)
    {
        auto ite = Map<KeyType, ValueType_MustBePointerToRef>::find(key);
        if (ite == Map<KeyType, ValueType_MustBePointerToRef>::end())
        {
            outValue = nullptr;
            return RJCBLib::FUNC_RETURN_VALUE_NOT_FOUND;
        }
        outValue = (*ite).second;
        return RJCBLib::FUNC_RETURN_VALUE_SUCCESS;
    }
    //移除指定K所对应的KV
    RJCBLib::FUNC_RETURN_TYPE remove(KeyType key)
    {
        if (Map<KeyType, ValueType_MustBePointerToRef>::find(key) == Map<KeyType, ValueType_MustBePointerToRef>::end())
        {
            return RJCBLib::FUNC_RETURN_VALUE_NOT_FOUND;
        }
        Map<KeyType, ValueType_MustBePointerToRef>::erase(key);
        return RJCBLib::FUNC_RETURN_VALUE_SUCCESS;
    }
    //是否包含某个K
    RJCBLib::FUNC_RETURN_TYPE has(KeyType key)
    {
        if (Map<KeyType, ValueType_MustBePointerToRef>::find(key) == Map<KeyType, ValueType_MustBePointerToRef>::end())
        {
            return RJCBLib::FUNC_RETURN_VALUE_NOT_FOUND;
        }
        return RJCBLib::FUNC_RETURN_VALUE_FOUND;
    }
    //返回cocos2d::Map的begin迭代器
    auto begin()
    {
        return Map<KeyType, ValueType_MustBePointerToRef>::begin();
    }
    //返回cocos2d::Map的end迭代器
    auto end()
    {
        return Map<KeyType, ValueType_MustBePointerToRef>::end();
    }

    virtual ~ObjectManager() {}
protected:
    ObjectManager() {}
};

如何使用:

class A : public RJCBLib::ObjectManager<A,std::string,std::string>
{
};
------------
把它当做单例的Map用吧。

A继承自ObjectManager,传入三个模板类型:A自身(因为继承自Sigleton<>)、Key类型、被管理者类型。实际上A就是一个单例的Map。

Json数据管理

继承自ObjectManager<>。

class JsonBufferDocumentBox : public cocos2d::Ref
{
public:
    static JsonBufferDocumentBox *create();
    rapidjson::Document& operator*();
    rapidjson::Document* operator->();
private:
    JsonBufferDocumentBox();
    ~JsonBufferDocumentBox();
    rapidjson::Document* document;
};

class JsonBuffer : public RJCBLib::ObjectManager<JsonBuffer,std::string, JsonBufferDocumentBox*>
{   
public:
    JsonBuffer();
    FUNC_RETURN_TYPE parseFile(std::string key, std::string filePath);
    FUNC_RETURN_TYPE parseString(std::string key, std::string data);
};

辅助类JsonBufferDocumentBox封装了rapidjson::Document,因为它继承自cocos2d::ref,所以可以对它进行内存管理,这也是cocos2d::Map<>要求的。
JsonBuffer继承自ObjectManager<>,提供了两个函数分别解析字符串和文件,解析后的内容和可以通过基类ObjectManager<JsonBuffer,KeyType,ValueType>的方法获取。

Lua支持

我没有使用cocos2d自带的Lua功能而是根据需要自己实现了Lua接口相关的工作。尚有很多不足(如线程安全)有待修改,也请读者多提宝贵意见。

#define MAKE_LUA_FUNCTION_NAME(_LUA_ID) (std::string("UpdateFunction")+_LUA_ID)
class LuaObject
{
private:
    //唯一标识的id,在lua虚拟机中不同的对象会通过这个id区别
    static unsigned int _lua_id;
    //是否已被初始化
    bool isInited;
    //
    std::string realFunctionName;
    //生成新的LuaID
    inline unsigned int generateLuaID()
    {
        ++_lua_id;
        return _lua_id;
    }
    //当前活动的LuaObject,因为会有许多LuaObject,这个变量用于和Lua虚拟机交互
    static LuaObject * currentLuaObject;
protected:
    LuaObject();
    virtual ~LuaObject();

    //override这个函数,返回脚本文件路径
    virtual std::string getLuaScriptFilePath() = 0;

    //文件中Update函数的名字
    virtual std::string getUpdateFunctionName() = 0;

    //override这个函数,lua虚拟机查询String时会被调用
    virtual std::string LuaObjectGetString(std::string key) = 0;

    //override这个函数,lua虚拟机查询Int时会被调用
    virtual int LuaObjectGetInt(std::string key) = 0;

    //override这个函数,lua虚拟机查询Double时会被调用
    virtual double LuaObjectGetDouble(std::string key) = 0;

    //override这个函数,lua虚拟机查询Bool时会被调用
    virtual bool LuaObjectGetBool(std::string key) = 0;

    //override这个函数,lua虚拟机设置String时会被调用
    virtual void LuaObjectSetString(std::string key, std::string value) = 0;

    //override这个函数,lua虚拟机设置Int时会被调用
    virtual void LuaObjectSetInt(std::string key, int value) = 0;

    //override这个函数,lua虚拟机设置Bool时会被调用
    virtual void LuaObjectSetBool(std::string key, bool value) = 0;

    //override这个函数,lua虚拟机设置Double时会被调用
    virtual void LuaObjectSetDouble(std::string key, double value) = 0;

    //虚拟机回调的日志函数
    static int Log(lua_State *L);

    //虚拟机回调的获取String函数
    static int StringGet(lua_State *L);

    //虚拟机回调的获取Int函数
    static int IntGet(lua_State *L);

    //虚拟机回调的获取Bool函数
    static int BoolGet(lua_State *L);

    //虚拟机回调的获取Double函数
    static int DoubleGet(lua_State *L);

    //虚拟机回调的设置String函数
    static int StringSet(lua_State *L);

    //虚拟机回调的设置Bool函数
    static int IntSet(lua_State *L);

    //虚拟机回调的设置Bool函数
    static int BoolSet(lua_State *L);

    //虚拟机回调的设置Double函数
    static int DoubleSet(lua_State *L);

    /*
    初始化这个LuaObject
    :
    注册虚拟机回调函数(static *Set, static *Get,static Log)
    执行脚本文件
    重命名update函数(使其在虚拟机中全局唯一)
    */
    void initLuaObject();
public:
    static cocos2d::LuaEngine *luaEngine;

    //外部发起lua事件调用时应调用这个函数,state会被传入虚拟机(通过 IN_STATE__ 获取),可以override这个函数来扩展功能
    virtual void Do(std::string state);

    //设置虚拟机的全局String
    void setGlobalString(std::string key, std::string value);

    //设置虚拟机的全局Int
    void setGlobalInt(std::string key, int value);

    //设置虚拟机的全局Bool
    void setGlobalBool(std::string key, bool value);

    //设置虚拟机的全局Double
    void setGlobalDouble(std::string key, double value);
};

如何使用:

class A : public RJCBLib::LuaObject
{
private:
    virtual std::string getLuaScriptFilePath() override;
    virtual std::string getUpdateFunctionName() override;
    virtual std::string LuaObjectGetString(std::string key) override;
    virtual int LuaObjectGetInt(std::string key) override;
    virtual bool LuaObjectGetBool(std::string key) override;
    virtual double LuaObjectGetDouble(std::string key) override;
    virtual void LuaObjectSetString(std::string key, std::string value) override;
    virtual void LuaObjectSetInt(std::string key, int value) override;
    virtual void LuaObjectSetBool(std::string key, bool value) override;
    virtual void LuaObjectSetDouble(std::string key, double value) override;
};

LuaObject作为基类使用,也是和Lua脚本交互的接口。每个对象对应一个回调函数(如Update),在不同事件触发时对其传入不同字符串,这样脚本就可以进行判断。脚本通过一些注册的全局函数用来和LuaObject交互,在执行脚本时LuaObject也会维护静态的当前LuaObject实例,这样就可以知道脚本获取的属性是哪个是哪个实例的。
A继承自LuaObjectoverride了一些方法:

获取Lua脚本路径
std::string getLuaScriptFilePath()

获取回调函数名
std::string getUpdateFunctionName()

返回属性给Lua脚本
std::string LuaObjectGetString(std::string key)
int LuaObjectGetInt(std::string key)
bool LuaObjectGetBool(std::string key)
double LuaObjectGetDouble(std::string key)

Lua脚本设置属性到这个类
void LuaObjectSetString(std::string key, std::string value)
void LuaObjectSetInt(std::string key, int value)
void LuaObjectSetBool(std::string key, bool value)
void LuaObjectSetDouble(std::string key, double value)

在Lua中使用这些全局函数:

获取当前对象属性
rjcblib_getstring
rjcblib_getint
rjcblib_getbool
rjcblib_getdouble

设置当前对象属性
rjcblib_setstring
rjcblib_setint
rjcblib_setbool
rjcblib_setdouble

通过cocos2d打印日志
rjcblib_log

KVObject

这个类通过std::map<>管理属性,提供灵活的属性变更能力,能够直接将Json数据解析。

class KVObject
{
public:
    std::map<std::string, int> *intKV;
    std::map<std::string, std::string> *stringKV;
    std::map<std::string, bool> *boolKV;
    std::map<std::string, double> *doubleKV;
    KVObject();
    ~KVObject();

    //查询是否包含KEY,T - 支持类型
    template<typename T>
    bool hasValue(std::string key)
    {
        do
        {
            if (typeid(T) == typeid(std::string))
            {
                if (stringKV->find(key) == stringKV->end())
                    break;
            }
            if (typeid(T) == typeid(int))
            {
                if (intKV->find(key) == intKV->end())
                    break;
            }
            if (typeid(T) == typeid(bool))
            {
                if (boolKV->find(key) == boolKV->end())
                    break;

            }
            if (typeid(T) == typeid(double))
            {
                if (doubleKV->find(key) == doubleKV->end())
                    break;
            }
            return true;
        } while (false);

        return false;
    }

    //直接获取值,KEY应是存在的,T - 支持类型
    template<typename T>
    void getValueDirect(std::string key, GenericValue& out)
    {
        if (typeid(T) == typeid(std::string))
        {
            out.String = stringKV->at(key);
        }
        if (typeid(T) == typeid(int))
        {
            out.Int = intKV->at(key);
        }
        if (typeid(T) == typeid(bool))
        {
            out.Bool = boolKV->at(key);
        }
        if (typeid(T) == typeid(double))
        {
            out.Double = doubleKV->at(key);
        }
    }

    //获取值,检测KEY是否存在,T - 支持类型
    template<typename T>
    FUNC_RETURN_TYPE getValue(std::string key,GenericValue & out)
    {

        auto hasvalue = hasValue<T>(key);
        if (!hasvalue)
            return FUNC_RETURN_VALUE_NOT_FOUND;
        getValueDirect<T>(key, out);
        return FUNC_RETURN_VALUE_SUCCESS;
    }

    //直接设置值,KEY应是存在的, T - 支持类型
    template<typename T>
    void setValueDirect(std::string key, const GenericValue& in)
    {
        if (typeid(T) == typeid(std::string))
        {
            stringKV->at(key) = in.String;
        }
        if (typeid(T) == typeid(int))
        {
            intKV->at(key) = in.Int;
        }
        if (typeid(T) == typeid(bool))
        {
            boolKV->at(key) = in.Bool;
        }
        if (typeid(T) == typeid(double))
        {
            doubleKV->at(key) = in.Double;
        }
    }

    //直接插入值,KEY应是不存在的, T - 支持类型
    template<typename T>
    void insertValueDirect(std::string key, const GenericValue& in)
    {
        if (typeid(T) == typeid(std::string))
        {
            stringKV->insert(std::pair<std::string, std::string>(key, in.String));
        }
        if (typeid(T) == typeid(int))
        {
            intKV->insert(std::pair<std::string,int>(key,in.Int));
        }
        if (typeid(T) == typeid(bool))
        {
            boolKV->insert(std::pair<std::string,bool>(key,in.Bool));
        }
        if (typeid(T) == typeid(double))
        {
            doubleKV->insert(std::pair<std::string,double>(key,in.Double));
        }
    }

    //设置值,检测KEY是否存在, T - 支持类型
    template<typename T>
    FUNC_RETURN_TYPE setValue(std::string key, const GenericValue& in)
    {
        auto ret = hasValue<T>(key);
        if (!ret)
            insertValueDirect<T>(key, in);
        else
            setValueDirect<T>(key, in);
        return FUNC_RETURN_VALUE_SUCCESS;
    }

    /*
    解析RapidjsonGenericValue
    RapidjsonGenericValueSelfType - RapidjsonGenericValue的类型
    示例:
    obj.parseRapidjsonGenericValue<decltype(arg)>(arg)
    */
    template<typename RapidjsonGenericValueSelfType>
    void parseRapidjsonGenericValue(RapidjsonGenericValueSelfType &gv)
    {
        if (!gv.IsObject())
        {
            return;
        }

        for (auto ite = gv.MemberBegin();ite != gv.MemberEnd();ite++)
        {
            if (ite->value.IsInt())
            {
                intKV->insert(std::pair<std::string, int>(ite->name.GetString(), ite->value.GetInt()));
            }
            if (ite->value.IsString())
            {
                stringKV->insert(std::pair<std::string,std::string>(ite->name.GetString(), ite->value.GetString()));
            }
            if (ite->value.IsBool())
            {
                boolKV->insert(std::pair<std::string,bool>(ite->name.GetString(), ite->value.GetBool()));
            }
            if (ite->value.IsDouble())
            {
                doubleKV->insert(std::pair<std::string, double>(ite->name.GetString(), ite->value.GetDouble()));
            }
        }
    }
};

如何使用:

这个类目前(2016-3-6)支持`int`、`bool`、`double`、`std::string`类型的属性管理,存取方法需要传入对应的类型作为模板参数如`instance.getValue<int>(key,out)`,相关方法在代码中均有说明。

矩阵对象管理

通过矩阵的方式管理二维对象:

template<typename DataType_MustBeTypeOnRef>
class Matrix
{
private:
    Matrix() :data(nullptr), height(0), width(0)
    {

    }
    DataType_MustBeTypeOnRef*** data;
public:
    int height;
    int width;
    static Matrix<DataType_MustBeTypeOnRef>* create()
    {
        auto t = new Matrix<DataType_MustBeTypeOnRef>();
        return t;
    }

    bool init(int height, int width)
    {
        this->height = height;
        this->width = width;
        data = new DataType_MustBeTypeOnRef**[height];
        for (int y = 0;y < height;y++)
        {
            data[y] = new DataType_MustBeTypeOnRef*[width];
            for (int x = 0;x < width;x++)
            {
                data[y][x] = nullptr;
            }
        }
        return true;
    }
    inline void set(int y, int x, DataType_MustBeTypeOnRef* data)
    {
        this->data[y][x] = data;
        data->retain();
    }
    inline DataType_MustBeTypeOnRef* get(int y, int x)
    {
        return this->data[y][x];
    }
    inline void remove(int y, int x)
    {
        if (this->data[y][x] != nullptr)
        {
            this->data[y][x]->release();
            this->data[y][x] = nullptr;
        }
    }
    virtual ~Matrix()
    {
        if (data != nullptr)
        {
            for (int y = 0;y < height;y++)
            {
                for (int x = 0;x < width;x++)
                {
                    remove(y, x);
                }
                delete[] data[y];
                data[y] = nullptr;
            }
            delete[] data;
            data = nullptr;
        }
    }
};

如何使用:

Matrix<Object>* matrix = Matrix<Object>::create();
matrix.init(10,10);
set..
get..
remove..
delete matrix;

注意Object类型须继承自cocos2d::refMatrix<>会进行内存管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值