Cocos2d-x之監聽鍵盤按住事件

我只想讓精靈一直向前走,爲何這麼難?html

zombie

一切故事發生的背景

同濟大學軟件學院每一個學期會要求學生獨立或者組隊完成一個大項目,因而2016年大項目是用cocos2d-x這款引擎製做一個本身的遊戲。c++

有幸能和<stroke>大神</stroke>組到了一塊兒,造成了兩人小分隊。編程

C++語言方面我還真的沒有能夠拿得出手的東西,可是在cocos2d-x的學習中,仍是略有收穫的。今天想談談經過監聽鍵盤事件從而讓精靈一直走路,鬆開按鍵精靈就停下的功能。就好像騎車同樣,按住一顆按鍵,就會向前行駛,不按就停下來。ide

一個簡單的Sprite

先在Demo.h裏聲明出一個sprite成員做爲咱們今天的主角,顧名思義我叫它hero,由於是簡單的例子,就不興師動衆建立一個嚴謹的主角類了。函數

# Demo.h

class HelloWorld : public cocos2d::Layer
{
public:
    static cocos2d::Scene* createScene();

    virtual bool init();
    cocos2d::Sprite *hero;
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    void update(float delta) override;
    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);
};

添加一個"殭屍"sprite

再在Demo.cpp中實實在在建立出這個帶有紋理的sprite,設置居中位置,並添加到場景中。
就像開頭貼出的效果圖,咱們可以控制的精靈紋理是一張《植物大戰殭屍》中殭屍的貼圖,而背景其實也是一個精靈對象,相關代碼就不在此列出了。學習

# Demo.cpp

hero = Sprite::create("zombie.png");
// position the sprite on the center of the screen
hero->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
// add the sprite as a child to this layer
this->addChild(hero, 0);

添加鍵盤事件監聽

我相信你必定會一些基本的事件監聽(Event Dispatcher)使用方法了吧,若是不是,請參照官方文檔中的該章節ui

固然其中有一些鼠標監聽之類的方法不是咱們今天須要的,EventListenerKeyboard纔是今天的重頭戲。因而咱們在Demo.cpp 中建立一個監聽器,並賦予一個lambda函數給它,讓它能知道監聽了鍵盤事件後該作什麼事情。this

# Demo.cpp

auto listener = EventListenerKeyboard::create();
listener->onKeyPressed = [=](EventKeyboard::KeyCode keyCode, Event* event){
    log("key pressed");
};

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

這樣一來,按任意鍵,控制檯就會輸出"key pressed"字樣的信息了,這爲咱們下一步作了良好的鋪墊。傳進來的參數是什麼意思?沒必要解釋了吧...url

記錄一共按過哪些鍵,以及其狀態

咱們還要記錄一下一共按過了哪些鍵,以及如今它們是否還被按着。通俗地講,就是用KeyPressed事件將當期按下的鍵信息保存起來,表示它如今被按下了。再判斷觸發KeyReleased事件時,將觸發的按鍵信息去除掉,表示如今這顆鍵已經再也不處於被按下的狀態了。spa

因而咱們在頭文件裏建立一個map類型的成員,取名爲keys,用以保存咱們按鍵記錄。

# Demo.h

class HelloWorld : public cocos2d::Layer
{
public:
    static cocos2d::Scene* createScene();

    virtual bool init();
    cocos2d::Sprite *hero;
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    void update(float delta) override;
    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);
private:
      std::map<cocos2d::EventKeyboard::KeyCode, bool> keys;
};

map鍵爲每一個按鍵所對應的keycode, 值爲如今這顆按鍵是否是處於被按下的狀態,正在被按爲true, 沒被按(即 鬆開)爲false。

固然,咱們還要爲鍵盤監聽器添加一個KeyReleased事件,用以監放任意按鍵被鬆開那一刻的狀態。

# Demo.cpp

listener->onKeyReleased = [=](EventKeyboard::KeyCode keyCode, Event* event){
   log("key release");
};

簡簡單單一些在控制檯的輸出是遠遠不夠的,如今開始改進咱們的倆監聽事件,讓它們能增長或改變keys裏的記錄。

# Demo.cpp

listener->onKeyPressed = [=](EventKeyboard::KeyCode keyCode, Event* event){
    keys[KeyCode] = true;
};

listener->onKeyReleased = [=](EventKeyboard::KeyCode keyCode, Event* event){
   keys[KeyCode] = false;
};

你問我爲什麼能夠寫得如此草率簡單,然而事實就是如此。在鬆開事件發生的時候,根本不須要判斷在keys是否是已經存在了這個按鍵,由於,被鬆開就表明着,確定被按下了。固然,咱們排除在遊戲以外的窗口按下,再切換到遊戲內鬆開的狀況,對於這種非法份子,今天網開一面。

Sprite show me your hands

老朋友update事件

每個由Node類繼承下來的子類中,不管你是精靈類,場景類,都有一個update方法,當判斷髮生改變時,可以作出一些反應。

要使用update,先要了解三種調度器Scheduler

咱們爲Demo類添加一個默認調度器,表示它要使用update方法。而且添加一些簡單的代碼到update事件中去。

# Demo.cpp

bool HelloWorld::init() {
    ...
    this->scheduleUpdate();
}

void HelloWorld::update(float delta) {
    // Register an update function that checks to see if the CTRL key is pressed
    // and if it is displays how long, otherwise tell the user to press it
    Node::update(delta);
    log("update");
}

打開遊戲之後,就會看見控制檯正在瘋狂地輸出"update",說明咱們的默認調度器監聽了每一幀的改變並執行了咱們自定義的操做。

你真的...一直在按着我嗎?

咱們建立一個方法,叫作isKeyPressed,用來判斷一個按鍵是否當前是否處於被按下的狀態,若是是,返回true,交給update來處理後續動做。

# Demo.cpp

bool HelloWorld::isKeyPressed(EventKeyboard::KeyCode keyCode) {
    if(keys[keyCode]) {
        return true;
    } else {
        return false;
    }
}

時時刻刻判斷特定的按鍵是否被按下

除了上下左右鍵,其餘的咱們都不須要理會,這個需求如何實現呢?

咱們要讓以前建立好的hero左右動,那就要在update事件中時時刻刻判斷着KEY_LEFT_ARROW或者KEY_RIGHT_ARROW是否被按下,若是是,就對主角執行動做(action),沒有,就忽略。

咱們將update方法進行改寫:

# Demo.cpp
void HelloWorld::update(float delta) {
    Node::update(delta);
    auto leftArrow = EventKeyboard::KeyCode::KEY_LEFT_ARROW, rightArrow = EventKeyboard::KeyCode::KEY_RIGHT_ARROW;
    if(isKeyPressed(leftArrow)) {
        keyPressedDuration(leftArrow);
    } else if(isKeyPressed(rightArrow)) {
        keyPressedDuration(rightArrow);
    }
}

等等,keyPressedDuration是什麼東西,內置方法嗎?咱們接着看。

Hero: 我該作些什麼呢?

差很少都快要搞定的時候,咱們的hero發現了一個嚴重的問題 --- 它該作些什麼。

咱們新建立一個方法叫作keyPressedDuration,表示着按下/按着對應按鍵後,hero應該作些什麼。

# Demo.cpp

void HelloWorld::keyPressedDuration(EventKeyboard::KeyCode code) {
    int offsetX = 0, offsetY = 0;
    switch (code) {
        case EventKeyboard::KeyCode::KEY_LEFT_ARROW:
            offsetX = -5;
            break;
        case EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
            offsetX = 5;
            break;
        default:
            offsetY = offsetX = 0;
            break;
    }
    // 0.3s表明着動做從開始到結束所用的時間,從而顯得不會那麼機械。
    auto moveTo = MoveTo::create(0.3, Vec2(hero->getPositionX() + offsetX, hero->getPositionY() + offsetY));
    hero->runAction(moveTo);
}

咱們判斷code變量從而得知用戶按了左右鍵中的哪個,再執行對應的動做。

爛尾

最後的最後,咱們將代碼一整合,按住左鍵或者右鍵,就能夠看見hero自由地走動啦。

原本做爲一名cocos2d-x的初學者,遇到這種問題,我一般就面向csdn和stackoverflow編程了,經過他們來解決個人問題。可無奈,我居然難以找到令我真正看得懂的教程,英文尚有幾篇,而中文關於這方面的根本沒有。

既然沒有,雖然我纔剛剛學習,但本着不逃避的態度,那就我來寫一篇吧。

下一章就該講講我和物理引擎的一些故事了,下期再見。

以上。

相關文章
相關標籤/搜索