cocos2d-x 格斗游戏教程(三)

在第一篇《如何制作一个横版格斗过关游戏》基础上,增加角色运动、碰撞、敌人、AI和音乐音效,原文《How To Make A Side-Scrolling Beat ‘Em Up Game Like Scott Pilgrim with Cocos2D – Part 2》,在这里继续以Cocos2d-x进行实现。有关源码、资源等在文章下面给出了地址。
步骤如下:
1.使用上一篇的工程;
2.移动英雄。在第一部分我们创建了虚拟方向键,但是还未实现按下方向键移动英雄,现在让我们进行实现。打开Hero.cpp文件,在init函数attack animation后面,添加如下代码:

1
2
3
4
5
6
7
8
9
 
//walk animation
CCArray *walkFrames = CCArray::createWithCapacity( 8);
for (i =  0; i <  8; i++)
{
   CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(CCString::createWithFormat( "hero_walk_%02d.png", i)->getCString());
   walkFrames->addObject(frame);
}
CCAnimation *walkAnimation = CCAnimation::createWithSpriteFrames(walkFrames,  float( 1. 0 /  12. 0));
this->setWalkAction(CCRepeatForever::create(CCAnimate::create(walkAnimation)));

打开ActionSprite.cpp文件,实现walkWithDirection方法,代码如下:

void ActionSprite::walkWithDirection(CCPoint direction)
{
    //检查前置动作状态是否空闲
    if (_actionState == kActionStateIdle)
    {
        //停止所有动作
        this->stopAllActions();
        //执行行走的动作
        this->runAction(_walkAction);
        //标记状态为行走
        _actionState = kActionStateWalk;
    }
    if (_actionState == kActionStateWalk)
    {
        //根据_walkSpeed值改变精灵的速度
        _velocity = ccp(direction.x * _walkSpeed, direction.y * _walkSpeed);
        //检查精灵的左右方向
        if (_velocity.x >= 0)
        {
            //用setScaleX来翻转精灵  看清楚是有 setScaleX是有X的  精灵可以左右变换
            this->setScaleX(1.0);
        }
        else
        {
            this->setScaleX(-1.0);
        }
    }
}
这段代码,检查前置动作状态是否空闲,若是的话切换动作到行走。在行走状态时,根据 _walkSpeed 值改变精灵速度。同时检查精灵的左右方向,并通过将精灵 scaleX 设置为1或-1来翻转精灵。要让英雄的行走动作跟方向键联系起来,需要借助方向键的委托: GameLayer 类。打开 GameLayer.cpp 文件,实现如下方法:


void GameLayer::didChangeDirectionTo(SimpleDPad *simpleDPad, CCPoint direction)
{
    //传递的参数为方向数据
    _hero->walkWithDirection(direction);
}

void GameLayer::isHoldingDirection(SimpleDPad *simpleDPad, CCPoint direction)
{
    _hero->walkWithDirection(direction);
}

void GameLayer::simpleDPadTouchEnded(SimpleDPad *simpleDPad)
{
    //触摸停止后 如果之前的状态为行走 让精灵变为空闲状态
    if (_hero->getActionState() == kActionStateWalk)
    {
        _hero->idle();
    }
}

此时,编译运行程序的话,通过方向键移动英雄,发现英雄只是原地踏步。改变英雄的位置是ActionSprite和GameLayer共同的责任。一个ActionSprite永远不会知道它在地图上的位置。因此,它并不知道已经到达了地图的边缘,它只知道它想去哪里。而GameLayer的责任就是将它的期望位置转换成实际的位置。打开ActionSprite.cpp文件,实现以下方法:


void ActionSprite::update(float delta)
{
    //在每次游戏更新场景的时候都会进行调用,当精灵处于行走状态时,
    if (_actionState == kActionStateWalk)
    {
        //它更新精灵的期望位置。位置+速度*时间,实际上就是意味着每秒移动X和Y点
        //这里的this指针就是指  谁调用就代表谁,英雄调用的就代表英雄,敌人调用的就代表敌人
        _desiredPosition = ccpAdd(this->getPosition(), ccpMult(_velocity, delta));
    }
}

这个方法在每次游戏更新场景的时候都会进行调用,当精灵处于行走状态时,它更新精灵的期望位置。位置+速度*时间,实际上就是意味着每秒移动X和Y点。打开GameLayer.cpp文件,在init函数this->initTileMap();后面添加如下代码:

1
 
this->scheduleUpdate();
在析构函数,添加如下代码:
1
2
3
4
 
GameLayer::~GameLayer( void)
{
this->unscheduleUpdate();
}
增加如下两个方法:

void GameLayer::update(float delta)
{
    _hero->update(delta);
    this->updatePositions();
    
    this->setViewpointCenter(_hero->getPosition());
    this->reorderActors();
}

void GameLayer::updatePositions()
{
    //_tileMap->getMapSize().width 得到的是地图中横向有多少个瓦片地图
    float widthMap = _tileMap->getMapSize().width;
    //_tileMap->getTileSize().width  得到的时每个瓦片地图的宽度
    float widthTile = _tileMap->getTileSize().width;
    //widthMap * widthTile 代表的是整个地图的宽
    //widthMap * widthTile - _hero->getCenterToSides()用整个地图的宽减去精灵中心到精灵边界的距离
    //相减得到的值就是精灵能够横向移动的最大位置
    //desiredPositionX  x轴的目标位置 就是在当前位置和目标
    float desiredPositionX = MAX(_hero->getCenterToSides(), _hero->getDesiredPosition().x);
    float posX = MIN( widthMap * widthTile - _hero->getCenterToSides(),desiredPositionX);
   
    
    //得到每个瓦片地图的高
    float heightTile = _tileMap->getTileSize().height;
    //下面的3   是指属于地板的瓦片只有3个
    float posY = MIN(3 *  heightTile + _hero->getCenterToBottom(),
                     MAX(_hero->getCenterToBottom(), _hero->getDesiredPosition().y));
    _hero->setPosition(ccp(posX, posY));
}
设定 GameLayer 的更新方法,每次循环时, GameLayer 让英雄更新它的期望位置,然后通过以下这些值,将期望位置进行检查是否在地图地板的范围内:

  • mapSize:地图tile数量。总共有10x100个tile,但只有3x100属于地板。
    tileSize:每个tile的尺寸,在这里是32x32像素。

GameLayer还使用到了ActionSprite的两个测量值,centerToSidescenterToBottom,因为ActionSprite要想保持在场景内,它的位置不能超过实际的精灵边界。假如ActionSprite的位置在已经设置的边界内,则GameLayer让英雄达到期望位置,否则GameLayer会让英雄留停在原地。
3.编译运行,此时点击方向键,移动英雄,如下图所示:

但是,很快你就会发现英雄可以走出地图的右边界,然后就这样从屏幕上消失了。
4.以上的问题,可以通过基于英雄的位置进行滚动地图,这个方法在文章《如何制作一个基于Tile的游戏》中有描述过。打开GameLayer.cpp文件,在update函数里最后添加如下代码:

1
 
this->setViewpointCenter(_hero->getPosition());
添加如下方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
void GameLayer::setViewpointCenter(CCPoint position)
{
   CCSize winSize = CCDirector::sharedDirector()->getWinSize();

int x = MAX(position.x, winSize.width /  2);
int y = MAX(position.y, winSize.height /  2);
   x = MIN(x, (_tileMap->getMapSize().width * _tileMap->getTileSize().width) - winSize.width /  2);
   y = MIN(y, (_tileMap->getMapSize().height * _tileMap->getTileSize().height) - winSize.height /  2);
   CCPoint actualPosition = ccp(x, y);

   CCPoint centerOfView = ccp(winSize.width /  2, winSize.height /  2);
   CCPoint viewPoint = ccpSub(centerOfView, actualPosition);
this->setPosition(viewPoint);
}

以上代码让英雄处于屏幕中心位置,当然,英雄在地图边界时的情况除外。编译运行,效果如下图所示:


代码例子 http://vdisk.weibo.com/s/BDn59yfnBVkre  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杜甲同学

感谢打赏,我会继续努力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值