Cocos2d-x3.0游戏实例之《别救我》第十篇——用Json配置各类型怪物数据

笨木头  2014-05-2 21:09   3.0游戏实例《别救我》,Cocos2d-x,Cocos2d-x3.0   阅读(12,691)   51条评论

 

 

现在我们有2种类型的怪物,而且创建的时候是写死在代码里的,这是要作死的节奏~

所以,必须可配置,不然会累死人的。

 

笨木头花心贡献,啥?花心?不呢,是用心~

转载请注明,原文地址: http://www.benmutou.com/archives/949 

文章来源:笨木头与游戏开发

 

Json文件

什么是Json文件?说白了,它就是一个文本文档,只不过它的内容是按照一定的规则填写的。

于是,我们就可以按照那个规则去读取这份文档。

这,就是配置文件产生的缘由(才怪)。

(小若:才怪是什么意思啊喂!)

 

我不知道配置文件产生的历史,但它的原理就是这样的~

 

关于Json的资料,百度很多,我坦白,我自己也没有深入地完整地使用过Json的所有东西。所以,大家感兴趣的,可以百度一下。

 

怪物类型配置文件

我们在这个游戏要用到的Json格式主要是数组(array)和对象(object),来我们在项目的Resources目录下创建一个新文件,命名为:monsterConf.plist

 

然后随便用一个文本编辑器打开,输入以下内容:

[

{“id”:1, “atk”:1, “model”:”item2.png”} // 1.普通,-1HP

,{“id”:2, “atk”:-1, “model”:”heart_red.png”}// 2.爱心,+1HP

]

很明显,由符号“[]”框起来的就是数组,数组里面用“{}”包围起来的,就是对象,对象之间有逗号分隔。

 

里面就是我们的怪物配置,这里我们只填写id、atk、model三个属性,完整版的《别救我》里面是有9个属性的。

 

导入Json库

要想在程序中读写Json文件,我们就用别人已经写好的库,说库太高深了,就是一些h和cpp文件。

我使用的是JsonCpp库,大家可以到这里下载:http://download.csdn.net/detail/musicvs/7392931

 

下载好后,解压json文件夹到项目的Classes目录下,然后就像导入代码文件一样导入到项目中就可以了。如图:

Cocos2d-x3.0游戏实例之《别救我》第十篇——用Json配置各类型怪物数据

Cocos2d-x3.0游戏实例之《别救我》第十篇——用Json配置各类型怪物数据

Cocos2d-x3.0游戏实例之《别救我》第十篇——用Json配置各类型怪物数据

Cocos2d-x3.0游戏实例之《别救我》第十篇——用Json配置各类型怪物数据

在程序中读取Json配置文件

好,这次真的是最后一步了,我们修改Monster的init函数:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
bool Monster::init(int ID)

{

    this->m_ID = ID;

 

    /* ----------- 读取怪物配置文件 ------------ */

 

    /* 读取配置文件字符串数据 */

    std::string sData = FileUtils::getInstance()->getStringFromFile("monsterConf.plist");

 

    /* 用于解析Json */

    Json::Reader reader;

    

    /* 解析Json后的根节点 */

    Json::Value root;

 

    /* 开始解析 */

    if (!reader.parse(sData, root, false))

    {

        return false;

    }

 

    /* 在这里,根节点是一个数组,遍历数组,找到我们要的ID */

    int size = root.size();

    for (int i = 0; i < size; ++i)

    {

        int id = root[i]["id"].asInt();

 

        if (id == ID)

        {

            m_sModelPath = root[i]["model"].asCString();

            break;

        }

    }

 

    return true;

}

有点小复杂,没关系,你们自己理解理解就好了…啊才怪啊,怎么可能!(小若:抢我台词O_O)

 

解释如下:

1. 首先,用FileUtils的getStringFromFile函数可以把文件读取成字符串格式,Json解析需要用到

2. 然后用Json库的Reader类可以解析Json文件

3. 解析之后,根节点赋值给root,通过root可以找到其他子节点

4. 因为我们配置文件里根节点就是一个数组,我们是用数组包围了怪物的配置数据的,没忘记吧?看看配置文件就知道了,整个数据是用“[]”符号包围的~

5. 然后遍历root节点,就像数组一样遍历

6. 比如root[0],那就是第一条怪物配置数据,然后通过root[0][“id”]就能找到这条数据的id字段的值

7. 找到我们ID配置的数据,然后获取它的所有字段,目前我们要用到的只有model字段而已。

 

OK,别忘了,要使用Json库的类,把头文件也在Monster顶部加上:


1
2
3
#include "json\writer.h"

#include "json\reader.h"

 

然后,编译运行,效果和之前是一样的,证明我们已经成功。

 

PS:有人反馈Json库编译报一堆错误,试试以下操作:

项目属性,【C/C++】->【常规】->【附加包含目录】,看看”..Classes”这一条有没有加上去,并且是不是在第一条,把它放在第一条试试。

 

额外的说明

也许大家要喷了,各种疑问:

1. 为什么读取一个配置文件就这么复杂,这么多部分,那不是很麻烦?

答:因为这是实例教程,不想涉及太多的东西,所以我就尽量没有去封装了,在完整版里我是封装了一个JsonData类的,解析Json文件只需简单的一、两句话。

2. 每创建一个怪物对象,就要这样解析一次,岂不是很浪费效率?

答:效率浪不浪费我就没有去试验了,但是实际开发中肯定不会这样做的。在完整版的《别救我》里,我是预先加载好怪物的配置文件,然后作为一个个对象(MonsterConfig)缓存起来,要使用的时候,直接根据怪物ID获取就可以了。不会每次都去解析Json。

 

总之,一切从简,点到即止,否则我这教程不知道得写到什么时候了。

 

最后,主角和怪物的碰撞

最后完善一个功能点,主角和怪物的碰撞,很简单,不需要加东西。

 

OK,运行游戏,然后我已经把怪物全都撞没了~如图:

Cocos2d-x3.0游戏实例之《别救我》第十篇——用Json配置各类型怪物数据

Cocos2d-x3.0游戏实例之《别救我》第十篇——用Json配置各类型怪物数据

(小若:等等!刚刚发生了什么事情?)

 

刚刚大家被我弄昏了(邪恶),我们回到刚刚的时间…

 

最后完善一个功能点,主角和怪物的碰撞,很简单,不需要加东西。

 

修改一下TollgateScene的onContactBegin函数:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
bool TollgateScene::onContactBegin(PhysicsContact& contact)

{

    /* 这里省略了很多代码 */

 

    /* 碰撞到边缘锯齿(墙),+1血 */

    if (other->getTag() == ObjectTag_Border)

    {

        /* 扣-1血,就相当于加1血 */

        player->beAtked(-1);

 

        log("player cur HP:%d", player->getiHP());

    }

    else if (other->getTag() == ObjectTag_Monster)

    {

        /* 碰撞到怪物,根据怪物的属性进行操作 */

        Monster* monster = (Monster*)other;

 

        if (monster->getiAtk() != 0)

        {

            /* 攻击玩家(可能是加血) */

            player->beAtked(monster->getiAtk());

 

            /* 碰撞之后,怪物消失 */

            monster->removeFromParent();

        }

    }

    return true;

}

我们新增了一个else if分支,判断如果other是怪物的话,那就对玩家进行攻击(或者加血),碰撞怪物之后,怪物就消失。

 

OK,现在运行游戏,我们就可以用主角碰撞怪物,然后怪物消失。

同时主角的HP会不断变化(从日志中查看)。

 

在《别救我》完整版里,玩家的任务是让主角的血量为0,所以是不能碰撞那些会加血的怪物的。

而且怪物也不是碰一下就消失,有些要碰好几下。

总之,所以的这一切都能通过修改怪物的配置文件来实现,给怪物加属性配置字段,我们就能实现多种多样的功能。

 

同时,还能通过修改TiledMap关卡配置文件,给矩形对象添加更多属性,比如添加怪物出场时的推力属性,这样怪物出场的时候就会往某个方向喷射。(于是连飞机大战都能做出来,所以这个就靠想象力了)

我给大家看一个完整版的关卡配置:

Cocos2d-x3.0游戏实例之《别救我》第十篇——用Json配置各类型怪物数据

Cocos2d-x3.0游戏实例之《别救我》第十篇——用Json配置各类型怪物数据

蓝色的怪物在出场的时候是会往上飞的,看起来就像是飞机的子弹,很有意思的配置吧~

 

OK,《别救我》所有的核心功能已经完成,剩下的,就是各种开场动画、菜单、UI、关卡了。

记得把物理引擎的调试模式关掉,不然会一直有很多红色的线。

 

结束语

好了,《别救我》简化版游戏实例教程,到此结束。

希望多多少少能帮到大家。

 

如果对完整版感兴趣的,可以到我的淘宝店购买完整版源码,这是对我最好的物质支持~地址:果断要看看

 

如果对完整版不感兴趣的,又想支持我的(哎呀,大家太热情了我也不想的(害羞)),可以点击我博客右上方的的“支付宝捐赠”,随意一下~

虽然捐赠不能让我成为土豪,但是它是对我最好的精神支持~

 

 

51 评论

  1. Pingback: free skype download
  2. Pingback: java download
  3. Pingback: download firefox
  4. Pingback: Vanessa Smith
  5. 有个问题一直想问下,为什么不直接用#pragma once而用#ifndef…. #endif 这种呢?

  6. 另外想说说的是完整的 主界面上的 “Quit” 是否去掉返回功能了?我在EndScene, MainMenuScene下断, 按这个按钮都没反应。

  7. 请教下,为什么,我导入这个JSON后,会出现85个错误,全部指向了json_reader.cpp json_writer.cpp这两个文件,之后就想是不是还有什么目录要加入,之后参照完整版,在 附加包含目录 加入了 $(EngineRoot)externaljson依旧不行。请教是什么原因?(为毛就感觉我遇到特别多问题Σ(っ °Д °;)っ)

    1. 之前还有人出现100多个错误。。。你算人品好了= =这个我真心无解,我就是这么导入的。。。好几个项目都没问题….externaljson那个自带的json目录是另外一个Json库,也可以用,用法稍微不太一样~

      1. 刚刚测试了几百个回合,我终于试出了报85个错误的情况了~你看看的项目属性,【C/C++】->【常规】->【附加包含目录】,看看”..Classes”这一条有没有加上去,并且是不是在第一条,把它放在第一条试试。

        1. 非常感谢,终于可以了,要是没有这几篇教程,还有这一问一答,单看源码估计也不容易走啊。话说能否也设个守护裙子OL的讨论地方呢?

  8. cocos2d-x自带了一个简易的json文件解释器#include “json/document.h”那部分代码可以修改为: string sData = FileUtils::getInstance()->getStringFromFile(“monsterConf.json”); rapidjson::Document reader; reader.Parse<0>(sData.c_str()); int size = reader.Size(); for (int i = 0; i < size; i++) { int id = reader [“id”].GetInt(); if (id == ID) { m_iAtk = reader [“atk”].GetInt(); m_sModelPath = reader [“model”].GetString(); break; } }木头,你看看这个是不是更方便点?大家随便分享一下

    1. rapidjson也是一个第三方的Json库,其实是Cocostudio在用的,效率比JsonCpp高~ 但是API用起来没那么方便,如果Json文件比较复杂庞大,那应该用它比较好,反正封装一下用起来都差不多

  9. 看了教程之后,我手一发抖,把完整源码拍下来了。 木有兄,赶快到淘宝确认吧。

          1. 木头凶,你代码完整版怎么没proj.android目录啊?我需要你的完整代码是包含proj.android目录,并且是你直接编译发布到应用商店的完整代码。可以重新发一份完整代码给我吗?

            1. Android工程当然没有喇,VS工程里已经是完整代码+资源了~我卖的代码一般都不包含Android和IOS工程Android的编译依赖于你的环境,你的Android环境搭好了,移植什么项目都是一样的,否则我直接丢个项目给你,也是编译不了的

              1. 你卖的代码没说明不包含Android工程,而且你说明你的游戏已移植到Android版本,我才认为有android工程的 。如果你不发Android工程给我,这个完整代码对我来说没有多少参考价值了。 我买你的代码就是为了参考Android工程。至于依赖编译环境的问题,如果搭建不好那是我自己的问题。我希望木头凶能把Android工程的代码再发给我。

                1. “代码平台:Windows VS2013”,不是写着么 你还没听懂我的意思。。。你把Classes和Resources的东西丢到一个Android工程里,编译,就是Android版本了。。。你是要我发什么给你= = 我是用3.0RC1版本写的,然后发布了。后来才整理了这个系列的教程,然后把RC1的代码弄成了正式版。3.0正式版的完整版我没有Android工程,懒得编译

  10. 当程序运行到,这个函数m_fTimeCounter变量时候,void MonsterLayer::logic(float dt){ /* 计时 */ m_fTimeCounter += dt; /* 记录本次出场的怪物 */ Vector deleteVec; /* 让达到出场时间的怪物添加到物理世界 */ for (auto monster : m_monsterVec) { /* 达到时间,可以出场了 */ if (m_fTimeCounter >= monster->getfShowTime()) { monster->joinToWorld(this); /* 记录本次出场的怪物,然后删除掉 */ deleteVec.pushBack(monster); } } /* 删除已经添加到物理世界的怪物,避免重复出场 */ for (auto monster : deleteVec) { m_monsterVec.eraseObject(monster, false); }}就报错:0x012FF915 处的第一机会异常(在 DontSaveMe.exe 中): 0xC0000005: 读取位置 0xCDCDCFD9 时发生访问冲突。0x012FF915 处有未经处理的异常(在 DontSaveMe.exe 中): 0xC0000005: 读取位置 0xCDCDCFD9 时发生访问冲突。m_fTimeCounter <无法读取内存>这是什么原因?

    1. 没,Json和XML都行(加载效率方面我就忽略了,我也没实验过)~我以前(以及限制)还用CSV呢~对比起来,我感觉Json可读性比CSV要差很多,特别是字段多的时候,毕竟CSV是表格形式,看着爽~但是扩展起来,Json(或者XML)强悍一些~

发表评论

电子邮件地址不会被公开。 必填项已用*标注