Cocos2d-x3.0游戏实例之《别救我》第九篇——从tmx文件中加载关卡怪物

2023-11-11 15:50

本文主要是介绍Cocos2d-x3.0游戏实例之《别救我》第九篇——从tmx文件中加载关卡怪物,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


上一篇我们已经制作好tg1.tmx文件了,现在就要使用它了。

 

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

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

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

 

很抱歉,我们又要新建2个类了,我已经尽力少新建类了,毕竟是教程,类越多越容易混乱。

 

我们要新建一个Monster类,以及一个MonsterLayer类,专门添加Monster对象。

 

Monster类

来看看Monster.h文件:

 
  1. #ifndef Monster_H
  2. #define Monster_H
  3.  
  4. #include "cocos2d.h"
  5. USING_NS_CC;
  6.  
  7. class Monster : public Node 
  8. {
  9. public:
  10. Monster();
  11. ~Monster();
  12.     static Monster* create(int ID);
  13.     bool init(int ID);
  14.  
  15.     /* 加入到当前场景的物理世界 */
  16.     void joinToWorld(Node* parent);
  17.  
  18.     CC_SYNTHESIZE(int, m_ID, ID);
  19.     CC_SYNTHESIZE(float, m_fShowTime, fShowTime);
  20.     CC_SYNTHESIZE(int, m_iPosX, iPosX);
  21.     CC_SYNTHESIZE(int, m_iPosY, iPosY);
  22.     CC_SYNTHESIZE(int, m_iAtk, iAtk);
  23.     CC_SYNTHESIZE(Value, m_sModelPath, sModelPath);
  24. };
  25.  
  26. #endif

很简单,有几个成员变量:

1. m_ID

2. m_fShowTime就是记录什么时候出场

3. m_sModelPath就是怪物的图片资源名称,Value是3.0的新特性,可以理解为CCString、CCInteger等的合体

4. m_iPosX和m_iPosY是坐标不解释(小若:已经解释了~)~

5. m_iAtk就是怪物的攻击力

 

主要关注一下joinToWorld函数,这个函数是让Monster加入到物理世界的,为什么不创建Monster对象的时候就让它加入到物理世界呢?

原因很简单,因为我们的怪物不是一开始就出现的,是随着游戏时间慢慢出现的,所以不能一开始就加入到世界中。

 

然后,来看看Monster.cpp文件:

 
  1. #include "Monster.h"
  2. #include "scene\ObjectTag.h"
  3. Monster::Monster()
  4. {
  5. }
  6.  
  7. Monster::~Monster()
  8. {
  9. }
  10.  
  11. Monster* Monster::create(int ID)
  12. {
  13.     auto monster = new Monster();
  14.     
  15.     if(monster && monster->init(ID)) 
  16.     {
  17.     monster->autorelease();
  18.     }
  19.     else
  20.     {
  21.     CC_SAFE_DELETE(monster);
  22.     }
  23.     
  24.     return monster;
  25. }
  26.  
  27. bool Monster::init(int ID)
  28. {
  29.     this->m_ID = ID;
  30.  
  31.     /* 这里以后要改成读取Json配置文件,暂时写死 */
  32.     if (ID == 1)
  33.     {
  34.         m_sModelPath = Value("item2.png");
  35. m_iAtk = 1;
  36.     }
  37.     else if (ID == 2)
  38.     {
  39.         m_sModelPath = Value("heart_red.png");
  40. m_iAtk = -1;
  41.     }
  42.     return true;
  43. }

 

为了别一下子太复杂,joinToWorld函数我暂时没有贴出来。

这里主要看看init函数,Monster对象是根据ID来创建的,不同的ID代表不同的怪物类型,目前我们只有两种类型。

 

因为还没讲解到Json的部分,所以init函数暂时写死,但是大概意思是一样的:根据怪物ID加载不同的配置。

 

好,再来看看joinToWorld函数:

 
  1. void Monster::joinToWorld(Node* parent)
  2. {
  3.     Sprite* sp = Sprite::createWithSpriteFrameName(m_sModelPath.asString().c_str());
  4.  
  5.     /* 创建刚体 */
  6.     PhysicsBody* body = PhysicsBody::createBox(sp->getContentSize());
  7.     body->setCategoryBitmask(1);    // 0001
  8.     body->setCollisionBitmask(1);   // 0001
  9.     body->setContactTestBitmask(1); // 0001
  10.  
  11.     /* 精灵居中 */
  12.     sp->setPosition(Point(sp->getContentSize().width * 0.5f, sp->getContentSize().height * 0.5f));
  13.  
  14.     /* 精灵作为Monster的表现,添加到Monster身上 */
  15.     this->addChild(sp);
  16.  
  17.     /* 设置怪物Tag类型 */
  18.     this->setTag(ObjectTag_Monster);
  19.  
  20.     /* 精灵作为Monster的表现,Monster本身没有大小,所以要设置一下大小 */
  21.     this->setContentSize(sp->getContentSize());
  22.  
  23.     /* 刚体添加到Monster本身,而不是精灵身上 */
  24.     this->setPhysicsBody(body);
  25.  
  26.     /* 设置坐标 */
  27.     this->setPosition(Point(getiPosX(), getiPosY()));
  28.  
  29.     /* Monster加入到物理世界 */
  30.     parent->addChild(this);
  31.  
  32. }

代码注释已经够详细的了,大概的意思就是:

1. 创建一个Sprite对象,用于表现Monster的样子

2. 创建一个刚体,用于做物理碰撞

3. 设置怪物Tag类型

4. 将刚体添加到Monster上

5. Monster根据自身属性进行设置(比如设置坐标,完整版还有出场音效、颜色之类的)

6. 将Monster添加到物理世界

 

MonsterLayer 怪物层

为了不要让TollgateScene的代码太过臃肿(虽然它已经很臃肿了),我不得不再创建一个MonsterLayer类。

 

看看MonsterLayer.h文件:

 
  1. #ifndef MonsterLayer_H
  2. #define MonsterLayer_H
  3.  
  4. #include "cocos2d.h"
  5. USING_NS_CC;
  6.  
  7. class Monster;
  8. class MonsterLayer : public Layer 
  9. {
  10. public:
  11. MonsterLayer();
  12. ~MonsterLayer();
  13. CREATE_FUNC(MonsterLayer);
  14. virtual bool init();
  15.  
  16.     void logic(float dt);
  17. private:
  18.     /* 从配置文件中加载怪物数据 */
  19.     void loadMonstersFromTmxConf();
  20.  
  21.     /* 存放所有即将要出场的怪物对象 */
  22.     Vector<Monster*> m_monsterVec;
  23.  
  24.     /* 计时器 */
  25.     float m_fTimeCounter;
  26. };
  27.  
  28. #endif

噗,好简单,你不用解释了~

哦,好吧,既然如此,我就不解释了。

(小若:喂,别在那里自己表演对白好吗?!谁说不用解释的?)

 

好吧,大概的意思的是:

1. loadMonstersFromTmxConf函数,很明显就是从我们生成的TiledMap关卡文件里读取数据,然后创建Monster对象了

2. m_monsterVec,这就是一个列表,用来保存读取到的所有怪物对象。而Vector也是3.0的新特性,封装了C++的Vector容器

3. m_fTimeCounter是计时器,因为怪物是达到某个时间才会出现的~

 

然后再来看看MonsterLayer.cpp文件:

 
  1. #include "MonsterLayer.h"
  2. #include "entity\Monster.h"
  3.  
  4. MonsterLayer::MonsterLayer()
  5. {
  6.     m_fTimeCounter = 0;
  7. }
  8.  
  9. MonsterLayer::~MonsterLayer()
  10. {
  11.    SpriteFrameCache::getInstance()->removeSpriteFramesFromFile("monster.plist");
  1. }
  2.  
  3. bool MonsterLayer::init()
  4. {
  5.     if (!Layer::init())
  6.     {
  7.         return false;
  8.     }
  9.     SpriteFrameCache::getInstance()->addSpriteFramesWithFile("monster.plist");
  10.     /* 加载关卡的怪物配置 */
  11.     loadMonstersFromTmxConf();
  12.     return true;
  13. }

好,好简单~

(小若:啊才怪啊,少了两个函数别以为我不知道)

 

OK,刚刚贴出来的一看就知道什么意思了,我就不说了,我们来看看loadMonstersFromTmxConf函数是怎么实现的:

 
  1. void MonsterLayer::loadMonstersFromTmxConf()
  2. {
  3.     /* 加载地图 */
  4.     TMXTiledMap* map = TMXTiledMap::create("tg1.tmx");
  5.     this->addChild(map);
  6.  
  7.     /* 加载怪物对象层的所有对象 */
  8.     TMXObjectGroup* objGroup = map->getObjectGroup("monster");
  9.     ValueVector objects = objGroup->getObjects();
  10.  
  11.     /* 遍历所有对象 */
  12.     for (const auto v : objects)
  13.     {
  14.         const ValueMap dict = v.asValueMap();
  15.  
  16.         int id = dict.at("id").asInt();
  17.         float fShowTime = dict.at("showTime").asFloat();
  18.         int iPosX = dict.at("x").asInt();
  19.         int iPosY = dict.at("y").asInt();
  20.  
  21.         /* 创建怪物对象,并保存起来 */
  22.         Monster* monster = Monster::create(id);
  23.         monster->setID(id);
  24.         monster->setfShowTime(fShowTime);
  25.         monster->setiPosX(iPosX);
  26.         monster->setiPosY(iPosY);
  27.  
  28.         /* 保存怪物对象 */
  29.         m_monsterVec.pushBack(monster);
  30.     }
  31. }

我对自己的注释还是比较有信心的,所以,我再讲解一下(小若:好有自信= =)

1. 首先,加载TiledMap地图对象(TMXTiledMap)

2. 然后获取对象层(TMXObjectGroup)也就是我们之前一直大做文章的monster对象层了

3. 从monster对象层中获取所有的对象列表,也就是我们之前画的那一堆矩形

4. 每一个矩形对象取出来之后就是一个ValueMap,这也是3.0封装好的,功能和map一样(当然,用法有点点区别)

5. 从ValueMap中取出所有的属性,然后创建一个Monter对象,给Monster对象赋值

6. 把Monster对象添加到m_monsterVec列表,保存好

7. 大功告成

 

这个函数完成之后,我们就得到了所有的怪物对象,这些怪物对象都已经跟进关卡配置文件赋值好了。

怎么样,很简单吧~

 

最后,我们看看MonsterLayer的logic函数,重点中的重点(小若:算了吧你,重点这个词被你用烂了好吧)

 
  1. void MonsterLayer::logic(float dt)
  2. {
  3.     /* 计时 */
  4.     m_fTimeCounter += dt;
  5.  
  6.     /* 记录本次出场的怪物 */
  7.     Vector<Monster*> deleteVec;
  8.  
  9.     /* 让达到出场时间的怪物添加到物理世界 */
  10.     for (auto monster : m_monsterVec)
  11.     {
  12.         /* 达到时间,可以出场了 */
  13.         if (m_fTimeCounter >= monster->getfShowTime())
  14.         {
  15.             monster->joinToWorld(this);
  16.  
  17.             /* 记录本次出场的怪物,然后删除掉 */
  18.             deleteVec.pushBack(monster);
  19.         }
  20.     }
  21.  
  22.     /* 删除已经添加到物理世界的怪物,避免重复出场 */
  23.     for (auto monster : deleteVec)
  24.     {
  25.         m_monsterVec.eraseObject(monster, false);
  26.     }
  27. }

虽然我对自己的注释很有自信,但是,我还是解释一下:

1. m_fTimeCounter计时器累计时间

2. 遍历怪物列表,判断有没有哪只怪物已经达到出场时间,可以出场的

3. 调用可出场怪物的joinToWorld函数,这样怪物就会出现在游戏中了

4. 把已经出场过的怪物对象从m_monsterVec中删除,避免下次又重复出场

 

完成喇~

木了个头的,真是没想到,就这么简单的几个类,竟然要花这么长时间讲解。

而且,本篇竟然还不是最后一篇~!(小若:那你在这么发表什么获奖感言啊)

 

我们来试试加载怪物有没有成功吧~

 

同样的,给TollgateScene添加一个成员变量(我就不贴详细的代码了):

 
  1. MonsterLayer* m_monsterLayer;

 

然后修改TollgateScene的scene函数,把MonsterLayer添加到scene里:

 
  1. Scene* TollgateScene::scene()
  2. {
  3.     auto scene = Scene::createWithPhysics();
  4.  
  5.     /* 这里省略了很多代码 */
  6.  
  7.     /* 背景层 */
  8.     auto backgroundLayer = BackgroundLayer::create();
  9.     scene->addChild(backgroundLayer, 0);
  10.  
  11.     /* 怪物层 */
  12.     auto monsterLayer = MonsterLayer::create();
  13.     scene->addChild(monsterLayer, 1);
  14.  
  15.     auto layer = TollgateScene::create();
  16.     scene->addChild(layer, 10);
  17.  
  18.     layer->m_backgroundLayer = backgroundLayer;
  19.     layer->m_monsterLayer = monsterLayer;
  20.     return scene;
  21. }

 

最后,别忘了在TollgateScene的logic里调用MonsterLayer的logic函数:

 
  1. void TollgateScene::logic(float dt)
  2. {
  3.     m_backgroundLayer->logic(dt);
  4.     m_monsterLayer->logic(dt);
  5. }

 

OK,运行游戏,我们看看怪物出现没?

 

Cocos2d-x3.0游戏实例之《别救我》第九篇

Cocos2d-x3.0游戏实例之《别救我》第九篇

怪物会根据我们配置的时间出现。

 

OK,到此,《别救我》的核心功能算是完成了,但是它完全还不能算是一个游戏。

 

下一篇,我们再介绍一个核心功能——读取Json配置文件

 

这篇关于Cocos2d-x3.0游戏实例之《别救我》第九篇——从tmx文件中加载关卡怪物的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/391172

相关文章

最好用的WPF加载动画功能

《最好用的WPF加载动画功能》当开发应用程序时,提供良好的用户体验(UX)是至关重要的,加载动画作为一种有效的沟通工具,它不仅能告知用户系统正在工作,还能够通过视觉上的吸引力来增强整体用户体验,本文给... 目录前言需求分析高级用法综合案例总结最后前言当开发应用程序时,提供良好的用户体验(UX)是至关重要

Oracle Expdp按条件导出指定表数据的方法实例

《OracleExpdp按条件导出指定表数据的方法实例》:本文主要介绍Oracle的expdp数据泵方式导出特定机构和时间范围的数据,并通过parfile文件进行条件限制和配置,文中通过代码介绍... 目录1.场景描述 2.方案分析3.实验验证 3.1 parfile文件3.2 expdp命令导出4.总结

MySQL的索引失效的原因实例及解决方案

《MySQL的索引失效的原因实例及解决方案》这篇文章主要讨论了MySQL索引失效的常见原因及其解决方案,它涵盖了数据类型不匹配、隐式转换、函数或表达式、范围查询、LIKE查询、OR条件、全表扫描、索引... 目录1. 数据类型不匹配2. 隐式转换3. 函数或表达式4. 范围查询之后的列5. like 查询6

MyBatis延迟加载的处理方案

《MyBatis延迟加载的处理方案》MyBatis支持延迟加载(LazyLoading),允许在需要数据时才从数据库加载,而不是在查询结果第一次返回时就立即加载所有数据,延迟加载的核心思想是,将关联对... 目录MyBATis如何处理延迟加载?延迟加载的原理1. 开启延迟加载2. 延迟加载的配置2.1 使用

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

Python开发围棋游戏的实例代码(实现全部功能)

《Python开发围棋游戏的实例代码(实现全部功能)》围棋是一种古老而复杂的策略棋类游戏,起源于中国,已有超过2500年的历史,本文介绍了如何用Python开发一个简单的围棋游戏,实例代码涵盖了游戏的... 目录1. 围棋游戏概述1.1 游戏规则1.2 游戏设计思路2. 环境准备3. 创建棋盘3.1 棋盘类

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

国产游戏崛起:技术革新与文化自信的双重推动

近年来,国产游戏行业发展迅猛,技术水平和作品质量均得到了显著提升。特别是以《黑神话:悟空》为代表的一系列优秀作品,成功打破了过去中国游戏市场以手游和网游为主的局限,向全球玩家展示了中国在单机游戏领域的实力与潜力。随着中国开发者在画面渲染、物理引擎、AI 技术和服务器架构等方面取得了显著进展,国产游戏正逐步赢得国际市场的认可。然而,面对全球游戏行业的激烈竞争,国产游戏技术依然面临诸多挑战,未来的

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数