本文实践自 Ray Wenderlich 的文章《 How To Make A Simple iPhone Game with Cocos2D 2.X Tutorial 》,文中使用Cocos2D,我在这里使用Cocos2D-x3.0alpha0进行学习和移植,前者是用Object-C所写,所以移植到Cocos2D-x会有些差异,比如某些函数、某些功能不能跟原文一样直接实现,需另转换方法实现。之前已经对Cocos2D-x的安装以及简单使用进行了介绍,这里不再介绍,直接进入主题。

步骤如下:使用脚本创建工程  

./create-multi-platform-projects.py -p Ninja -k com.HuLuo.Ninja -l cpp

2.编译运行,可以看到如下图所示:

炫酷的HTML5 炫酷的忍者飞镖教程_数组


3.下载本游戏所需的资源,将资源放置"Resources"目录下;

炫酷的HTML5 炫酷的忍者飞镖教程_#include_02


4.游戏需要一个白色的背景,最简单的方法是使用CCLayerColor,将HelloWorldScene.h文件"HelloWorld"类改为如下:


class HelloWorld : public cocos2d::LayerColor

之后添加如下代码:

Sprite* _player;
    Sprite* _monster;
    
    void addMonster();
    
    void spriteMoveFinished(Node* sender);
    
    void gameLogic(float dt);

首先添加玩家,让玩家位于左边屏幕中间,将

HelloWorldScene.cpp 文件的

init 函数,改为如下:

bool HelloWorld::init()
{
    bool bRet = false;
    do {
        CC_BREAK_IF(!LayerColor::initWithColor(Color4B(255, 255, 255, 255)));
        Size winSize = Director::getInstance()->getWinSize();
        
        _player = Sprite::create("Player.png");
        _player->setPosition(Point(_player->getContentSize().width / 2, winSize.height / 2));
        _player->setScale(2);
        this->addChild(_player);
        
        this->schedule(schedule_selector(HelloWorld::gameLogic), 1.0);
        
        
        bRet = true;
    } while (0);
    return bRet;
    
}

void HelloWorld::gameLogic(float dt)
{
    
}

5.编译运行,可以看到玩家精灵在白色背景上,如下图所示:

炫酷的HTML5 炫酷的忍者飞镖教程_cocos2d-x-3.0alpha0_03




6.接下来添加怪物,并且让怪物可以移动,我们在屏幕右边创建怪物,建立动作让它们向左移动,增加addMonster方法,代码如下

void HelloWorld::addMonster()
{
    _monster = Sprite::create("monster.png");
    _monster->setScale(2);
    
    Size winSize = Director::getInstance()->getWinSize();
    
    int minY = _monster->getContentSize().height / 2;
    int maxY = winSize.height - _monster->getContentSize().height / 2;
    
    int rangeY = maxY - minY;
    
    int actualY = (rand() % rangeY) + minY;
    
    _monster->setPosition(Point(winSize.width + _monster->getContentSize().width / 2, actualY));
    
    this->addChild(_monster);
    
    int minDuration = 2;
    int maxDuration = 4;
    int rangeDuration = maxDuration - minDuration;
    int actualDuration = (rand() % rangeDuration) + minDuration;
    
    MoveTo* actionMove = MoveTo::create(actualDuration, Point(-_monster->getContentSize().width / 2, actualY));
    CallFuncN* actionMoveDone = CallFuncN::create(std::bind(&HelloWorld::spriteMoveFinished, this,_monster));
    
    _monster->runAction(Sequence::create(actionMove,actionMoveDone, NULL));
    
    
}

在右边屏幕以随机的位置添加怪物精灵,注意计算精灵的位置坐标,默认描点在中心,不要让怪物截断了。然后再以2~4秒的随机总时间,让怪物从右边移动到左边,移动出边界后,即回调函数

spriteMoveFinished ,进行删除精灵对象,增加的spriteMoveFinished方法如下:

void HelloWorld::spriteMoveFinished(cocos2d::Node *sender)
{
    Sprite* sprite = (Sprite*)sender;
    this->removeChild(sprite);
}

接下去就是定时创建怪物,在

init 函数返回之前,安装定时器,每秒执行一次,代码如下

this->schedule(schedule_selector(HelloWorld::gameLogic), 1.0);

增加

gameLogic 方法,代码如下

void HelloWorld::gameLogic(float dt)
{
    this->addMonster();
}

7.编译运行,可以看到右边怪物定时增加,并且以不同的速度向左边移动,如下图所示:

炫酷的HTML5 炫酷的忍者飞镖教程_#include_04

8.接着让玩家可以射击子弹,当用户在屏幕点击时,就让玩家往点击的方向进行发送子弹,用户的屏幕点击点并不是子弹移动的最终地,借用原文的一张图片来说明:

炫酷的HTML5 炫酷的忍者飞镖教程_C++_05


要让层可以支持触摸,需要在init方法,添加如下代码:

this->setTouchEnabled(true);

然后重载onTouchesEnded方法,代码如下


void HelloWorld::onTouchesEnded(const std::vector<Touch *> &touches, cocos2d::Event *event)
{
    Touch* touch = touches.front();
    Point location = this->convertTouchToNodeSpace(touch);
    
    
    Size winSize = Director::getInstance()->getWinSize();
    
    Sprite* projectile = Sprite::create("Projectile.png");
    projectile->setScale(2);
    
    projectile->setPosition(Point(20, winSize.height / 2));
    Point offset = ccpSub(location, projectile->getPosition());
    
    if (offset.x <= 0) {
        return;
    }
    
    this->addChild(projectile);
    
    int realX = winSize.width + projectile->getContentSize().width / 2;
    float ratio = (float)offset.y / (float)offset.x;
    int realY = realX * ratio + projectile->getPosition().y;
    Point realDest = Point(realX, realY);
    
    int offRealX = realX - projectile->getPosition().x;
    int offRealY = realY - projectile->getPosition().y;
    float length = sqrtf(offRealX * offRealX + offRealY * offRealY);
    float velocity = 480 / 1;
    
    float realMoveDuration = length / velocity;
    
    projectile->runAction(Sequence::create(MoveTo::create(realMoveDuration, realDest),CallFuncN::create(std::bind(&HelloWorld::spriteMoveFinished, this,projectile)), NULL));
}

首先,得到触摸点,然后创建子弹精灵,算出触摸点与子弹初始位置之差,若触摸点在初始位置的前方(即玩家前方),则添加子弹到层上。以同比例方法,计算出子弹飞向屏幕右边的最终坐标。然后再用勾股定理计算飞行长度,假定速度为每秒480像素,则计算出飞行总时间。之后就是让子弹执行给定的飞行动作,以及之后的删除自身调用。

9.编译运行,往屏幕点击,可以看到子弹发射出去,如下图所示:

炫酷的HTML5 炫酷的忍者飞镖教程_炫酷的HTML5_06


10.当子弹碰到怪物时,怪物被消灭,子弹消失,即碰撞检测。需要在场景中跟踪目标和子弹,在HelloWorldScene.h声明如下:

Array* _monsters;
    Array* _projectiles;

在构造函数和析构函数,添加如下:

HelloWorld::HelloWorld()
{
    _monsters = NULL;
    _projectiles = NULL;
}

HelloWorld::~HelloWorld()
{
    if (_monsters) {
        CC_SAFE_RELEASE_NULL(_monsters);
    }
    if (_projectiles) {
        CC_SAFE_RELEASE_NULL(_projectiles);
    }
}

然后在 init

函数中初始化这两个数组:


this->_monsters = Array::create();
        this->_monsters->retain();
        
        this->_projectiles = Array::create();
        this->_projectiles->retain();

修改 addMonster

函数,为怪物精灵添加标签,并加入到数组,代码如下


_monster->setTag(1);
    _monsters->addObject(_monster);

修改 onTouchesEnded

函数,为子弹精灵添加标签,并加入到数组,代码如下


projectile->setTag(2);
    _projectiles->addObject(projectile);

然后修改 spriteMoveFinished

函数,增加如下代码:


if (sprite->getTag() == 1) {
        _monsters->removeObject(sprite);
    }
    else if (sprite->getTag() == 2)
    {
        _projectiles->removeObject(sprite);
    }

添加如下方法:


void HelloWorld::update(float delta)
{
    Array* projectilesToDelete = Array::create();
    
    Object* pObject = NULL;
    Object* pObject2 = NULL;
    
    CCARRAY_FOREACH(_projectiles, pObject)
    {
        Sprite* projectile = (Sprite*)pObject;
        Array* monstersToDelete = Array::create();
        
        CCARRAY_FOREACH(_monsters, pObject2)
        {
            Sprite* monster = (Sprite*)pObject2;
            if (projectile->getBoundingBox().intersectsRect(monster->getBoundingBox())) {
                monstersToDelete->addObject(monster);
            }
        }
        
        CCARRAY_FOREACH(monstersToDelete, pObject2)
        {
            Sprite* monster = (Sprite*)pObject2;
            _monsters->removeObject(monster);
            this->removeChild(monster);
        }
        
        if (monstersToDelete->count() > 0) {
            projectilesToDelete->addObject(projectile);
        }
        monstersToDelete->release();
    }
    
    CCARRAY_FOREACH(projectilesToDelete, pObject)
    {
        Sprite* projectile = (Sprite*)pObject;
        _projectiles->removeObject(projectile);
        this->removeChild(projectile);
    }
    
    projectilesToDelete->release();
}

遍历子弹数组,计算每一个子弹所可能遇到的怪物,用它们各自的边界框进行交叉检测,检测到交叉,则将怪物对象放入ToDelete(待删除)数组,不能在遍历的时候删除一个对象。若是子弹遇到了怪物,也需要放入ToDelete(待删除)数组。然后从场景和数组中移动掉。同样,也在 init

函数,安装定时器,代码如下


this->scheduleUpdate();

11.编译运行,这时当子弹和怪物碰撞时,它们就会消失;


12.接下来,创建一个新的场景,来指示"You Win"或者"You Lose"。创建新类

GameOverLayer.h文件代码为:

#include "cocos2d.h"
USING_NS_CC;

class GameOverLayer:public LayerColor{
    
public:
    GameOverLayer();
    ~GameOverLayer();
    
    
    
    bool initWithWon(bool won);
    static Scene* sceneWithWon(bool won);
    void gameOverDone();
};

GameOverLayer.cpp 文件代码为:

//
//  GameOverLayer.cpp
//  HelloCpp
//
//  Created by 杜甲 on 13-12-1.
//
//

#include "GameOverLayer.h"
#include "HelloWorldScene.h"

GameOverLayer::GameOverLayer()
{
    
}
GameOverLayer::~GameOverLayer()
{
    
}
GameOverLayer* GameOverLayer::createWithWon(bool won)
{
    GameOverLayer* pRet = new GameOverLayer();
    if (pRet && pRet->initWithWon(won)) {
        
    }
    else{
        CC_SAFE_RELEASE_NULL(pRet);
    }
    return pRet;
}

bool GameOverLayer::initWithWon(bool won)
{
    bool bRet = false;
    do {
        CC_BREAK_IF(!LayerColor::initWithColor(Color4B(255, 255, 255, 255)));
        
        char* message;
        if (won) {
            message  = "You Won";
        }
        else
        {
            message = "You Lose";
        }
        Size winSize = Director::getInstance()->getWinSize();
        LabelTTF* label = LabelTTF::create(message, "Arial", 32);
        label->setColor(Color3B(0, 0, 0));
        label->setPosition(Point(winSize.width / 2, winSize.height / 2));
        this->addChild(label);
        
        this->runAction(Sequence::create(DelayTime::create(3),
                                         CallFunc::create(std::bind(&GameOverLayer::gameOverDone, this)), NULL));
        bRet = true;
    } while (0);
    return bRet;
}

Scene* GameOverLayer::sceneWithWon(bool won)
{
    Scene* scene = NULL;
    do {
        scene = Scene::create();
        
        CC_BREAK_IF(!scene);
        
        GameOverLayer* layer = GameOverLayer::createWithWon(won);
        CC_BREAK_IF(!layer);
        scene->addChild(layer);
        
    } while (0);
    return scene;
    
}
void GameOverLayer::gameOverDone()
{
    Director::getInstance()->replaceScene(HelloWorld::createScene());
    
}

游戏结束时,切换到以上所建的场景,场景上的层显示一个文本,在3秒之后返回到HelloWorld场景中。


13.最后,为游戏添加一些游戏逻辑。记录玩家消灭怪物的数量,进而决定该玩家输赢。在 HelloWorldScene.h 文件中,添加如下:

int _monstersDestroyed;

添加头文件引用:

#include "GameOverLayer.h"

在 update

定时函数中,monstersToDelete循环removeChild(monster, true)的后面添加被消灭怪物的计数,并判断胜利条件,代码如下

_monstersDestroyed++;
            if (_monstersDestroyed > 2) {
                Scene* gameOverScene = GameOverLayer::sceneWithWon(true);
                Director::getInstance()->replaceScene(gameOverScene);
            }

最后为玩家添加失败条件,规定只要有一只怪物跑到左边屏幕里,则玩家失败,在 spriteMoveFinished

函数里,sprite->getTag() == 1条件的后面,添加如下:

Scene* gameOverScene = GameOverLayer::sceneWithWon(false);
        Director::getInstance()->replaceScene(gameOverScene);

14.编译并运行,到此已完成了一个简单的游戏,包含音效,并带有胜利和失败的结束。游戏效果如下:


炫酷的HTML5 炫酷的忍者飞镖教程_C++_07