cocos2dx 3.1從零學習(三)——Touch事件(回調,反向傳值)

第三講 Touchios

前面兩篇咱們學習的內容足夠咱們作一款簡單的小遊戲也可以說咱們已經入門了可以蹣跚的走路了windows

本篇將解說cocos2dx中很是重要的touch回調機制你確定記得第一章作定時器時間的時候用過CC_CALLBACK_1宏定義它讓咱們回調一個僅僅有一個形參的函數來運行定時操做api


回調函數的實現(Lambda表達式

學習本篇前請細緻學習一下C++11的特性std::functionlambda表達式。C++11還引入了很是多boost庫的優秀代碼使咱們在使用的時候沒必要再加boost::,比方將要使用的std::bind;xcode

學習地址例如如下閉包

C++11function使用函數

lua閉包,iosblock,C++lambda函數學習

 

簡單的說一下function的使用統一的函數定義格式function<int(int,float)>就至關於一種數據類型僅僅只是int是定義一個整形數function是定義一個函數this

function<int(int,float)>func = [](int a ,float b){return a+b;};//定義一個函數,名爲func,它的第一個形參是int,第二個形參是float,返回值是int類型。
int ret = func(3, 1.2f);

 

首先讓咱們來看一下CC_CALLBACK_的定義lua

// new callbacksbased on C++11
#defineCC_CALLBACK_0(__selector__,__target__, ...)std::bind(&__selector__,__target__, ##__VA_ARGS__)
#defineCC_CALLBACK_1(__selector__,__target__, ...)std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)
#defineCC_CALLBACK_2(__selector__,__target__, ...)std::bind(&__selector__,__target__, std::placeholders::_1,std::placeholders::_2, ##__VA_ARGS__)
#defineCC_CALLBACK_3(__selector__,__target__, ...)std::bind(&__selector__,__target__, std::placeholders::_1,std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)

事實上它們僅僅是封裝了bind的使用方法可以看到有幾個參數,bind後面就跟幾個_*。spa

要使用bindfunction需要引入頭文件#include <functional>,bind的參數類型需要引入命名空間using namespace std::placeholders;

 

#include"stdafx.h"
#include<iostream>
#include<string>
#include<functional>
using namespace std;
using namespacestd::placeholders;
 
struct Foo {
    Foo(int num) : num_(num) {}
    void print_add(int i) const { std::cout<< num_ + i << '\n'; }
    int num_;
};
int _tmain(int argc,_TCHAR* argv[])
{
    const Foo foo(123);
   function<void(int)> f_add_display =bind(&Foo::print_add, foo, _1);
return0;
}

注意:bind要綁定一個類函數的時候第二個參數必須是類對象

 

因此咱們在菜單子項綁定回調函數的時候可以不使用CC_CALLBACK_1

        auto item =MenuItemLabel::create(Label::createWithBMFont("fonts/futura-48.fnt",itemName->getCString()));
       item->setCallback(CC_CALLBACK_1(KT0618::change, this));
等價於

        auto item =MenuItemLabel::create(Label::createWithBMFont("fonts/futura-48.fnt",itemName->getCString()));
       item->setCallback(std::bind(&KT0618::change, this,std::placeholders::_1));

也等價於

        auto item =MenuItemLabel::create(Label::createWithBMFont("fonts/futura-48.fnt",itemName->getCString()));
       item->setCallback([=](Ref *ref)->void{//lambd表達式
……
         });

 

怎樣正肯定義和聲明回調函數

   當咱們create一個精靈的時候每每記不住這樣的類型的精靈的回調函數有幾個參數參數類型是什麼這裏很是重要的方法就是閱讀api咱們追蹤進去看create方法源代碼create方法中查看需要的回調函數返回值是什麼類型形參是什麼類型複製過來就能夠


    在練習過程當中咱們要儘可能不使用CC_CALLBACK_*,而是本身書寫bind函數或者lambda表達式


***************************************************************************************************************

反向傳值

    一個簡單的應用場景遊戲主界面展現最高分切換到遊戲場景完畢遊戲後要向主場景返回成績推斷是否刷新紀錄假設是的話就更新主界面的最高分

    前面咱們學習了正向傳值使用子場景的成員函數可以向子場景傳值所謂反向傳值可以理解爲子場景傳值回主場景

 

    依據咱們上面學習的function,咱們應該把主場景的函數指針利用子場景的成員函數傳遞給子場景存儲起來而後在子場景中可以調用它的成員變量來調用主場景的函數 這樣咱們切換場景的時候僅僅能pushScene,子場景使用popScene。不然主場景的對象都不存在了還怎樣實現回調呢?!

    新手可能會想再在子場景中實例化一個主場景的類對象這麼就可以傳遞值了而後使用replace切換場景而不需要這麼麻煩的傳遞一個函數指針假設按這樣的作法主場景和子場景要相互引用頭文件實例化對方違反了低耦合的原則

 

在子場景頭文件裏這麼定義

 

Public:
    std::function<void(int)> func;

咱們就可以在主場景切換到子場景的時候這樣來註冊回調函數:

auto scene =HomeWorkSnowFight::createScene();
HomeWorkSnowFight*layer = (HomeWorkSnowFight*)scene->getChildren().at(0);
layer->func = std::bind(&HomeWorkSnow::callback1,this,std::placeholders::_1 );//綁定回調函數到子場景
Director::getInstance()->pushScene(TransitionCrossFade::create(1,scene));

這樣咱們在子場景中調用func(99);就至關於調用的主場景的callback1(99)


三 touch事件

陀螺儀

    Device::setAccelerometerEnabled(true);
   // auto ac =EventListenerAcceleration::create(CC_CALLBACK_2(KT0618::accelerationc, this));
    auto ac =EventListenerAcceleration::create([&](Acceleration* acc, Event* e){
       sp->setPositionX(acc->x+sp->getPositionX());
    });
   _eventDispatcher->addEventListenerWithSceneGraphPriority(ac, this);

書寫函數的時候細緻查看api,create有幾個參數

比方陀螺儀create函數的形參格式例如如下

const std::function<void(Acceleration*, Event*)>& callback

這就說明需要傳入的參數應該是有兩個參數的void類型的函數對象

陀螺儀的代碼僅僅能在真機上測試了


鍵盤事件

xcode下是沒法模擬的僅僅有在VS下才幹測試。如下的這些代碼都是要牢記於心的,動手實現一下就明確了!

    auto keyboardLs =EventListenerKeyboard::create();
    keyboardLs->onKeyPressed =[=](EventKeyboard::KeyCode code, Event*event){
        if(code==EventKeyboard::KeyCode::KEY_A)
        {
            CCLOG("AAA");
        }
    };
    keyboardLs->onKeyReleased =[](EventKeyboard::KeyCode code, Event*event){
        CCLOG("BBB");
    };
   _eventDispatcher->addEventListenerWithSceneGraphPriority(keyboardLs,this);

鼠標事件  單點觸控

    auto listen =EventListenerTouchOneByOne::create();
    listen->onTouchBegan =CC_CALLBACK_2(KT0618::onTouchBegan, this);
    listen->onTouchMoved =CC_CALLBACK_2(KT0618::onTouchMoved, this);
    listen->onTouchEnded =CC_CALLBACK_2(KT0618::onTouchEnded, this);
 
    listen->setSwallowTouches(true);
   Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listen,this);

動手實現一下拖動一個物體到還有一個物體上僅僅需要拖動到目的的邊緣鬆開鼠標物體會本身主動放到中心處

 

b

oolKT0618::onTouchBegan(Touch *t, Event*e)
{
    Point p = t->getLocation();
    Rect rect = spMove->getBoundingBox();
    if (rect.containsPoint(p))
    {
        return true;
    }
    else
    {
        return false;
    }
    return true;
}
voidKT0618::onTouchMoved(Touch *t, Event*e)
{
    Point p = t->getLocation();
    Rect rect = spMove->getBoundingBox();
    if (rect.containsPoint(p))
    {
        spMove->setPosition(p);
    }
}
 
voidKT0618::onTouchEnded(Touch *t, Event*e)
{
    Point p = t->getLocation();
    Rect rect = spBase->getBoundingBox();
 
    if (rect.containsPoint(p))
    {
       spMove->setPosition(spBase->getPosition());
    }
}

 

多點觸控

iosAppController.mm加一句[eaglView setMultipleTouchEnabled:YES];在模擬器按住alt可以調試多點

windows就不用想了,surface除外

    auto touchMore =EventListenerTouchAllAtOnce::create();
    touchMore->onTouchesBegan =CC_CALLBACK_2(KT0618::onTouchesBegan, this);
    touchMore->onTouchesMoved =CC_CALLBACK_2(KT0618::onTouchesMoved, this);
    touchMore->onTouchesEnded =CC_CALLBACK_2(KT0618::onTouchesEnded, this);
   _eventDispatcher->addEventListenerWithSceneGraphPriority(touchMore,this);

onTouchesBegan跟單點觸控返回值不一樣,請詳細的依據api來寫

void  KT0618::onTouchesBegan(conststd::vector<Touch*>& touches, Event* events)
{
    for (auto v : touches )
    {
        v->getLocation();
    }
}
voidKT0618::onTouchesMoved(const std::vector<Touch*>& touches, Event*unused_event)
{
}
void  KT0618::onTouchesEnded(conststd::vector<Touch*>& touches, Event *unused_event)
{
}

加入本身定義消息響應EventListenerCustom

init()裏面加入例如如下代碼,那麼這個層就會響應標誌位shutdown的消息。

   auto listenCustom =EventListenerCustom::create("shutdown",CC_CALLBACK_1(KT0618::popupLayerCustom, this));
   Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(listenCustom,1);

 

這樣可以在需要發送shutdown消息的地方這樣加入,第二個參數是這條消息的本身定義參數。 

   _eventDispatcher->dispatchCustomEvent("shutdown", (void*)"Go!DongGuan!");
 

這樣就往外分發了一個名字是shutdown的消息

這個消息可以在不一樣的層中接收到利用第二個參數可以作到數據傳遞可以是不論什麼類型的數據比回調方便

其它層init也照上面加入並加入相應的響應函數

voidPopupLayer::shutdown(EventCustom * event)
{
    char * str =(char *)event->getUserData();
    CCLOG("Do not toucheme,bullshit!");
    CCLOG(str);
}


加入音效

#include<SimpleAudioEngine.h>

usingnamespace CocosDenshion;

CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic(MUSIC_BG,true);

CocosDenshion:

:SimpleAudioEngine::sharedEngine()->playEffect(MUSIC_ENEMY1);

 

音效init的時候預載入,但是要注意切換場景的時候要釋放掉預載入的音效。

CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadBackgroundMusic(MUSIC_BG);
CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect(MUSIC_BULLET);
相關文章
相關標籤/搜索