游戏有三种状态,准备开始、游戏中、游戏结束,定义一个枚举来表示:
/** * The status of game,it has three status. */
typedef enum tag_GameState {
/** The game hasn't started,but ready to start */
kGameStateReady = 1,/** The game has started,and the player is playing the game */
kGameStateStarted,/** The game has over,it means that the player has lost */
kGameStateOver
} GameState;
游戏层与控制层需要通信,因此要遵守代理协议:
class GameScene : public cocos2d::Layer,public OptionDelegate
代理协议只有一个方法,就是点击屏幕事件监听回调:
/** * The OptionDelegate method override */
void onTouch();
游戏层还需要与状态层通信,因此需要接收一个代理:
/** * The delegate of status. * When is setted,it will call StatusDelegate corresponding method on correct * time. */
CC_SYNTHESIZE(GameStatusDelegate*,_statusDelegate,StatusDelegate);
把状态层对象作为代理传到游戏层,就可以二者通信了。
下面看下初始化方法,这个是很重要的,添加物理特性,并监听触碰事件:
bool GameScene::init() {
if (!Layer::init()) {
return false;
}
auto size = Director::getInstance()->getVisibleSize();
auto origin = Director::getInstance()->getVisibleOrigin();
// Add the bird
auto bird = BirdSprite::getInstance();
bird->createBird();
auto body = PhysicsBody::create();
body->addShape(PhysicsShapeCircle::create(kBirdRadius));
body->setDynamic(true);
body->setLinearDamping(0.0f);
body->setGravityEnable(false);
body->setCategoryBitmask(0x01);
body->setCollisionBitmask(0x01);
body->setContactTestBitmask(0x01);
bird->setPhysicsBody(body);
bird->setPosition(origin.x + size.width / 3 - 5,origin.y + size.height / 2 + 5);
bird->setActionState(kActionStateIdle);
this->addChild(bird);
// Add the ground
_groundNode = Node::create();
auto groundBody = PhysicsBody::create();
auto groundSize = Size(kDesignWidth,BackgroundLayer::getLandHeight());
groundBody->addShape(PhysicsShapeBox::create(groundSize));
groundBody->setDynamic(false);
groundBody->setLinearDamping(0.0f);
groundBody->setCategoryBitmask(0x01);
groundBody->setContactTestBitmask(0x01);
groundBody->setCollisionBitmask(0x01);
_groundNode->setPhysicsBody(groundBody);
_groundNode->setPosition(groundSize.width / 2,groundSize.height);
this->addChild(_groundNode);
// land
// Add the land1 and land2
_land1 = Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrame("land"));
_land1->setAnchorPoint(Vec2::ZERO);
_land1->setPosition(Vec2::ZERO);
this->addChild(_land1,30);
_land2 = Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrame("land"));
_land2->setAnchorPoint(Vec2::ZERO);
_land2->setPosition(Vec2(_land1->getContentSize().width - 2.0f,0));
this->addChild(_land2,30);
// Add a timer to update the land
_shiftLand = schedule_selector(GameScene::scrollLand);
this->schedule(_shiftLand,0.01f);
// will call update(float delta) method
this->scheduleUpdate();
// Add contact listener
//
// If body->getCategoryBitmask() & groundBody->getContactTestBitmask() == 1
// Then we can listen the physics touch event,otherwise not.
//
// If body->getCategoryBitmask() & groundBody->getCollisionBitmask() == 1
// Then the bird and the ground can collide,otherwise not.
auto listener = EventListenerPhysicsContact::create();
listener->onContactBegin = CC_CALLBACK_1(GameScene::onContactBegin,this);
this->getEventDispatcher()->addEventListenerWithSceneGraPHPriority(listener,this);
return true;
}
要想让A和B能够在相碰时,发出事件,规则如下:
// If body->getCategoryBitmask() & groundBody->getContactTestBitmask() == 1
// Then we can listen the physics touch event,otherwise not.
要想让A和B能够发生冲突并发出事件,规则如下:
//
// If body->getCategoryBitmask() & groundBody->getCollisionBitmask() == 1
// Then the bird and the ground can collide,otherwise not.
如果不添加,默认情况下是不会监听到的,刚开始我就遇到此问题,然后通过百度才明白原因。
下面是创建场景,需要把背景层、控制层、状态层、游戏层添加到场景中:
Scene* GameScene::createScene() {
// create a scene with physics world
// 场景需要使用物理世界来创建,否则添加的物理特性就无效
auto scene = Scene::createWithPhysics();
if (scene->getPhysicsWorld()) {
scene->getPhysicsWorld()->setGravity(Vect(0,-900));
} else {
CCLOG("Error: Game scene get physics world,but it is nullptr");
}
// background layer
auto backgroundLayer = BackgroundLayer::create();
if (backgroundLayer) {
scene->addChild(backgroundLayer);
}
// game layer
auto gameLayer = GameScene::create();
// status layer
auto statusLayer = StatusLayer::create();
if (gameLayer) {
gameLayer->setPhysicsWorld(scene->getPhysicsWorld());
// 游戏层与状态层是需要通信的,把状态层作为游戏层的代理
gameLayer->setStatusDelegate(statusLayer);
gameLayer->setTag(kGameLayerTag);
scene->addChild(gameLayer);
}
if (statusLayer) {
scene->addChild(statusLayer);
}
// option layer
auto optionLayer = OptionLayer::create();
if (optionLayer) {
// 状态层与游戏层是需要通信的,把游戏层作为状态层的代理
optionLayer->setOptionDelegate(gameLayer);
scene->addChild(optionLayer);
}
return scene;
}
添加水管,给水管也添加物理特性,就可以让小鸟与水管在接触时,发出相碰事件
void GameScene::createPipes() {
// create pipes
auto size = Director::getInstance()->getVisibleSize();
for (int i = 0; i < kPipePairCount; ++i) {
auto pipeUp = Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrame("pipe_up"));
//pipeUp->setPosition(0,<#float y#>)
auto pipeDown = Sprite::createWithSpriteFrame(AtlasLoader::getInstance()->getSpriteFrame("pipe_down"));
pipeDown->setPosition(0,kPipeHeight + kPipeUpDownDistance);
auto pipeNode = Node::create();
pipeNode->setPosition(size.width + i * kPipeInterval + kWaitDistance,getRandomPipeHeight());
pipeNode->addChild(pipeDown,0,kPipeDownTag);
pipeNode->addChild(pipeUp,kPipeUpTag);
// Add physics to pipe
auto body = PhysicsBody::create();
auto Box = PhysicsShapeBox::create(pipeDown->getContentSize(),PHYSICSSHAPE_MATERIAL_DEFAULT,Vec2(0,kPipeHeight + kPipeUpDownDistance));
body->addShape(Box);
body->addShape(PhysicsShapeBox::create(pipeUp->getContentSize()));
body->setDynamic(false);
// If body->getCategoryBitmask() & 小鸟的物理body>->getCollisionBitmask() == 1
// 小鸟与水管接触才会发出事件
body->setCategoryBitmask(0x01);
body->setContactTestBitmask(0x01);
body->setCollisionBitmask(0x01);
pipeNode->setPhysicsBody(body);
pipeNode->setTag(kPipeNewTag);
this->addChild(pipeNode);
_pipes.pushBack(pipeNode);
}
}
小鸟通过水管检测:
void GameScene::checkHit() {
for (auto pipe : _pipes) {
if (pipe->getTag() == kPipeNewTag) {
// 通过一根
if (pipe->getPositionX() < BirdSprite::getInstance()->getPositionX()) { CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("sfx_point.ogg");
++_currentscore;
if (this->getStatusDelegate()) {
this->getStatusDelegate()->onGamePlaying(_currentscore);
}
pipe->setTag(kPipePassedTag);
}
}
}
}
通过遍历所有水管,如果当前是显示在屏幕上新的水管,再判断与小鸟的X坐标,来判断小鸟是否通过了水管。 当通过后,又把水管设置为已经通过的水管,如此重复使用。