OK, lots and lots and lots of trial and error, I figured out a work around. I’m documenting this so I don’t forget how I did it, and to help others.
To review, If you want to add a custom cursor sprite by hiding the system cursor and then updating the sprite to the cursor’s position, Windows behaves erratically and the cursor flies by way to fast. Windows does not like to track the cursor position if it is hidden or has no pixels. To fix this, I change the system cursor to a 1 pixel cursor which isn’t ideal, but it works. I can make it the same color as my sprite image that follows the cursor if I want, and offset it to hide it.
To do this, open up the file CCGLViewImpl-desktop.cpp and go to void GLViewImpl::setCursorVisible( bool isVisible )
Change the code to the following:
void GLViewImpl::setCursorVisible( bool isVisible )
{
if( _mainWindow == NULL )
return;
if( isVisible )
glfwSetInputMode(_mainWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
else {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
unsigned char pixels[1 * 1 * 4];
memset(pixels, 0xff, sizeof(pixels));
GLFWimage image;
image.width = 1;
image.height = 1;
image.pixels = pixels;
GLFWcursor* cursor = glfwCreateCursor(&image, 0, 0);
glfwSetCursor(_mainWindow, cursor);
#else
glfwSetInputMode(_mainWindow, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
#endif
}
}
Now, when you hide the cursor, it will instead make the cursor a pixel dot if on win32. However I have a hacked together version of cocos2d-x v 3.17 that allows me to build WinRT projects as well, which uses a different file to hide the cursor. Hiding the cursor works correctly for Xbox, but if I build it for a UWP desktop app, it has the same issue erratic activity.
To solve it, since it seems to use GL ES with Angle to translate to DirectX instead of using OpenGL, we need to do it slightly different.
First we need to create a custom Windows cursor. Then edit a similar function to load it when it typically wants to hide it.
- Right click your Game’s project in the Solution Explorer and select Add New Item, select Visual C++ -> Resource->Resource File(.rc). I just left it as Resource.rc
- The Solution Explorer switched to Resource View, Right click your newly added resource and click “Add Resource”, select Cursor, and click “New”
- Draw whatever you want the cursor to look like… mine again will just be one pixel in the upper left corner.
- We need to find out what the Resource ID will be for the cursor. To do this, Go back to the Solution Explorer in Resource View. Right click Resource.rc, or whatever you named it. Select “Resource Symbols”. It should list your cursor and a value. Mine is 101. If you add other cursors this number will change. This is the cursor resource ID.
- Change code to Cocos2dEngine/OpenGLESPage.xaml/OpenGLESPage.xaml.cpp, this time the function you want to edit is CreateInput()
void OpenGLESPage::CreateInput()
{
// Register our SwapChainPanel to get independent input pointer events
auto workItemHandler = ref new WorkItemHandler([this](IAsyncAction ^)
{
// The CoreIndependentInputSource will raise pointer events for the specified device types on whichever thread it’s created on.
mCoreInput = swapChainPanel->CreateCoreIndependentInputSource(
Windows::UI::Core::CoreInputDeviceTypes::Mouse |
Windows::UI::Core::CoreInputDeviceTypes::Touch |
Windows::UI::Core::CoreInputDeviceTypes::Pen
);
// Register for pointer events, which will be raised on the background thread.
mCoreInput->PointerPressed += ref new TypedEventHandler<Object^, PointerEventArgs^>(this, &OpenGLESPage::OnPointerPressed);
mCoreInput->PointerMoved += ref new TypedEventHandler<Object^, PointerEventArgs^>(this, &OpenGLESPage::OnPointerMoved);
mCoreInput->PointerReleased += ref new TypedEventHandler<Object^, PointerEventArgs^>(this, &OpenGLESPage::OnPointerReleased);
mCoreInput->PointerWheelChanged += ref new TypedEventHandler<Object^, PointerEventArgs^>(this, &OpenGLESPage::OnPointerWheelChanged);
if (GLViewImpl::sharedOpenGLView() && !GLViewImpl::sharedOpenGLView()->isCursorVisible())
{
if (Windows::System::Profile::AnalyticsInfo::VersionInfo->DeviceFamily == "Windows.Desktop") {
Windows::UI::Core::CoreCursorType cursorType = Windows::UI::Core::CoreCursorType::Custom;
Windows::UI::Core::CoreCursor ^* theCursor = new Windows::UI::Core::CoreCursor ^ (nullptr);
*theCursor = ref new Windows::UI::Core::CoreCursor(cursorType, 101); //101 is the resource id number for my cursor
mCoreInput->PointerCursor = *theCursor;
}
else {
mCoreInput->PointerCursor = nullptr;
}
}
// Begin processing input messages as they're delivered.
mCoreInput->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit);
});
// Run task on a dedicated high priority background thread.
mInputLoopWorker = ThreadPool::RunAsync(workItemHandler, WorkItemPriority::High, WorkItemOptions::TimeSliced);
}