Cocos2d-x打砖块游戏

作业要求

  • 控制板子左右移动
  • 在顶部生成小砖块
  • 使用关节固定球与板子
  • 为板子、球、以及砖块设置物理属性
  • 砖、球碰撞则消去砖头,球与地板碰撞则游戏结束
  • 至少使用一种粒子效果(加在球上面,etc)

游戏演示

打砖块

具体实现

HitBrick.h

#ifndef __HIT_BRICK_H__
#define __HIT_BRICK_H__

#include "cocos2d.h"
using namespace std;
USING_NS_CC;
class HitBrick : public Layer {
public:
  static PhysicsWorld* world;
  static cocos2d::Scene* createScene();
  void setPhysicsWorld(PhysicsWorld * world);
  virtual bool init();
  void setJoint();
  void addSprite();   // 添加背景和各种精灵
  void addPlayer();   // 添加玩家
  void addListener(); // 添加监听器

  void update(float dt);
  void updateShip(float dt);  // 更新船的平衡情况
  void BrickGeneraetd();  // 掉落箱子

  void onKeyPressed(EventKeyboard::KeyCode code, Event * event);
  void onKeyReleased(EventKeyboard::KeyCode code, Event * event);
  bool onConcactBegin(PhysicsContact & contact);

  void preloadMusic();                   // 预加载音乐
  void GameOver();                      // 游戏结束
  void loadAnimation(string filepath); // 加载动画

  // 重玩或退出按钮响应函数
  void replayCallback(Ref * pSender);
  void exitCallback(Ref * pSender);

  // implement the "static create()" method manually
  CREATE_FUNC(HitBrick);

private:
  PhysicsWorld* m_world;
  Size visibleSize;
  Sprite* player;
  Sprite* ship;
  bool onBall;
  Sprite* ball;
  // 固定关节
  PhysicsJointPin * joint1;
  int spHolded;
  int spFactor;
  //新加了标签,分别代表了底部的船,球,砖块,玩家的板子
  enum Tag {SHIP, BALL, BOX, BOARD};
};

#endif // __HIT_BRICK_H__

HitBrick.cpp

#pragma execution_character_set("utf-8")
#include "HitBrick.h"
#include "cocos2d.h"
#include "SimpleAudioEngine.h"
#define database UserDefault::getInstance()

USING_NS_CC;
using namespace CocosDenshion;

void HitBrick::setPhysicsWorld(PhysicsWorld* world) { m_world = world; }

Scene* HitBrick::createScene() {
    srand((unsigned)time(NULL));
    auto scene = Scene::createWithPhysics();

    scene->getPhysicsWorld()->setAutoStep(true);

    // Debug 模式
    // scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
    scene->getPhysicsWorld()->setGravity(Vec2(0, -300.0f));
    auto layer = HitBrick::create();
    layer->setPhysicsWorld(scene->getPhysicsWorld());
    layer->setJoint();
    scene->addChild(layer);
    return scene;
}

// on "init" you need to initialize your instance
bool HitBrick::init() {
    //////////////////////////////
    // 1. super init first
    if (!Layer::init()) {
        return false;
    }
    visibleSize = Director::getInstance()->getVisibleSize();

    auto edgeSp = Sprite::create();  //创建一个精灵
    auto boundBody = PhysicsBody::createEdgeBox(visibleSize, PhysicsMaterial(0.0f, 1.0f, 0.0f), 3);  //edgebox是不受刚体碰撞影响的一种刚体,我们用它来设置物理世界的边界
    edgeSp->setPosition(visibleSize.width / 2, visibleSize.height / 2);  //位置设置在屏幕中央
    edgeSp->setPhysicsBody(boundBody);
    addChild(edgeSp);


    preloadMusic(); // 预加载音效

    addSprite();    // 添加背景和各种精灵
    addListener();  // 添加监听器 
    addPlayer();    // 添加球与板
    BrickGeneraetd();  // 生成砖块


    schedule(schedule_selector(HitBrick::update), 0.01f, kRepeatForever, 0.1f);

    onBall = true;

    spHolded = -1;
    spFactor = 0;
    return true;
}

// 关节连接,固定球与板子
// Todo
//这个关节连接似乎有点奇怪,改变板子的位置,会导致球的位置发生震荡
void HitBrick::setJoint() {
    Vec2 fixPoint = Vec2(ball->getAnchorPoint().x, ball->getAnchorPoint().y - 30);
    joint1 = PhysicsJointPin::construct(ball->getPhysicsBody(), player->getPhysicsBody(), fixPoint, player->getAnchorPoint());
    m_world->addJoint(joint1);
}



// 预加载音效
void HitBrick::preloadMusic() {
    auto sae = SimpleAudioEngine::getInstance();
    sae->preloadEffect("gameover.mp3");
    sae->preloadBackgroundMusic("bgm.mp3");
    sae->playBackgroundMusic("bgm.mp3", true);
}

// 添加背景和各种精灵
void HitBrick::addSprite() {
    // add background
    auto bgSprite = Sprite::create("bg.png");
    bgSprite->setPosition(visibleSize / 2);
    bgSprite->setScale(visibleSize.width / bgSprite->getContentSize().width, visibleSize.height / bgSprite->getContentSize().height);
    this->addChild(bgSprite, 0);


    // add ship
    ship = Sprite::create("ship.png");
    ship->setScale(visibleSize.width / ship->getContentSize().width * 0.97, 1.2f);
    ship->setPosition(visibleSize.width / 2, 0);
    auto shipbody = PhysicsBody::createBox(ship->getContentSize(), PhysicsMaterial(100.0f, 0.0f, 1.0f));
    shipbody->setCategoryBitmask(0xFFFFFFFF);
    shipbody->setCollisionBitmask(0xFFFFFFFF);
    shipbody->setContactTestBitmask(0xFFFFFFFF);
    shipbody->setTag(Tag::SHIP);
    shipbody->setDynamic(false);
    ship->setPhysicsBody(shipbody);
    this->addChild(ship, 1);

    // add sun and cloud
    auto sunSprite = Sprite::create("sun.png");
    sunSprite->setPosition(rand() % (int)(visibleSize.width - 200) + 100, 550);
    this->addChild(sunSprite);
    auto cloudSprite1 = Sprite::create("cloud.png");
    cloudSprite1->setPosition(rand() % (int)(visibleSize.width - 200) + 100, rand() % 100 + 450);
    this->addChild(cloudSprite1);
    auto cloudSprite2 = Sprite::create("cloud.png");
    cloudSprite2->setPosition(rand() % (int)(visibleSize.width - 200) + 100, rand() % 100 + 450);
    this->addChild(cloudSprite2);
}

// 添加监听器
void HitBrick::addListener() {
    auto keyboardListener = EventListenerKeyboard::create();
    keyboardListener->onKeyPressed = CC_CALLBACK_2(HitBrick::onKeyPressed, this);
    keyboardListener->onKeyReleased = CC_CALLBACK_2(HitBrick::onKeyReleased, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(keyboardListener, this);

    auto contactListener = EventListenerPhysicsContact::create();
    contactListener->onContactBegin = CC_CALLBACK_1(HitBrick::onConcactBegin, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
}

// 创建角色
void HitBrick::addPlayer() {



    player = Sprite::create("bar.png");
    int xpos = visibleSize.width / 2;

    player->setScale(0.1f, 0.1f);
    player->setPosition(Vec2(xpos, ship->getContentSize().height - player->getContentSize().height*0.1f));
    // 设置板的刚体属性
    // Todo
    //三个参数分别是密度,恢复系数,以及摩擦,恢复系数为0,完全非弹性碰撞,恢复系数为1,完全弹性碰撞
    auto playerBody = PhysicsBody::createBox(player->getContentSize(), PhysicsMaterial(1000.0f, 1.0f, 10.0f));
    //这三个码分别表示种类,碰撞,以及是否触发监听
    //只有己方的CategoryBitmask,与对方的CollisionBitmask与运算不为0
    //并且对方的CategoryBitmask,与己方的CollisionBitmask与运算不为0,才会发生碰撞
    playerBody->setCategoryBitmask(0xFFFFFFFF);
    playerBody->setCollisionBitmask(0xFFFFFFFF);
    playerBody->setContactTestBitmask(0xFFFFFFFF);
    playerBody->setGravityEnable(false);
    playerBody->setTag(Tag::BOARD);
    player->setPhysicsBody(playerBody);
    //不旋转
    player->getPhysicsBody()->setRotationEnable(false);

    this->addChild(player, 2);

    ball = Sprite::create("ball.png");
    ball->setPosition(Vec2(xpos, player->getPosition().y + ball->getContentSize().height*0.1f));
    ball->setScale(0.1f, 0.1f);
    // 设置球的刚体属性
    // Todo
    auto ballBody = PhysicsBody::createCircle(ball->getContentSize().height / 2, PhysicsMaterial(0.1f, 1.0f, 0.0f));

    ballBody->setCategoryBitmask(0xFFFFFFFF);
    ballBody->setCollisionBitmask(0xFFFFFFFF);
    ballBody->setContactTestBitmask(0xFFFFFFFF);
    ballBody->setGravityEnable(false);
    ballBody->setTag(Tag::BALL);
    ballBody->setRotationEnable(false);
    ball->setPhysicsBody(ballBody);

    addChild(ball, 3);

}

// 实现简单的蓄力效果
// Todo
//这边加了player的position设置,因为如果不设置,那么player以一定的速度碰到边界会发生碰撞
//虽然这样可以解决碰撞的问题,但是有轻微的震荡感,感觉解决起来不是那么优雅
void HitBrick::update(float dt) {
    if (player->getPosition().x < 100) {
        player->setPosition(100,player->getPosition().y);
        player->getPhysicsBody()->setVelocity(Vec2(0, 0));
    }
    else if (player->getPosition().x > visibleSize.width - 100)
    {
        player->setPosition(visibleSize.width - 100, player->getPosition().y);
        player->getPhysicsBody()->setVelocity(Vec2(0, 0));
    }
    //如何实现蓄力效果,首先蓄力效果只要游戏刚开始的时候才有,
    //用spHolded来控制是否蓄力
    //一开始spHolded为-1,当按住space的时候,令spHolded为1,
    //那么就会立刻触发update中的这部分,就会开始加spFactor
    //再将小球以spFactor的速度发射出去就实现了蓄力
    if (spHolded == 1)
        spFactor++;
}




// Todo
void HitBrick::BrickGeneraetd() {

    for (int i = 0; i < 3; i++) {
        int cw = 30;
        while (cw <= visibleSize.width) {
            auto box = Sprite::create("box.png");
            // 为砖块设置刚体属性
            // Todo
            box->setPosition(cw, visibleSize.height - box->getContentSize().height * (i + 1));
            cw += box->getContentSize().width;

            auto boxBody = PhysicsBody::createCircle(box->getContentSize().height / 2, PhysicsMaterial(100.0f, 1.0f, 0.0f));
            boxBody->setCategoryBitmask(0xFFFFFFFF);
            boxBody->setCollisionBitmask(0xFFFFFFFF);
            boxBody->setContactTestBitmask(0xFFFFFFFF);
            boxBody->setGravityEnable(false);
            boxBody->setTag(Tag::BOX);
            boxBody->setDynamic(false);
            box->setPhysicsBody(boxBody);
            box->getPhysicsBody()->setRotationEnable(false);

            addChild(box, 4);
        }
    }

}


// 左右
//触发小球蓄力
void HitBrick::onKeyPressed(EventKeyboard::KeyCode code, Event* event) {

    switch (code) {
    case cocos2d::EventKeyboard::KeyCode::KEY_LEFT_ARROW:
        if (player->getPosition().x > 100) {
            player->getPhysicsBody()->setVelocity(Vec2(-500, 0));
        }
        else {
            //player->setPosition(100, player->getPosition().y);
            player->getPhysicsBody()->setVelocity(Vec2(0, 0));
        }
        break;
    case cocos2d::EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
        // 左右移动
        // Todo
        if (player->getPosition().x < visibleSize.width - 100) {
            player->getPhysicsBody()->setVelocity(Vec2(500, 0));
        }
        else {
            //player->setPosition(visibleSize.width-100,player->getPosition().y);
            player->getPhysicsBody()->setVelocity(Vec2(0, 0));
        }
        break;

    case cocos2d::EventKeyboard::KeyCode::KEY_SPACE:
        if (spHolded == -1)
            spHolded = 1;
        break;
    default:
        break;
    }
}

// 释放按键
//实现小球蓄力释放
void HitBrick::onKeyReleased(EventKeyboard::KeyCode code, Event* event) {
    switch (code) {
    case cocos2d::EventKeyboard::KeyCode::KEY_LEFT_ARROW:
    case cocos2d::EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
        // 停止运动
        // Todo
        player->getPhysicsBody()->setVelocity(Vec2(0, 0));
        break;
    case cocos2d::EventKeyboard::KeyCode::KEY_SPACE: 
        if (spHolded == 1) {
            spHolded = 0;
            m_world->removeJoint(joint1);
            ball->getPhysicsBody()->setVelocity(Vec2(0, spFactor * 30));
        }
        break;

    default:
        break;
    }
}

// 碰撞检测
// Todo
//加了个下雨的粒子特效
bool HitBrick::onConcactBegin(PhysicsContact & contact) {
    auto c1 = contact.getShapeA()->getBody(), c2 = contact.getShapeB()->getBody();
    auto s1 = (Sprite*)c1->getNode(), s2 = (Sprite*)c2->getNode();
    auto rain = ParticleRain::create();
    if (c1->getTag() == Tag::BOX && c2->getTag() == Tag::BALL) {
        //下雨
        rain->setPosition(s1->getPosition());
        addChild(rain);
        s1->removeFromParentAndCleanup(true);
    }
    else if (c2->getTag() == Tag::BOX && c1->getTag() == Tag::BALL) {
        rain->setPosition(s2->getPosition());
        addChild(rain);
        s2->removeFromParentAndCleanup(true);
    }
    else if (c1->getTag() == Tag::SHIP && c2->getTag() == Tag::BALL ||
        c2->getTag() == Tag::SHIP && c1->getTag() == Tag::BALL) {
        GameOver();
    }
    return true;
}


void HitBrick::GameOver() {

    _eventDispatcher->removeAllEventListeners();
    ball->getPhysicsBody()->setVelocity(Vec2(0, 0));
    player->getPhysicsBody()->setVelocity(Vec2(0, 0));
    SimpleAudioEngine::getInstance()->stopBackgroundMusic("bgm.mp3");
    SimpleAudioEngine::getInstance()->playEffect("gameover.mp3", false);

    auto label1 = Label::createWithTTF("Game Over~", "fonts/STXINWEI.TTF", 60);
    label1->setColor(Color3B(0, 0, 0));
    label1->setPosition(visibleSize.width / 2, visibleSize.height / 2);
    this->addChild(label1);

    auto label2 = Label::createWithTTF("重玩", "fonts/STXINWEI.TTF", 40);
    label2->setColor(Color3B(0, 0, 0));
    auto replayBtn = MenuItemLabel::create(label2, CC_CALLBACK_1(HitBrick::replayCallback, this));
    Menu* replay = Menu::create(replayBtn, NULL);
    replay->setPosition(visibleSize.width / 2 - 80, visibleSize.height / 2 - 100);
    this->addChild(replay);

    auto label3 = Label::createWithTTF("退出", "fonts/STXINWEI.TTF", 40);
    label3->setColor(Color3B(0, 0, 0));
    auto exitBtn = MenuItemLabel::create(label3, CC_CALLBACK_1(HitBrick::exitCallback, this));
    Menu* exit = Menu::create(exitBtn, NULL);
    exit->setPosition(visibleSize.width / 2 + 90, visibleSize.height / 2 - 100);
    this->addChild(exit);
}

// 继续或重玩按钮响应函数
void HitBrick::replayCallback(Ref * pSender) {
    Director::getInstance()->replaceScene(HitBrick::createScene());
}

// 退出
void HitBrick::exitCallback(Ref * pSender) {
    Director::getInstance()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
}

视频以及资源

视频以及资源

猜你喜欢

转载自blog.csdn.net/yaoxh6/article/details/80765819