在游戏中,很多怪物本身是会移动的,这里主要有蘑菇怪,乌龟等。
说起怪物的移动,首先在游戏里先要考虑怪物的抽象和设计。
在CMMonster.h中,有个类CMMonsterBasic,这个类抽象了所有的怪物,具体的怪物都是它的派生类,比如CMMonsterMushrooms蘑菇怪。
CMMonsterBasic继承自CCNode和CMSender,所以怪物都是渲染对象,并且有消息发送功能。
怪物基本类的接口:
virtual bool init(CCPoint ptMonsterPos,CMMario *pMario,CMGameMap *pGameMap,CMReceiver *pReceiver); 初始化
virtual bool OnCollisionMario() = 0; 与马里奥相撞处理
virtual void Dead(enMonsterDeadType DeadType); 怪物死亡
virtual bool OnCallPerFrame(float fT); 帧刷新定时调用
void MonsterTurn(); 怪物转向,比如蘑菇怪遇到阻挡它的墙时,它会转向
怪物类的基本数据
CMMario *m_pMario; 马里奥对象
CMGameMap *m_pGameMap; 地图对象
enMoveDirection m_MoveDirection; 移动方向
bool m_bIsActivation; 是否激活
float m_fDropSpeedPlus; 掉落加速度
bool m_bIsTouched; 是否相撞
怪物的创建:
当游戏开始时,根据tmx地图中的标记,创建怪物对象并放置到响应位置,具体代码在CMGameMap的init函数中
创建完怪物之后,将其加入到数组中,这些怪物刚刚创建时候,都是不激活的,也就是不会动的
怪物的激活和移动:
触发怪物的激活的原理在于判断怪物是不是已经进入视图,如果没有进入视图,那么怪物不需要动。但是判断是否进入视图还是很麻烦的,所以在马里奥程序里,是用马里奥和怪物的距离,来触发怪物的激活的。
怪物的激活和移动都是在virtual bool OnCallPerFrame(float fT); 函数中,这个函数是CMGameMap的帧更新函数调用过来的,也就是说每次帧更新,每个怪物都会响应这个函数。在这个函数里,负责激活和移动,以及判断怪物和马里奥的碰撞。
下面我们以蘑菇怪的帧更新函数来学习怪物帧更新处理。
bool CMMonsterMushrooms::OnCallPerFrame( float fT ) { do { //是否激活 if (m_bIsActivation==true) { //移动与碰撞 if (m_MoveDirection == enMoveLeft) { //用怪物左方的2个瓦片来判断移动碰撞 CCSprite* pTileSpriteLeftTop = m_pGameMap->TileMapLayerPosToTileSprite(ccp(getPositionX(),getPositionY()+getContentSize().height)); CCSprite* pTileSpriteLeftMid = m_pGameMap->TileMapLayerPosToTileSprite(ccp(getPositionX(),getPositionY()+getContentSize().height/2)); if (pTileSpriteLeftTop!=NULL || pTileSpriteLeftMid!=NULL) { m_MoveDirection = enMoveRight; } else { setPositionX(getPositionX()-1); } } else if(m_MoveDirection == enMoveRight) { //用怪物右方的2个瓦片来判断移动碰撞 CCSprite* pTileSpriteRightTop = m_pGameMap->TileMapLayerPosToTileSprite(ccp(getPositionX()+getContentSize().width,getPositionY()+getContentSize().height)); CCSprite* pTileSpriteRightMid = m_pGameMap->TileMapLayerPosToTileSprite(ccp(getPositionX()+getContentSize().width,getPositionY()+getContentSize().height/2)); if (pTileSpriteRightTop!=NULL || pTileSpriteRightMid!=NULL) { m_MoveDirection = enMoveLeft; } else { setPositionX(getPositionX()+1); } } //用怪物下方的三个瓦片来判断掉落碰撞 CCSprite* pTileSpriteBottomMid = m_pGameMap->TileMapLayerPosToTileSprite(ccp(getPositionX()+boundingBox().size.width/2,getPositionY())); CCSprite* pTileSpriteBottomLeft = m_pGameMap->TileMapLayerPosToTileSprite(ccp(getPositionX()+COLLISION_POS_ADJUSTMENT,getPositionY())); CCSprite* pTileSpriteBottomRight = m_pGameMap->TileMapLayerPosToTileSprite(ccp(getPositionX()+boundingBox().size.width-COLLISION_POS_ADJUSTMENT,getPositionY())); if (pTileSpriteBottomLeft!=NULL || pTileSpriteBottomMid!=NULL || pTileSpriteBottomRight!=NULL) { //掉落速度归零 m_fDropSpeedPlus = 0; } else { setPositionY(getPositionY()-m_fDropSpeedPlus); //掉落加速度 m_fDropSpeedPlus += DROP_SPEED_PLUS; } } return (CMMonsterBasic::OnCallPerFrame(fT)||OnCollisionMario()); } while (false); CCLog("fun CMMonsterMushrooms::OnCallPerFrame Error!"); return false; }
最后调用基类的定时刷新函数和马里奥的冲突函数。基类的定时刷新函数做什么呢
bool CMMonsterBasic::OnCallPerFrame(float fT) { do { //判断当怪物离开地图则发消息删除自己 if (getPositionX()<0 || getPositionY()<0) { TCmd_Remove_Monster* pData = new TCmd_Remove_Monster; pData->pMonster = this; SendMsg(enMsgMonsterDisappear,pData,sizeof(pData)); return true;//需要删除自己 } CC_BREAK_IF(m_pMario==NULL); //判断马里奥与当前怪物的距离,用以激活。 if (abs(m_pMario->getPositionX() - getPositionX())<MONSTER_ACTIVE_DISTANCE) { m_bIsActivation = true; } return false;//不需要删除自己 } while (false); CCLog("fun CMMonsterBasic::OnCallPerFrame Error!"); return false; }
1)如果怪物离开地图,则自杀。自杀是通过发送消息给地图类,让地图类杀掉本对象
2)如果怪物与马里奥的距离小于某个值,则激活,激活的意思就是这个怪物开始运动了