Beginner guide to desktop controller input?

hello,
I have search a lot in google and this forum about how to input a gamepad controller, but didn’t really succed to follow how to actually make it work. Some topic are very old, and some other have snippets of code that didn’t really help.
Can someone provide some beginner code to make it work please? I tried with a very simple test. All I want is to see if the event is triggered when I press down something.

#include <CCGameController.h>
USING_NS_CC;

//....

void Player::gamePadListener()
{
	Controller::startDiscoveryController();

	auto eventListenerController = EventListenerController::create();

	eventListenerController->onKeyDown = [=](cocos2d::Controller* controller, int key_code, cocos2d::Event* evt) {
        CCLOG("gamepad key pressed %i", key_code);
    };

	this->_eventDispatcher->addEventListenerWithSceneGraphPriority(eventListenerController, this);

}

I get the following:

ControllerImpl: Could not find a button input map for controller: Xbox Controller
ControllerImpl: Could not find an axis input map for controller: Xbox Controller

Does that means that it only works with an Xbox Controller or am I doing something wrong? I have tried this with 2 controllers: one, the xbox one wireless controller, an two a nacom controller (so a random controller).

Can someone provide some “guide for dummys” . Many topic about it seem to be already in the assumptions on how all this works. How can we make any controller work on windows, mac or linux?? is this too complex to achieve with cocos2dx? Because obviously, every user have a different type of controller. I am even playing games with the ps5 controller on pc.

And one last thing. They keyboard event listener works perfectly fine. I have somthing like this

void Player::createEventListenerKeyboard()
{
	auto eventListenerKeyboard = EventListenerKeyboard::create();  // hacer attr privado?
	eventListenerKeyboard->onKeyPressed = [=](EventKeyboard::KeyCode code, Event* event) {
        //....some code
        }      
	this->_eventDispatcher->addEventListenerWithSceneGraphPriority(eventListenerKeyboard, GameGlobals::playerSprite);
}

my question is about the last line.

	this->_eventDispatcher->addEventListenerWithSceneGraphPriority(eventListenerKeyboard, GameGlobals::playerSprite);

if go back to the controller code and do this

// this doesn't crash
// this->_eventDispatcher->addEventListenerWithSceneGraphPriority(eventListenerController, this);  

// this crashes
this->_eventDispatcher->addEventListenerWithSceneGraphPriority(eventListenerController, GameGlobals::playerSprite);

then the game crashes before it starts with the error

Program: ...in\bin\spaceShooter_0.1\Debug\spaceShooter_0.1.exe
File: D:\CHIMERA STUDIO\CHIMERA GAMES\lea...\CCEvent...her.cpp
Line: 504

Expression: listener && node

For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts

so why using GameGlobals::playerSprite crashes for the controller, but works fine in the keyboard?

Any, I hope someone shed some light on this because I am about to publish my first game and I can’t if I don’t figure out all this.

thanks in advance

I kept digging and managed to make a callback work detecting if the controller is connected or not:

void key_callback(int key, int event)
{
	if (event == GLFW_CONNECTED)
        CCLOG("Gamepad connected");
    if (event == GLFW_DISCONNECTED)
        CCLOG("Gamepad disconnected");
	if (event == GLFW_GAMEPAD_BUTTON_A)
		CCLOG("Button A pressed.");
}

bool Player::init()
{
	auto _listener = EventListenerController::create();
	glfwSetJoystickCallback(key_callback);

	return true;
}

As a result, I got the CCLOG printed correctly when I plug and unplug the game controller but it ignores
event == GLFW_GAMEPAD_BUTTON_A. Which is weird because if I code directly in Player::update()
this :

void Player::update()
{	
	GLFWgamepadstate state;
	if (glfwGetGamepadState(GLFW_JOYSTICK_1, &state))
	{
		if (state.buttons[GLFW_GAMEPAD_BUTTON_A])
		{
			CCLOG("button A pressed");
		}
	}
//...
}

this does work, but the problem is that is is evaluating for each loop cycle, so it is like it repressed again, and again , and again, which is not what I want. I just want to be pressed once.

I don’t know if cocos2dx is defintely left to die and that’s why I am not getting any support, or if it is because no one uses cocos2dx for desktop gaming, so there is few info about this. In any case, please if any cocos developper reads this I would appreciate some guidance.

thank you.

Have you used a debugger to dig into why it’s not working as you expect it to? The debugger will, more often than not, help you find a solution to the problem.

are you able to create a custom event listener, put the code above inside the listener then call the listener in the “onLoad” method?

it would add the listener onload and trigger the event once when a button is pressed.

you can also create states that move from pressed-> event-> idlestate on end. the state can only be changed by a button etc maybe.

i use JavaScript/TypeScript so idk what the event system is like.

R101 yes, I use always the debugger, I did post the error.
I figured out a way to make it work. but it might be a bit of overkill.
I initialized some booleans in the header such as bool BUTTON_A = false;
And then in the update method I did this to make it work only on one press:

GLFWgamepadstate state;
if (glfwGetGamepadState(GLFW_JOYSTICK_1, &state))
{
	if (state.buttons[GLFW_GAMEPAD_BUTTON_A] && BUTTON_A)
	{
			bulletInstance->shoot(GameGlobals::playerSprite->getPosition() + Vec2(80, -5), PLAYER_SHOT_DEFAULT_SPRITE_PNG);
			AudioEngine::play2d(PLAYER_LASERSHOT_FX, false, 0.4f);
			BUTTON_A = false;
	}
	else if (!state.buttons[GLFW_GAMEPAD_BUTTON_A])
			BUTTON_A = true;
}

this works pretty well. I am not a fan of the if else statements, but at least it works and there it keeps constant at 60 fps.

CVIGx, can you provide a snippet code? thanks for the reply but it is a bit hard to figure out, at least for me when I am new to a subject, if I don’t actually see some code.

R

I’m not quite sure what you mean by that, as the error has nothing to do with a debugger. You can set breakpoints and step through the relevant section of engine code to see why it’s not doing what you expect it to (before it crashes/returns etc.).

This may seem like me stating the obvious, but if you if don’t use the debugger to trace through the code, then all you’re doing is guessing, and it is just going to cost you even more time before you find a solution.

Yes, i did set break points. The error happenned at the breakpoint where i commented //this crashes. I moved it out of the init() and it was okey then.
But i am not using that now anyways. I am using what i mentioned in my previous reply.

There is no way to get pressed button via callback according to the doc


https://www.glfw.org/docs/3.3/input_guide.html#events

ahh, damn it, it seems that glfw doesn’t have a rumble feature right?
Any idea how to implemente vibration on the gamepad?