首页 > 代码库 > cocos2d-x 3.0游戏实例学习笔记《卡牌塔防》第八部---怪物出场
cocos2d-x 3.0游戏实例学习笔记《卡牌塔防》第八部---怪物出场
/* 说明:
**1.本次游戏实例是《cocos2d-x游戏开发之旅》上的最后一个游戏,这里用3.0重写并做下笔记
**2.我也问过木头本人啦,他说:随便写,第一别完全照搬代码;第二可以说明是学习笔记---好人
**3.这里用cocos2d-x 3.0版本重写,很多地方不同,但是从重写过程中也很好的学习了cocos2d-x
*/
***每一步对应的所有代码以及用到的资源都会打包在最后给出
***为避免代码过多,每一步的代码都做了标记--一看就晓得是第几步实现的避免出错改不回去(难不成还用Git?)
***为了方便移植到手机上,对于每一步都进行编译android测试;因为很多时候代码在win32下可以,编译就会出错,给出的代码会是测试过后的。
本次笔记内容:
1,、简单的设计思路
2、代码&效果
3、下次笔记内容
4、本次代码&资源下载
一:简单的设计思路
1、怪物需要移动,若全部放到Monster类里面就太复杂啦,于是乎和前面一样,也抽离出来一个移动控制器,怪物带着一个控制器就可以按照路线行走
2、关于移动控制器,是根据之前编辑好的怪物路线---也就是那些有顺序的点来行走,那么移动控制器得用update来实现自动行走
3、英雄暂时只有一种,但是怪物有5中,也有其属性
4、每一关的怪物数量,种类也通过plist文件来配置,只不过要手动
二:代码&效果
这里先有一个移动控制器的基类:
#define CHECK_MOVE_SPEED_LVL1 0.1f //移动间隔 #define CHECK_MOVE_SPEED_LVL2 0.04f #define CHECK_MOVE_SPEED_LVL3 0.03f #define SPEED 1 class ControllerMoveBase : public Node{ public: ControllerMoveBase(); ~ControllerMoveBase(); CC_SYNTHESIZE(int,_speed,Speed); protected: Entity* _entity; //实体 bool _isMoving; //是否在移动 bool _isXLeft; //x方向是否在左移 bool _isYUp; //y方向是否在上移 int _checkMoveSpeed;//移动间隔 //根据当前Pos,以及目标点,获取下一个点坐标 Point getNextPos(Point curPos,Point destPos); };解释一下getNextPos函数,如我们编辑好的地图,两个点,(0,0) (0,500)那么最开始curPos 就是(0,0) 到达目标点之前,目标点一直都是(0,500),一秒之后走到了(0,100);那么此时curPos就是这个,然后继续按照速度计算下一个点
实现:
ControllerMoveBase::ControllerMoveBase(){ _isMoving = false; _isXLeft = false; _isYUp = false; _speed = SPEED; _checkMoveSpeed = CHECK_MOVE_SPEED_LVL2; _entity = NULL; } ControllerMoveBase::~ControllerMoveBase(){ CC_SAFE_RELEASE(_entity); } Point ControllerMoveBase::getNextPos(Point curPos,Point destPos){ //移动方向 if(curPos.x > destPos.x){ _isXLeft = true; } else{ _isXLeft = false; } if(curPos.y < destPos.y){ _isYUp = true; } else{ _isYUp = false; } //改变坐标 if(curPos.x < destPos.x && _isXLeft == false){ curPos.x += _speed; if(curPos.x > destPos.x){ curPos.x = destPos.x; } } else if(curPos.x > destPos.x && _isXLeft){ curPos.x -= _speed; if(curPos.x < destPos.x){ curPos.x = destPos.x; } } if(curPos.y < destPos.y && _isYUp == true) { curPos.y += _speed; if(curPos.y > destPos.y) { curPos.y = destPos.y; } } else if(curPos.y > destPos.y && _isYUp == false) { curPos.y -= _speed; if(curPos.y < destPos.y) { curPos.y = destPos.y; } } return curPos; }那么来一个简单的移动控制器,也就是要用到怪物身上的。h
class ControllerSimpleMove : public ControllerMoveBase{ public: ControllerSimpleMove(); ~ControllerSimpleMove(); static ControllerSimpleMove* create(Entity* entity); bool init(Entity* entity); void moveByPoslist(Vector<PosBase*> posList, int speed, int spanTime); private: Vector<PosBase*> _movePosList; //根据坐标点移动 PosBase* _curDestPos; //当前的目标点 float _moveSpan; //移动时间间隔 float _moveTimeCnt; //移动计时 //移动update void checkMoveUpdate(float delta); void nextMovePos(); };.cpp
ControllerSimpleMove::ControllerSimpleMove(){ _curDestPos = NULL; _moveTimeCnt = 0; _moveSpan = 0; } ControllerSimpleMove::~ControllerSimpleMove(){ CC_SAFE_RELEASE(_curDestPos); } ControllerSimpleMove* ControllerSimpleMove::create(Entity* entity){ ControllerSimpleMove* simpleMove = new ControllerSimpleMove(); if(simpleMove && simpleMove->init(entity)){ simpleMove->autorelease(); } else{ CC_SAFE_DELETE(simpleMove); } return simpleMove; } bool ControllerSimpleMove::init(Entity* entity){ CC_SAFE_RETAIN(entity); this->_entity = entity; this->schedule(schedule_selector(ControllerSimpleMove::checkMoveUpdate)); return true; } void ControllerSimpleMove::checkMoveUpdate(float delta){ if(_isMoving) { _moveTimeCnt += delta*1000; //移动时间到了 if(_moveTimeCnt >= _moveSpan) { _moveTimeCnt = 0; //**8**移动 if(_entity != NULL) { Point entityPos = _entity->getPosition(); Point curDestPos = _curDestPos->getPos(); //根据移动速度,来获得下一个点位置 entityPos = getNextPos(entityPos,curDestPos); _entity->setPosition(entityPos); //当走到目标点 PosBase List 中的一个点之后,就要更新目标点 if(entityPos.x == curDestPos.x && entityPos.y == curDestPos.y) { if(_movePosList.size() > 0){ nextMovePos(); } } } } } } void ControllerSimpleMove::nextMovePos(){ if( _movePosList.size() <= 0){ return; } CC_SAFE_RELEASE(_curDestPos); _curDestPos = (PosBase*)_movePosList.front(); CC_SAFE_RETAIN(_curDestPos); _movePosList.eraseObject(_curDestPos); } void ControllerSimpleMove::moveByPoslist(Vector<PosBase*> posList, int speed, int spanTime){ this->_speed = speed; this->_moveSpan = spanTime; if(posList.size() <= 0){ return; } this->_movePosList.clear(); this->_movePosList = posList; this->_isMoving = true; nextMovePos(); }总之可以先从public 成员函数moveByPosList开始看,这里才开始让_isMoving true,然后开始移动一步步移动
接触真正的怪物Monster
enum EnumMonsterPropConfType { enMonsterPropConf_ID, // 怪物ID enMonsterPropConf_Name, // 怪物名字 enMonsterPropConf_Level, // 怪物等级 enMonsterPropConf_Type, // 怪物类型 enMonsterPropConf_ModelID, // 怪物模型ID enMonsterPropConf_Defense, // 防御力 enMonsterPropConf_Hp, // 血量 enMonsterPropConf_Speed, // 移动速度 }; class Monster : public Entity{ public: Monster(); ~Monster(); static Monster* createFromCsvByID(int monsterID); bool initFromCsvByID(int monsterID); //**8**有一个移动的方法,在怪物管理器中调用让其移动 void moveByPosList(Vector<PosBase*> posList); private: CC_SYNTHESIZE(int,_level,Level); CC_SYNTHESIZE(float,_showTime,ShowTime); ControllerSimpleMove* _moveController; };这里也有一个public成员函数,moveByPosList,这是留给在怪物管理器中,让怪物开始活动的函数
Monster::Monster(){ _moveController = NULL; } Monster::~Monster(){ CC_SAFE_RELEASE(_moveController); } Monster* Monster::createFromCsvByID(int monsterID){ Monster* monster = new Monster(); if(monster && monster->initFromCsvByID(monsterID)){ monster->autorelease(); } else{ CC_SAFE_DELETE(monster); } return monster; } bool Monster::initFromCsvByID(int monsterID){ //**8**获得ID const char* chMonsterID = __String::createWithFormat("%d",monsterID)->getCString(); //**8**绑定精灵 const char* monsterSprite = __String::createWithFormat("sprite/monster/monster_%d.png",monsterID)->getCString(); Sprite* sprite = Sprite::create(monsterSprite); bindSprite(sprite); //**8**绑定移动控制器 _moveController = ControllerSimpleMove::create(this); this->addChild(_moveController); //**8**属性 CsvUtil* csvUtil = CsvUtil::getInstance(); Size csvSize = csvUtil->getFileRowColNum("csv/Monster.csv"); int line = csvUtil->findValueInWithLine(chMonsterID,enMonsterPropConf_ID,"csv/Monster.csv"); setID(monsterID); setLevel(csvUtil->getInt(line,enMonsterPropConf_Level,"csv/Monster.csv")); setModeID(csvUtil->getInt(line,enMonsterPropConf_ModelID,"csv/Monster.csv")); setDefense(csvUtil->getInt(line,enMonsterPropConf_Defense,"csv/Monster.csv")); setHP(csvUtil->getInt(line,enMonsterPropConf_Hp,"csv/Monster.csv")); setSpeed(csvUtil->getInt(line,enMonsterPropConf_Speed,"csv/Monster.csv")); return true; } void Monster::moveByPosList(Vector<PosBase*> posList){ if(posList.size() <= 0) return ; _moveController->moveByPoslist(posList,2,getSpeed());那么这里的属性什么的都和英雄差不多。。。。
只不过这里需要注意一点点问题,csv/monster.csv要稍微修改:怪物ID 从上到下改为1~5,而不是1000~1004、或者直接用本次的资源替换。
然后看看怪物管理器,通过plist文件加载怪物信息,update函数管理怪物出现
class MonsterManager : public Node{ public: MonsterManager(); ~MonsterManager(); static MonsterManager* createWithLevel(int curLevel); bool initWithLevel(int curLevel); private: //**8**有多个怪物,每一个的出来的时间递增,用showTime累加,一个个登场 float _showTime; //**8**所有的坐标点 Vector<PosBase*> _monsterPosList; //**8**没有出场的怪物,一开始所有怪物都没出来 Vector<Monster*> _notShowMonsterList; //**8**配置文件中的所有怪物 Vector<Monster*> _monsterList; //**8**在init 函数中,创建怪物 void createMonsters(int curLevel); //没有出场怪物数量 int getNotShowMonsterCnt(); //怪物的起始点 PosBase* getMonsterStartPos(); PosBase* getMonsterEndPos(); //**8**展示怪物的update函数 void showMonster(float dt); };-----------------------------实现:
MonsterManager::MonsterManager(){ _showTime = 0; } MonsterManager::~MonsterManager(){ } MonsterManager* MonsterManager::createWithLevel(int curLevel){ MonsterManager* monsterMgr = new MonsterManager(); if(monsterMgr && monsterMgr->initWithLevel(curLevel)){ monsterMgr->autorelease(); } else{ CC_SAFE_DELETE(monsterMgr); } return monsterMgr; } bool MonsterManager::initWithLevel(int curLevel){ //**8**根据关卡级别创建怪物 createMonsters(curLevel); //**8**开启update,让怪物有顺序出现 this->schedule(schedule_selector(MonsterManager::showMonster)); return true; } void MonsterManager::createMonsters(int curLevel){ //**8**加载路线坐标 __String* monsterPosPath = __String::createWithFormat("tollgate/monsterPos_level_%d.plist",curLevel); PosLoadUtil::getInstance()->loadPosWithFile(_monsterPosList,enMonsterPos,monsterPosPath->getCString(), this,10,false); //**8**读取当前关卡的怪物配置 __String* monsterConfPath = __String::createWithFormat("tollgate/monster_level_%d.plist",curLevel); auto monsterConfList = FileUtils::getInstance()->getValueVectorFromFile(monsterConfPath->getCString()); for(auto ref : monsterConfList){ auto temp_map = ref.asValueMap(); int id = temp_map.at("id").asInt(); float showTime = temp_map.at("showTime").asFloat(); if( id != 0 && showTime != 0.0f){ auto monster = Monster::createFromCsvByID(id); monster->setShowTime(showTime); monster->setVisible(false); _monsterList.pushBack(monster); _notShowMonsterList.pushBack(monster); this->addChild(monster); } } } int MonsterManager::getNotShowMonsterCnt(){ return _notShowMonsterList.size(); } PosBase* MonsterManager::getMonsterStartPos(){ return _monsterPosList.front(); } PosBase* MonsterManager::getMonsterEndPos(){ return _monsterPosList.back(); } void MonsterManager::showMonster(float dt){ int notShowMonsterCnt = _notShowMonsterList.size(); if(notShowMonsterCnt > 0){ _showTime += dt; } PosBase* monsterFirstPos = getMonsterStartPos(); //**8**把本次出场的怪物保存,然后删除,C++中不能在容器遍历的过程中删除,会出错 Vector<Monster*> temp_deletList; for(auto monster : _notShowMonsterList){ if(monster != NULL){ if(_showTime >= monster->getShowTime()){ temp_deletList.pushBack(monster); monster->setPosition(monsterFirstPos->getPos()); monster->setVisible(true); monster->moveByPosList(_monsterPosList); } } } for(auto deletMonster : temp_deletList){ _notShowMonsterList.eraseObject(deletMonster); } }这里可到后面的资源中game/中获取怪物的管理配置文件
---------------------------------------------
然后在MapLayer中添加成员,以及在init函数中把怪物管理加入
//**8** _monsterMgr = MonsterManager::createWithLevel(_curLevel); this->addChild(_monsterMgr);那么最后展示如下,怪物分组出来啦......
三:下次内容
英雄没有尽职,不攻击怪物!!!
四:
----------------------------------
资源&代码
----------------------------------
个人愚昧观点,欢迎指正与讨论
cocos2d-x 3.0游戏实例学习笔记《卡牌塔防》第八部---怪物出场