Cocos2dx 3.2 横版过关游戏Brave学习笔记(六)
2014-09-04 17:58
381 查看
使用物理引擎
在cocos2dx中自带了一个物理引擎,可以在初始化场景时启用。
把MainScene的createScene函数修改一下:
MainScene增加setPhysicsWorld函数,用于设置一个PhysicsWorld*类型的私有变量。
然后角色类在初始化的时候设置一下body以及碰撞和接触的条件,不应发生碰撞,但需要检测到接触事件。原文里用到了Sensor,可能是版本不一样我没有发现怎么设置sensor,所以用监听接触事件代替碰撞。
这个时候运行一下,发现角色们直接开始往下掉了……原来默认重力不为0.
可以在MainScene::onEnter里将其设置为0:
另外再额外增加按钮来切换是否显示调试用的框框。
然后在MainScene中增加对物理接触的监听。
对应的监听函数为:
函数的作用主要是将接触到的敌人设置为可被攻击,在可被攻击状态如果点击这个敌人,玩家会进行攻击。
这时我发现教程好像省略了些东西。现在应该补充一下了。
敌人应该会响应触摸,然后发出一个clickEnemy消息,MainScene收到消息后,会判断敌人是否可被攻击,如果可被攻击,玩家执行attack Event,敌人执行beHit Event。
玩家会播放攻击帧动画,敌人会播放被击中帧动画。
这就需要把状态机的状态完善一下,在状态中加入"beingHit"状态, 并添加相应的用于转换的Event。在FSM::init()中
另外每个态的回调函数也要写好,在Player::initFSM()中
beingHit里可以延迟一点时间,等到刀砍下来在播放帧动画。
死亡之后尸体可以闪烁几下在消失不迟。
然后我们让敌人响应触摸事件并发送消息,Player中:
在MainScene中,接收这个消息:
接受消息的回调函数:
给Player增加生命值,最大生命值,攻击力属性,并可以用函数获取/设定。
然后上面的函数:
可以看出把状态机设置好,然后在适当时候触发事件即可。
这一次主要是关于物理引擎碰撞检测,但同时也涉及了触摸事件捕捉,事件的发放与接收,状态机的使用,等等。
现在终于可以把怪杀死了。
我想我会提交一个“Note 6” 的版本更新。https://github.com/douxt/Brave_cpp
在cocos2dx中自带了一个物理引擎,可以在初始化场景时启用。
把MainScene的createScene函数修改一下:
Scene* MainScene::createScene() { // init with physics auto scene = Scene::createWithPhysics(); auto layer = MainScene::create(); //set physics world layer->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 enemy log("contact enemy!"); playerB->setCanAttack(true); } if(typeB == Player::PlayerType::PLAYER) { // only one player so ShapeA must belong to an enemy log("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 enemy log("leave enemy!"); playerB->setCanAttack(false); } if(typeB == Player::PlayerType::PLAYER) { // only one player so ShapeA must belong to an enemy log("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; l 4000 og("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
相关文章推荐
- Cocos2dx 3.2 横版过关游戏Brave学习笔记(二)
- Cocos2dx 3.2 横版过关游戏Brave学习笔记(七)
- Cocos2dx 3.2 横版过关游戏Brave学习笔记(三)
- Cocos2dx 3.2 横版过关游戏Brave学习笔记(五)
- Cocos2dx 3.2 横版过关游戏Brave学习笔记(四)
- cocos2dx3.2 学习笔记(2)--ActionManagerTest(3)
- Unity学习笔记1 简易2D横版RPG游戏制作(一)
- Unity学习笔记3 简易2D横版RPG游戏制作(三)
- cocos2dx3.2 学习笔记(3)--ActionsTest(1)
- cocos2dx3.2 学习笔记(1)--CppTest
- cocos2dx3.2 学习笔记(3)--ActionsEaseTest(2)
- cocos2dx游戏开发——微信打飞机学习笔记(三)——WelcomeScene的搭建
- Cocos2dx游戏开发笔记21:动手学习《Flappy Bird》之继续优化(付源码)
- cocos2dx3.2学习笔记(2)cocos2d-x开发环境搭建
- cocos2dx3.2学习笔记之Director(导演类)
- cocos2dx3.2 学习笔记(3)--ActionsEaseTest(1)
- cocos2dx游戏开发学习笔记2-从helloworld开始
- Unity学习笔记-2D横版游戏中的背景移动
- Cocos2dx游戏开发笔记21:动手学习《Flappy Bird》
- cocos2dx游戏开发学习笔记3-lua面向对象分析