第三講 Touchios
前面兩篇咱們學習的內容,足夠咱們作一款簡單的小遊戲。也可以說,咱們已經入門了,可以蹣跚的走路了。windows
本篇將解說cocos2dx中很是重要的touch回調機制。你確定記得第一章作定時器時間的時候用過CC_CALLBACK_1宏定義,它讓咱們回調一個僅僅有一個形參的函數來運行定時操做。api
學習本篇前請細緻學習一下C++11的特性,std::function和lambda表達式。C++11還引入了很是多boost庫的優秀代碼,使咱們在使用的時候沒必要再加boost::,比方將要使用的std::bind;xcode
學習地址例如如下:閉包
簡單的說一下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
要使用bind和function需要引入頭文件#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)了。
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()); } }
ios下AppController.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) { }
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);