cocos2d-x v3.x win32でゲームパッドを利用するためのメモです。
軽く調べた感じcocos側に対応がなかったので、Android/iOSにあるcontroller系の機能をwin32対応してます。(glfwにjoystick系の処理あるんですが何か上手く動かなかったので、joyGetPosEx使ってます)
v3.3で検証しましたが、たぶんv3.x系ならいけると思います。
大まかな流れとしては、以下な感じ。
- テスト用プロジェクトの準備
- controller系イベントの修正
- 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向けに修正。
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS ||
CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS ||
CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
//#include "jni/JniHelper.h"//いらない
void Controller::receiveExternalKeyEvent(int externalKeyCode,bool receive)
{
//実装削る
}
//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を追加
class CC_DLL Controller
class CC_DLL EventListenerController : public EventListener
ここまでの作業でビルドが通り、game-controller-testが実行できるようになるはず。
ただし、実行はできるがまだコントローラーは反応しない。
Resourceのロードが失敗する場合は、デバッグ→作業ディレクトリに$(ProjectDir)..\Resourcesを追加する。
win32向けゲームパッド関連の処理を追加
今のままだとゲームパッドの入力を受けてcocosのeventを発行する箇所がないので、それを追加する。
今回はGameControllerTest.cppに追加する。
//updateに処理を書くので、scheduleUpdate有効にしておく
void GameControllerTest::registerControllerListener()
scheduleUpdate();
//イベントの発行
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側に追加しておくと楽かも?