LoginSignup
5
5

More than 5 years have passed since last update.

cocos2d-x v3.x win32でゲームパッド利用する

Posted at

cocos2d-x v3.x win32でゲームパッドを利用するためのメモです。
軽く調べた感じcocos側に対応がなかったので、Android/iOSにあるcontroller系の機能をwin32対応してます。(glfwにjoystick系の処理あるんですが何か上手く動かなかったので、joyGetPosEx使ってます)
v3.3で検証しましたが、たぶんv3.x系ならいけると思います。

大まかな流れとしては、以下な感じ。
1. テスト用プロジェクトの準備
2. controller系イベントの修正
3. win32向けゲームパッド関連の処理を追加

テスト用プロジェクトの準備

win32にはそもそもgame-controller-test自体が存在していないので、それを準備する。

  • cpp-empty-tests/proj.win32フォルダをcpp-empty-tests/game-controller-testへリネームコピー。
  • 同フォルダにあるcpp-empty-tests.*をgame-controller-test.*へリネーム。
  • HelloWorldScene.cpp/hをプロジェクトから削除。
  • GameControllerTest.cpp/hをプロジェクトに追加
  • cpp-empty-tests.vcxprojをcocos2d-win32.vc2012.slnへ追加

これでテスト用プロジェクトの準備は終了。この段階ではgame-controller-testをビルドしてもリンクエラーがでるので注意。

controller系イベントの修正

Android/iOS用にマスクされている箇所を修正、不足しているファイルをlibcocos2dプロジェクトへ追加する。また不足している処理を追加する。

  • cocos2d/baseに存在する CCEventController.cpp/hをlibcocos2dプロジェクトへ追加。
  • 同じくCCEventListenerController.cpp/hを追加。
  • 同じくCCController.cpp/hを追加。
  • CCController-android.cppをCCController-win32.cppへリネームコピーし、libcocos2dプロジェクトへ追加。
  • Android/iOSのプラットフォーム依存箇所をwin32向けに修正。
CCController.cpp
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || 
CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
CCEventDispatcher.cpp
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || 
CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
CCController-win32.cpp
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
//#include "jni/JniHelper.h"//いらない
CCController-win32.cpp
void Controller::receiveExternalKeyEvent(int externalKeyCode,bool receive)
{
//実装削る
}
CCController-win32.cpp
//adapter系の関数をwin32用にリネームして引数を修正
    void CC_DLL win32_cocos2dx_lib_GameControllerAdapter_nativeControllerConnected(const char* deviceName, int controllerID)
    {
        CCLOG("controller id: %d connected!", controllerID);
        cocos2d::ControllerImpl::onConnected(std::string(deviceName), controllerID);
    }

    void CC_DLL win32_cocos2dx_lib_GameControllerAdapter_nativeControllerDisconnected(const char* deviceName, int controllerID)
    {
        CCLOG("controller id: %d disconnected!", controllerID);
        cocos2d::ControllerImpl::onDisconnected(std::string(deviceName), controllerID);
    }

    void CC_DLL win32_cocos2dx_lib_GameControllerAdapter_nativeControllerButtonEvent(const char* deviceName, int controllerID, int button, bool isPressed, float value, bool isAnalog)
    {
        cocos2d::ControllerImpl::onButtonEvent(std::string(deviceName), controllerID, button, isPressed, value, isAnalog);
    }

    void CC_DLL win32_cocos2dx_lib_GameControllerAdapter_nativeControllerAxisEvent(const char* deviceName, int controllerID, int axis, float value, bool isAnalog)
    {
        cocos2d::ControllerImpl::onAxisEvent(std::string(deviceName), controllerID, axis, value, isAnalog);
    }
  • そのままだとリンクエラーになるので、CC_DLLを追加
CCController.h
class CC_DLL Controller
CCEventListenerController.h
class CC_DLL EventListenerController : public EventListener

ここまでの作業でビルドが通り、game-controller-testが実行できるようになるはず。
ただし、実行はできるがまだコントローラーは反応しない。
Resourceのロードが失敗する場合は、デバッグ→作業ディレクトリに$(ProjectDir)..\Resourcesを追加する。

GameControllerTest.png

win32向けゲームパッド関連の処理を追加

今のままだとゲームパッドの入力を受けてcocosのeventを発行する箇所がないので、それを追加する。
今回はGameControllerTest.cppに追加する。

GameControllerTest.cpp
//updateに処理を書くので、scheduleUpdate有効にしておく
void GameControllerTest::registerControllerListener()

    scheduleUpdate();
GameControllerTest.cpp
//イベントの発行
extern "C" {

    void win32_cocos2dx_lib_GameControllerAdapter_nativeControllerConnected(const char* deviceName, int controllerID);
    void win32_cocos2dx_lib_GameControllerAdapter_nativeControllerDisconnected(const char* deviceName, int controllerID);
    void win32_cocos2dx_lib_GameControllerAdapter_nativeControllerButtonEvent(const char* deviceName, int controllerID, int button, bool isPressed, float value, bool isAnalog);
    void win32_cocos2dx_lib_GameControllerAdapter_nativeControllerAxisEvent(const char* deviceName, int controllerID, int axis, float value, bool isAnalog);

} // extern "C" {
#include <array>
struct ControllerInfo{
    ControllerInfo()
    :_isConnected(false)
    , _button(0)
    , _joy(0)
    {
    }
    bool    _isConnected;
    int     _button;
    int     _joy;
};
std::array<ControllerInfo, 32>  _controllerInfos;

void GameControllerTest::update(float delta)
{
    JOYINFOEX JoyInfoEx;
    JoyInfoEx.dwSize = sizeof(JOYINFOEX);
    JoyInfoEx.dwFlags = JOY_RETURNALL;
    for (uint32_t i = 0; i < joyGetNumDevs(); i++){
        bool connected = false;
        if (JOYERR_NOERROR == joyGetPosEx(i, &JoyInfoEx)){
            connected = true;
        }
        char deviceName[64];
        sprintf(deviceName, "pad%d", i);
        if (_controllerInfos[i]._isConnected != connected){
            if (connected){
                win32_cocos2dx_lib_GameControllerAdapter_nativeControllerConnected(deviceName, i);
            }
            else{
                win32_cocos2dx_lib_GameControllerAdapter_nativeControllerDisconnected(deviceName, i);
            }
        }
        _controllerInfos[i]._isConnected = connected;

        if (connected){
            struct InputTable{
                int _src;
                int _dst;
            };
            static InputTable   buttonTable[] = {
                { 1 << 0, Controller::Key::BUTTON_A },
                { 1 << 1, Controller::Key::BUTTON_B },
                { 1 << 2, Controller::Key::BUTTON_X },
                { 1 << 3, Controller::Key::BUTTON_Y },
            };
            auto buttonDiff = _controllerInfos[i]._button ^ JoyInfoEx.dwButtons;
            for (auto& j : buttonTable){
                if (buttonDiff&j._src){
                    bool isPressed = (_controllerInfos[i]._button&j._src) ? (false) : (true);
                    win32_cocos2dx_lib_GameControllerAdapter_nativeControllerButtonEvent(deviceName, i, j._dst, isPressed, 0, false);
                }
            }
            int joy = 0;
            switch (JoyInfoEx.dwPOV){
            case 0://up
                joy = 1 << 0;
                break;
            case 4500:
                joy = 1 << 0 | 1 << 1;
                break;
            case 9000://right
                joy = 1 << 1;
                break;
            case 13500:
                joy = 1 << 1 | 1 << 2;
                break;
            case 18000://down
                joy = 1 << 2;
                break;
            case 22500:
                joy = 1 << 2 | 1 << 3;
                break;
            case 27000://left
                joy = 1 << 3;
                break;
            case 31500:
                joy = 1 << 3 | 1 << 0;
                break;
            }
            static InputTable   joyTable[] = {
                { 1 << 0, Controller::Key::BUTTON_DPAD_UP },
                { 1 << 1, Controller::Key::BUTTON_DPAD_RIGHT },
                { 1 << 2, Controller::Key::BUTTON_DPAD_DOWN },
                { 1 << 3, Controller::Key::BUTTON_DPAD_LEFT },
            };
            auto joyDiff = _controllerInfos[i]._joy ^ joy;
            for (auto& j : joyTable){
                if (joyDiff&j._src){
                    bool isPressed = (_controllerInfos[i]._joy&j._src) ? (false) : (true);
                    win32_cocos2dx_lib_GameControllerAdapter_nativeControllerButtonEvent(deviceName, i, j._dst, isPressed, 0, false);
                }
            }
            float x = (float)JoyInfoEx.dwXpos / 32768.f - 1.f;
            win32_cocos2dx_lib_GameControllerAdapter_nativeControllerAxisEvent(deviceName, i, Controller::Key::JOYSTICK_LEFT_X, x, true);
            float y = (float)JoyInfoEx.dwYpos / 32768.f - 1.f;
            win32_cocos2dx_lib_GameControllerAdapter_nativeControllerAxisEvent(deviceName, i, Controller::Key::JOYSTICK_LEFT_Y, y, true);

            _controllerInfos[i]._button = JoyInfoEx.dwButtons;
            _controllerInfos[i]._joy = joy;
        }
    }
}

これでゲームパッドの接続を検出し、基本的なボタン等が利用できるはずです。
ゲームパッドはボタンの差が結構あるので、キーアサインなどは別途対応の必要があります。
また現状のままだと、都度updateにゲームパッド処理書くのが手間なので、何か適当なcustom event準備してlibcocos2d側に追加しておくと楽かも?

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5