标签:仿马里奥 横版平台动作游戏源码 横版动作 cocos2d-x源码
让考拉动起来!
这里控制考拉移动变得非常简单,它只有向前和跳两个能力(源码中我加了考拉向后走功能,建议大家自己加几个虚拟按键来实现更非富的功能)如果你按着屏幕左半部考拉会向前走,按住右半部考拉会跳起来(原文设定考拉不会后退-_-)。
我们需要在Player.h里加两个成员变量:
bool _forwardMarch; //是否向前走
bool _mightAsWellJump; //可以跳跃吗?
在player.cpp的init方法或在构造函数里把它们设置为false
在GameLevelLayer类里加上触摸,在.h文件里加上:
virtual void registerWithTouchDispatcher(); void ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent); void ccTouchesMoved(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent); void ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);在GameLevelLayer.cpp的init里加上(加载地图代码后)
void GameLevelLayer::registerWithTouchDispatcher()
{
CCDirector* pDirector = CCDirector::sharedDirector();
pDirector->getTouchDispatcher()->addStandardDelegate(this, 0); //注册多点触摸
}现在,让我们看看那三个触摸事件吧!void GameLevelLayer::ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent)
{
CCSetIterator iter = pTouches->begin();
for (; iter!=pTouches->end(); iter++)
{
CCTouch* pTouch = (CCTouch*)(*iter);
CCPoint touchLocation = this->convertTouchToNodeSpace(pTouch); //把touch点位置转换为本地坐标
if (touchLocation.x > 240) //在屏幕最右边点,就是跳
{
_player->bMightAsWellJump = true;
}
else
{
_player->bForwardMarch = true;
}
}
}
void GameLevelLayer::ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent)
{
_player->bForwardMarch = false; //松开按键时,设置为不可跳也不是向前状态
_player->bMightAsWellJump = false;
}代码一目了然,ccTouchesBegan时根据玩家按的位置决定了考拉状态是前进还是跳跃,松开按键时将这两个状态变量重置为false。
真正的角色移动是在player的update方法里进行的,看代码:
void Player::update(float delta)
{
CCPoint gravity = ccp(0.f, -450.f); //考拉每秒下降450个单位
CCPoint gravityStep = ccpMult(gravity, delta); //计算在重力影响下delta时间内具体下降了多少, 即dt时间后下落速度为多少
CCPoint forwardMove = ccp(800.f, 0.f); //前进速度,每秒前进800.f
CCPoint forwardStep = ccpMult(forwardMove, delta); // 1
this->_velocity = ccpAdd(this->_velocity, gravityStep); //当前速度=当前速度+重力加速度
this->_velocity = ccp(this->_velocity.x *0.9f, this->_velocity.y); //2
if (this->bForwardMarch)
{
this->_velocity = ccpAdd(this->_velocity, forwardStep); //当前速度要加上向前的速度矢量
}// 3
CCPoint minMovement = ccp(-120.f, -350.f);
CCPoint maxMovement = ccp(120.0f, 250.f);
this->_velocity = ccpClamp(this->_velocity, minMovement, maxMovement); //4
CCPoint stepVelocity = ccpMult(this->_velocity, delta); //计算下此速度下主角移动了多少
this->_desiredPosition = ccpAdd(this->getPosition(), stepVelocity); //当前期望要去的位置=当前位置+当前速度
}让我们来详细地看一下新增部分:
让考拉跳起来!
跳跃是动作游戏里最明显的一个特征。我们希望角色跳的平滑逼真,现在让我们来实现它。
到player类的update方法里,在if(this->_forwardMarch)语句之前加上下面代码:
CCPoint jumpForce = ccp(0.f, 310.f);
if(this->_mightAsWellJump && this->_onGround)
{
this->_velocity = ccpAdd(this->_velocity, jumpForce);
} 只要加一个向上的力,角色就可以跳起来了。CCPoint jumpForce = ccp(0.f, 310.f); //向上的跳跃力 玩家一直按着跳跃键时的跳跃力
float jumpCutOff = 150.f; //玩家没有按住跳跃键时的跳跃力
if(this->bMightAsWellJump && this->onGround) //如果当前玩家按了跳跃键并且在地上
{
this->_velocity = ccpAdd(this->_velocity, jumpForce); //跳跃就是加上一个向上的速度
}
else if (!this->bMightAsWellJump && this->_velocity.y > jumpCutOff) //玩家没有按住跳跃键,并且向上的速度已经超过了设定的值,就限定向上跳跃速度
{
this->_velocity = ccp(this->_velocity.x, jumpCutOff);
}注释解释的很清楚,就不多解释了。void GameLevelLayer::setViewpointCenter(cocos2d::CCPoint pos)
{
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
//限定角色不能超过半屏
int x = MAX(pos.x, winSize.width/2);
int y = MAX(pos.y, winSize.height/2);
//限定角色不能跑出屏幕
x = MIN(x, (_map->getMapSize().width * _map->getTileSize().width) - winSize.width/2);
y = MIN(y, (_map->getMapSize().height * _map->getTileSize().height) - winSize.height/2);
CCPoint actualPosition = ccp(x, y);
CCPoint centerOfView = ccp(winSize.width/2, winSize.height/2);
CCPoint viewPoint = ccpSub(centerOfView, actualPosition);
//设定一下地图的位置
_map->setPosition(viewPoint);
} 方法参数就是玩家考拉当前位置。这个方法可以不但能左右跟随还能上下跟随主角,非常好用。这个方法原理在很多博客都有提到,原理其实就是地图在跟玩家做返方向运动,大家可以查阅一下其他文章解释它的原理,不过我在这里不想再多说了,不是一两句能说清,我们只要会用这个方法就行了。
尝一下受伤的滋味!
现在我们可以着手做游戏过关和GameOver的功能了。
地图里有个hazards层,这个层里放置一些考拉碰上就会挂的object。其实本质也上碰撞检测,看代码:
void GameLevelLayer::handleHazardCollisions(Player* player)
{
CCArray *tiles = this->getSurroundingTilesAtPosition(player->getPosition(), _hazards);
CCDictionary* dic = NULL;
CCObject* obj = NULL;
float x=0.f; float y = 0.f;
int gid = 0;
CCARRAY_FOREACH(tiles, obj)
{
dic = (CCDictionary*)obj;
x = dic->valueForKey("x")->floatValue();
y = dic->valueForKey("y")->floatValue();
CCRect tileRect = CCRectMake(x, y, _map->getTileSize().width, _map->getTileSize().height);
CCRect pRect= player->collisionBoundingBox();
gid = dic->valueForKey("gid")->intValue();
if (gid && tileRect.intersectsRect(pRect)) //如果有钉刺并且玩家与它发生碰撞了,gameOver
{
this->gameOver(false);
}
}
}代码看上去是不是很熟悉呀,你没记错,其实就是从checkAndResolveCollisions方法里拷来的,只不过没分那么多情况而且处理方式也简单了只是调用了gameOver方法,gameOver的布尔值参数为true表示游戏胜利,为false就是失败void GameLevelLayer::update(float delta)
{
_player->update(delta);
this->handleHazardCollisions(_player);
this->checkForAndResolveCollisions(_player);
this->setViewpointCenter(_player->getPosition());
}现在实现gameOver方法了,当玩家跳到harads层里的刺上时,我们会调用这个方法游戏结束,或者当玩家走到终点时,我也会调用这个方法,这个方法会显示出一个restart按钮,并在屏幕上打印出一些信息,告诉玩家你死了或者你赢了之类的-_-void GameLevelLayer::gameOver(bool bWon)
{
bGameOver = true;
CCString* gameText;
if (bWon)
{
gameText = CCString::create("You Won!");
}
else
gameText = CCString::create("You have Died!");
CCLabelTTF* diedLabel = CCLabelTTF::create(gameText->getCString(), "Marker Felt", 40);
diedLabel->setPosition(ccp(240, 200));
CCMoveBy *slideIn = CCMoveBy::create(1.f, ccp(0, 250));
CCMenuItemImage* replay = CCMenuItemImage::create("replay.png","replay.png","replay.png", this, menu_selector(GameLevelLayer::restartGame));
CCArray *menuItems = CCArray::create();
menuItems->addObject(replay);
CCMenu *menu = CCMenu::create();
menu->addChild(replay);
menu->setPosition(ccp(240, -100));
this->addChild(menu);
this->addChild(diedLabel);
menu->runAction(slideIn);
}方法开头的变量bGameOver也是GameLevelLayer类新定义的一个成员变量,在init里要初始化为false,此变量表示游戏是否结束。这个变量作用是你在update方法里开头判断一下,如果bGameOver==true,则直接返回不要做任何事了。如下:void GameLevelLayer::update(float delta)
{
if(bGameOver)
return;
_player->update(delta);
this->handleHazardCollisions(_player);
this->checkForAndResolveCollisions(_player);
this->setViewpointCenter(_player->getPosition());
}现在你再编译运行,碰上钉板试试,会出现杯具性的结局...修正一个致命BUG
还记得前面出现过当考拉掉出地图出现的事情吗?在游戏地图里有些坑我们本意是考拉掉下去游戏会结束,但事实上程序崩掉了。凡是带有TILED地图的都会出现这样的问题,只要角色走出地图边界游戏就会崩掉,这个问题困扰了好多人,也包括我。其实解决这个BUG很简单,关键在tileGIDAt方法。
此方法你打开源码实现就知道,它有个CCASSERT宏,判断出地图就会抛出异常。本来吗此方法是取地图上某处tile的gid,你都出地图了还取什么当然要抛异常啦。这里我们就在getSurroundingTilesAtPosition方法里加个判断,注意当然要在调用tileGIDAt方法之前,不让考拉出地图:
if (tilePos.y > (_map->getMapSize().height-1))
{
this->gameOver(false);
return NULL;
}在checkForAndResolveCollisions方法里有调用getSurroundingTilesAtPosition方法,如果它返回个空数组可就不妙了,所以在checkForAndResolveCollisions也要改一下,在 CCArray* tiles
= this->getSurroundingTilesAtPosition(player->getPosition(), _walls);一句之后,加上if (bGameOver) //可能玩家掉坑里,就不处理了
{
return;
}编译再运行,把考拉掉进坑里试试,发现程序不再DOWN了!void GameLevelLayer::checkForWin()
{
if (_player->getPositionX()>3130.0)
{
this->gameOver(true);
}
}真实游戏里我们通常在终点放置一个object,如门呀城堡小旗什么的,主角碰到就胜利了可进入下一关,有兴趣的自己尝试!void GameLevelLayer::update(float delta)
{
if(bGameOver)
return;
_player->update(delta);
this->handleHazardCollisions(_player);
this->checkForWin();
this->checkForAndResolveCollisions(_player);
this->setViewpointCenter(_player->getPosition());
}运行一下,走到终点试试:if(this->bMightAsWellJump && this->onGround) //如果当前玩家按了跳跃键并且在地上
{
this->_velocity = ccpAdd(this->_velocity, jumpForce); //跳跃就是加上一个向上的速度
SimpleAudioEngine::sharedEngine()->playEffect("jump.wav");
}在GameLevelLayer.cpp的gameOver方法里也加上音效:void GameLevelLayer::gameOver(bool bWon)
{
if (bGameOver) //不要反复调用
{
return;
}
bGameOver = true;
SimpleAudioEngine::sharedEngine()->playEffect("hurt.wav");编译运行,试一试你亲手制作的带有音乐感的游戏吧!















标签:仿马里奥 横版平台动作游戏源码 横版动作 cocos2d-x源码
原文地址:http://blog.csdn.net/qiou2719/article/details/42395193