Cocos2d-X小游戏——别踩白块

前端之家收集整理的这篇文章主要介绍了Cocos2d-X小游戏——别踩白块前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

自己学了快两个月的cocos2d-x了,前几天自己看着教学视频,跟着做了一个小游戏——别踩白块。

今天来说一说我自己是如何写这个项目的。逻辑有点乱,不知道看不看得懂。。。。

首先,这个游戏有两个场景,一个是开始场景,一个是失败场景。


开始场景如下:

开始场景里面有三类块:

  1. 起始块(黄色,有Start Game)
  2. 正常块(黑白块)
  3. 结束块(绿色,有You Win)

起始块如下:

正常块如下:

结束块如下:

因为这个游戏都是以块为单位的所以可以将这些块统称为抽象块,所以要给块来写一个 create 方法如下:

static Block * create(Size size,Color3B color,String str,float strSize,Color3B strColor);

这个函数里面有五个参数,第一个参数是块的大小 size ;第二个参数是块的颜色 color ;第三个参数是在块上要显示的字符串 str ;第四个参数是字符串的大小 strSize ;第五个参数是字符串的颜色 strColor 。

我们创建块的时候要确定块的大小,如

  • 起始块:宽为,winSize.width;高为,winSize.height/4
  • 正常块:宽为,winSize.width/4;高为,winSize.height/4
    这里除以 4 是将屏幕给分成了4×4个正常块
  • 结束块:宽为,winSize.width;高为,winSize.height

注意:这里的 winSize 就是屏幕的大小。

现在把 Block 类写好:

#ifndef __Block_H_
#define __Block_H_
#include "cocos2d.h"
USING_NS_CC;
class Block : public Sprite
{
public:
    static Block * create(Size size,Color3B strColor);
    bool init(Size size,Color3B strColor);

    //类内部静态成员在单独的内存里的,
    //在定义的时候已经存在了,它不属于某一个对象,只属于这个类
    static Vector<Sprite *> vec; //用来存放已经创建好的块
    static Vector<Sprite *> getBlockVector();

    //产生get方法和set方法
    CC_SYNTHESIZE(int,_LineIndex,LineIndex);

    //向下移动块,并且清除
    void moveDownAndCleanUp();
};

#endif

将 Block 类写好后,就可以开始写游戏逻辑了。

  1. 游戏一开是就进入开始场景,只要玩家一开始点击黑块游戏就会开始。
  2. 玩家只能按顺序点击黑块,即每次都是点击最下面的黑块。
  3. 玩家只要正确地点击了黑块,那个被点击的黑块就会变成灰色,
    被点击那一行块就会往下移动,并且在屏幕上方添加一新行。
  4. 玩家在点击玩所有的黑块后,即赢得胜利,出现结束块。
  5. 若玩家点到了白块则游戏失败,切换到游戏失败的场景

失败场景如下:


开始游戏,我们需要将屏幕最开始的布局给设置好,大家可能已经注意到 Block 类中有一个行号函数

CC_SYNTHESIZE(int,LineIndex);

行号函数就是用来设置(set)和得到(get)块的行号的,行号在屏幕上只有0到3行,
如下图所示

首先我们需要三个函数,分别添加开始块,正常块和结束块。

在开始的布局中第0行添加开始块,第 1,2,3行添加正常块。
在点击完所有的黑块后添加结束块。

添加开始块的函数如下:

void LayerGame::addStartLineBlock()
{
    Size startBlockSize = Size(winSize.width,winSize.height/4);
    Block * b = Block::create(startBlockSize,Color3B::YELLOW,"Start Game",30,Color3B::BLUE);
    b->setPosition(Point(0,0));
    this->addChild(b);
    b->setLineIndex(0);//设置行号为0
    _lineCount++;//行数+1,注意,不是行号,行号只有0,1,2,3这4行,而行数可以一直增加
}

添加正常块的函数如下:

void LayerGame::addNormalLineBlocks(int LineIndex)
{
    Size normalBlockSize = Size(winSize.width/4,winSize.height/4);
    int idx = rand()%4;//取一个0到3之间的随机
    for(int i = 0; i < 4; i++)//一下添加一行,即4块
    {
        Block * b = Block::create(normalBlockSize,i == idx ? Color3B::BLACK : Color3B::WHITE,/*设置黑白块*/
                                  "",Color3B::WHITE);
        b->setLineIndex(LineIndex);
        //根据 i 和 LineIndex 来设置位置
        b->setPosition(Point( i * winSize.width/4,LineIndex * winSize.height/4 ) );
        this->addChild(b);
    }
    _lineCount++;//行数+1
}

添加结束块的函数如下:

void LayerGame::addEndLineBlock()
{
    Block * b = Block::create(winSize,Color3B::GREEN,"You Win!",50,Color3B::RED);
    b->setAnchorPoint(Point(0,0));
    b->setPosition(Point(0,winSize.height));
    this->addChild(b);
    b->setLineIndex(4);
    _lineCount++;//行数+1

    //添加两个按钮 Again 和 Exit
    LabelBMFont * bm = LabelBMFont::create("Again","fonts/arial-unicode-26.fnt");
    LabelBMFont * bm1 = LabelBMFont::create("Exit","fonts/arial-unicode-26.fnt");
    MenuItem * again = MenuItemLabel::create(bm);
    MenuItem * ext = MenuItemLabel::create(bm1);
    again->setTarget(this,menu_selector(LayerGame::tryAgainCallback));
    ext->setTarget(this,menu_selector(LayerGame::exitCallback));
    Menu * menu = Menu::create(again,ext,NULL);
    b->addChild(menu);
    menu->setPosition(Point::ZERO);
    again->setPosition(Point(again->getBoundingBox().size.width/2,winSize.height-again->getBoundingBox().size.height/2));
    ext->setPosition(Point(winSize.width-ext->getBoundingBox().size.width/2,winSize.height-ext->getBoundingBox().size.height/2));

}

到这里,我们就可以设置好最开始的布局了,写一个StatGame函数如下:

void LayerGame::startGame() { addStartLineBlock();
    addNormalLineBlocks(1);
    addNormalLineBlocks(2);
    addNormalLineBlocks(3);
}

现在来有了最开始的布局了,就可以开始写点击事件了,因为我看的视频教程是cocos2d-x3.0以下的版本,而我自己用的却是3.0的版本,所以,就造成了我写的代码3.0以下的版本和3.0版本的混搭……

首先设置触摸监听:

//3.0版本
    auto listener = EventListenerTouchOneByOne::create();
    listener->setSwallowTouches(true);//吞噬
    listener->onTouchBegan = CC_CALLBACK_2(LayerGame::onTouchBegan,this);
    this->_eventDispatcher->addEventListenerWithSceneGraPHPriority(listener,this);

然后在写点击事件函数

bool LayerGame::onTouchBegan(Touch *touch,Event *unused_event)
{
    //遍历所有存在vec中的块 C++11方法
    for (auto obj : Block::getBlockVector())
    {
        Block * b = (Block *)obj;
        //判断是否点击到该块,且行号为1
        if (b->boundingBox().containsPoint(touch->getLocation()) &&
            b->getLineIndex() == 1)
        {
            if (b->getColor() == Color3B::BLACK)//点到黑块
            {
                //播放音效
                SimpleAudioEngine::getInstance()->playEffect("onclick.wav");
                //开始计时
                this->startTimer();
                //将被点击的黑块变灰
                b->setColor(Color3B::GRAY);
                //被点击的这行下移
                this->moveDown();
            }
            else if (b->getColor() == Color3B::GREEN )//点到结束块
            {
                //播放音效
                SimpleAudioEngine::getInstance()->playEffect("gamewin.wav");
                //终止计时
                this->stopTimer();
                //下移
                this->moveDown();
            }
            else if (b->getColor() == Color3B::WHITE)//点到白块
            {
                //播放音效
                SimpleAudioEngine::getInstance()->playEffect("wrong.wav");
                Scene * scene = LayerFailed::scene();
                Director::getInstance()->replaceScene(scene);//切换到失败场景
            }
            break;
        }
    }
    return false;
}

现在看看点击事件函数的中的 moveDown 函数

void LayerGame::moveDown()
{
    if (this->getLineCount() < 20)//如果行号小于20
    {
        this->addNormalLineBlocks(4);//则添加一正常行,并设置行号为4
    }
    else if (!showEnd)//如果行号不小于20且标志showEnd为false
    {
        this->addEndLineBlock();//则添加结束块
        showEnd = true;//并将showEnd设置为true
    }
    for (auto obj : Block::getBlockVector())//遍历这一行所有块
    {
        Block * b = (Block *)obj;
        b->moveDownAndCleanUp();//将他们下移且从渲染树中删除
    }
}

moveDown函数中又有一个moveDownAndCleanUp函数,我们把它写在了Block类里面

void Block::moveDownAndCleanUp()
{
    //行号减1,因为这个函数是在moveDown函数的for循环里面,所以vec中所有的行号都减1
    _LineIndex--;
    //下移动作(时间,位移距离)
    MoveTo * to = MoveTo::create(0.01,Point(getPositionX(),getPositionY()-winSize.height/4));
    this->runAction(to);//执行动作
    if (_LineIndex < 0)//判断当前行号是否小于0
    {

        //从渲染树上拿下来
        this->removeFromParentAndCleanup(true);
        //从vec中删除当前块
        vec.eraSEObject(this);
    }
}

上面moveDown函数moveDownAndCleanUp函数都是为了实现玩家在点击时的滚屏效果


前面说的都是游戏顺利完成的情况,下面来说,游戏失败的情况——在点击到白块时,游戏失败
我们用了一个 if 语句来判断是否点击到了白块:

else if (b->getColor() == Color3B::WHITE)
{
    SimpleAudioEngine::getInstance()->playEffect("wrong.wav");
    Scene * scene = LayerFailed::scene();
    Director::getInstance()->replaceScene(scene);
}

如果踩到白块,则播放 wrong 音效,并且切换到游戏失败的场景。

补充,在游戏中我们还加入了计时功能播放音乐功能

首先说说计时功能

1.首先设置一个标志和一个变量

long startTime = 0;//全部变量
bool isRunning = false;//全局变量

2.再创建一个 Label 来显示时间

LabelTTF * ttf = LabelTTF::create("0.000","Courier New",30); //ttf为全局变量
ttf->setZOrder(100);
ttf->setPosition(Point(winSize.width/2,winSize.height - 20));
ttf->setColor(Color3B::BLUE);
this->addChild(ttf);

3.然后再 写两个函数计时器startTimerstopTimer

void LayerGame::startTimer()
{
    if (!isRunning)//设置标志,只让其运行一次
    {

        this->scheduleUpdate();//开始帧循环定时器,每一帧都会调用默认的update函数
        startTime = clock();//获取系统当前时间
        isRunning = true;
    }
}

void LayerGame::stopTimer()
{
    if (isRunning)//只让其运行一次
    {
        this->unscheduleUpdate();//关闭帧循环定时器
    }
}

4.然后将startTimerstopTimer分别放入触摸响应函数if 条件中

if (b->getColor() == Color3B::BLACK)
{
    SimpleAudioEngine::getInstance()->playEffect("onclick.wav");
    this->startTimer();//开启计时器
    b->setColor(Color3B::GRAY);
    this->moveDown();
}

else if (b->getColor() == Color3B::GREEN )
{
    SimpleAudioEngine::getInstance()->playEffect("gamewin.wav");
    this->stopTimer();//关闭计时器
    this->moveDown();
}

再说说播放音乐的功能

1.首先要包含头文件

#include "SimpleAudioEngine.h"
using namespace CocosDenshion;

2.再在相应部分加入播放音效代码

if (b->getColor() == Color3B::BLACK)
{
    //播放点击音效
    SimpleAudioEngine::getInstance()->playEffect("onclick.wav");
    this->startTimer();
    b->setColor(Color3B::GRAY);
    this->moveDown();
}
else if (b->getColor() == Color3B::GREEN )
{
    //播放胜利音效
    SimpleAudioEngine::getInstance()->playEffect("gamewin.wav");
    this->stopTimer();
    this->moveDown();
}
else if (b->getColor() == Color3B::WHITE)
{
    //播放失败音效
    SimpleAudioEngine::getInstance()->playEffect("wrong.wav");
    Scene * scene = LayerFailed::scene();
    Director::getInstance()->replaceScene(scene);
}

好了,到这里差不多完成了。上面给了一些代码片段,若需要看完整代码,请移步至:(github)别踩白块

写得有点乱,若有不足之处还请斧正。

猜你在找的Cocos2d-x相关文章