Cocos2dx 3.2 横版过关游戏Brave学习笔记(六)

来源:互联网 发布:淘宝心是怎么升级的 编辑:程序博客网 时间:2024/06/06 04:03
使用物理引擎


在cocos2dx中自带了一个物理引擎,可以在初始化场景时启用。
把MainScene的createScene函数修改一下:
Scene* MainScene::createScene(){    // init with physics    auto scene = Scene::createWithPhysics();    auto layer = MainScene::create();//set physics worldlayer->setPhysicsWorld(scene->getPhysicsWorld());    scene->addChild(layer);    return scene;}


MainScene增加setPhysicsWorld函数,用于设置一个PhysicsWorld*类型的私有变量。


然后角色类在初始化的时候设置一下body以及碰撞和接触的条件,不应发生碰撞,但需要检测到接触事件。原文里用到了Sensor,可能是版本不一样我没有发现怎么设置sensor,所以用监听接触事件代替碰撞。
auto size = this->getContentSize();auto body = PhysicsBody::createBox(Size(size.width/2, size.height));body->setCollisionBitmask(0);body->setContactTestBitmask(1);this->setPhysicsBody(body);


这个时候运行一下,发现角色们直接开始往下掉了……原来默认重力不为0. 
可以在MainScene::onEnter里将其设置为0:
void MainScene::onEnter(){Layer::onEnter();// set gravity to zero_world->setGravity(Vec2(0, 0));}


另外再额外增加按钮来切换是否显示调试用的框框。
auto debugItem = MenuItemImage::create(                                        "CloseNormal.png",                                        "CloseSelected.png",CC_CALLBACK_1(MainScene::toggleDebug, this));    debugItem->setScale(2.0);debugItem->setPosition(Vec2(VisibleRect::right().x - debugItem->getContentSize().width - pauseItem->getContentSize().width ,VisibleRect::top().y - debugItem->getContentSize().height));_menu = Menu::create(pauseItem, debugItem, NULL);_menu->setPosition(0,0);this->addChild(_menu);


然后在MainScene中增加对物理接触的监听。
_listener_contact = EventListenerPhysicsContact::create();_listener_contact->onContactBegin = CC_CALLBACK_1(MainScene::onContactBegin,this);_listener_contact->onContactSeperate = CC_CALLBACK_1(MainScene::onContactSeperate,this);_eventDispatcher->addEventListenerWithFixedPriority(_listener_contact, 10);


对应的监听函数为:
bool MainScene::onContactBegin(const PhysicsContact& contact){auto playerA = (Player*)contact.getShapeA()->getBody()->getNode();auto playerB = (Player*)contact.getShapeB()->getBody()->getNode();auto typeA = playerA->getPlayerType();auto typeB = playerB->getPlayerType(); if(typeA == Player::PlayerType::PLAYER){// only one player so ShapeB must belong to an enemylog("contact enemy!");playerB->setCanAttack(true);}if(typeB == Player::PlayerType::PLAYER){// only one player so ShapeA must belong to an enemylog("contact enemy!");playerA->setCanAttack(true);}return true;}void MainScene::onContactSeperate(const PhysicsContact& contact){auto playerA = (Player*)contact.getShapeA()->getBody()->getNode();auto playerB = (Player*)contact.getShapeB()->getBody()->getNode();auto typeA = playerA->getPlayerType();auto typeB = playerB->getPlayerType(); if(typeA == Player::PlayerType::PLAYER){// only one player so ShapeB must belong to an enemylog("leave enemy!");playerB->setCanAttack(false);}if(typeB == Player::PlayerType::PLAYER){// only one player so ShapeA must belong to an enemylog("leave enemy!");playerA->setCanAttack(false);}}


函数的作用主要是将接触到的敌人设置为可被攻击,在可被攻击状态如果点击这个敌人,玩家会进行攻击。


这时我发现教程好像省略了些东西。现在应该补充一下了。
敌人应该会响应触摸,然后发出一个clickEnemy消息,MainScene收到消息后,会判断敌人是否可被攻击,如果可被攻击,玩家执行attack Event,敌人执行beHit Event。
玩家会播放攻击帧动画,敌人会播放被击中帧动画。

这就需要把状态机的状态完善一下,在状态中加入"beingHit"状态, 并添加相应的用于转换的Event。在FSM::init()中
bool FSM::init(){this->addState("walking",[](){cocos2d::log("Enter walking");})->addState("attacking",[](){cocos2d::log("Enter attacking");})->addState("dead",[](){cocos2d::log("Enter dead");})->addState("beingHit",[](){cocos2d::log("Enter beingHit");});this->addEvent("walk","idle","walking")->addEvent("walk","attacking","walking")->addEvent("attack","idle","attacking")->addEvent("attack","walking", "attacking")->addEvent("die","idle","dead")->addEvent("die","walking","dead")->addEvent("die","attacking","dead")->addEvent("stop","walking","idle")->addEvent("stop","attacking","idle")->addEvent("walk","walking","walking")->addEvent("beHit","idle","beingHit")->addEvent("beHit","walking","beingHit")//->addEvent("beHit","attacking","beingHit") can attacking be stoped by beHit?->addEvent("die","beingHit","dead")->addEvent("stop","beingHit","idle")->addEvent("stop","idle","idle");return true;}


另外每个态的回调函数也要写好,在Player::initFSM()中
void Player::initFSM(){_fsm = FSM::create("idle");_fsm->retain();auto onIdle =[&](){log("onIdle: Enter idle");this->stopActionByTag(WALKING);auto sfName = String::createWithFormat("%s-1-1.png", _name.c_str());auto spriteFrame = SpriteFrameCache::getInstance()->getSpriteFrameByName(sfName->getCString());this->setSpriteFrame(spriteFrame);};_fsm->setOnEnter("idle",onIdle);auto onAttacking =[&](){log("onAttacking: Enter Attacking");auto animate = getAnimateByType(ATTACKING);auto func = [&](){this->_fsm->doEvent("stop");};auto callback = CallFunc::create(func);auto seq = Sequence::create(animate, callback, nullptr);this->runAction(seq);};_fsm->setOnEnter("attacking",onAttacking);auto onBeingHit = [&](){log("onBeingHit: Enter BeingHit");auto animate = getAnimateByType(BEINGHIT);auto func = [&](){this->_fsm->doEvent("stop");};auto wait = DelayTime::create(0.6f);auto callback = CallFunc::create(func);auto seq = Sequence::create(wait,animate, callback, nullptr);this->runAction(seq);};_fsm->setOnEnter("beingHit",onBeingHit);auto onDead = [&](){log("onDead: Enter Dead");auto animate = getAnimateByType(DEAD);auto func = [&](){log("A charactor died!");NotificationCenter::getInstance()->postNotification("ENEMY_DEAD",nullptr);this->removeFromParentAndCleanup(true);};auto blink = Blink::create(3,5);auto callback = CallFunc::create(func);auto seq = Sequence::create(animate, blink, callback, nullptr);this->runAction(seq);_progress->setVisible(false);};_fsm->setOnEnter("dead",onDead);}


beingHit里可以延迟一点时间,等到刀砍下来在播放帧动画。
死亡之后尸体可以闪烁几下在消失不迟。


然后我们让敌人响应触摸事件并发送消息,Player中:
_listener = EventListenerTouchOneByOne::create();_listener->setSwallowTouches(true);_listener->onTouchBegan = CC_CALLBACK_2(Player::onTouch,this);_eventDispatcher->addEventListenerWithSceneGraphPriority(_listener,this);


bool Player::onTouch(Touch* touch, Event* event){if(_type == PLAYER)return false;log("Player: touch detected!");auto pos = this->convertToNodeSpace(touch->getLocation());auto size = this->getContentSize();auto rect = Rect(size.width/2, 0, size.width, size.height);if(rect.containsPoint(pos)){NotificationCenter::getInstance()->postNotification("clickEnemy",this);log("enemy touched!");return true;}log("enemy not touched!");return false;}


在MainScene中,接收这个消息:
NotificationCenter::getInstance()->addObserver(this, callfuncO_selector(MainScene::clickEnemy),"clickEnemy",nullptr);


接受消息的回调函数:
void MainScene::clickEnemy(Ref* obj){log("click enemy message received!");auto enemy = (Player*)obj;if(enemy == nullptr){log("enemy null");return;}if(enemy->isCanAttack()){_player->attack();enemy->beHit(_player->getAttack());}else{_player->walkTo(enemy->getPosition());}}


给Player增加生命值,最大生命值,攻击力属性,并可以用函数获取/设定。

然后上面的函数:

void Player::attack(){_fsm->doEvent("attack");}void Player::beHit(int attack){_health -= attack;if(_health <= 0){_health = 0;this->_progress->setProgress((float)_health/_maxHealth*100);_fsm->doEvent("die");return;}else{this->_progress->setProgress((float)_health/_maxHealth*100);_fsm->doEvent("beHit");}}


可以看出把状态机设置好,然后在适当时候触发事件即可。


这一次主要是关于物理引擎碰撞检测,但同时也涉及了触摸事件捕捉,事件的发放与接收,状态机的使用,等等。

现在终于可以把怪杀死了。

我想我会提交一个“Note 6” 的版本更新。https://github.com/douxt/Brave_cpp










0 0
原创粉丝点击