UE5 Dev log #1

Hardware cursor creation

UE 5 allows to use Hardware cursor which is way more convenient that Software cursor as it has it's own space on the GPU and it's FPS unbound.

To properly create a Hardware cursor you need to have a *.cur asset prepared for each cursor image. The most important part is Hot Spot which essentially is the spot on the image that is a pointing pixel. Unreal Editor allows to set it in Project Settings.

Those settings didn't solve each case, for example Hot Spot didn't work in PIE.

It turns out the cur file stores the Hot Spot information as well. I used RealWorld Cursor Editor to properly setup the image.

Subsystems overriding

Some subsystems are able to self manage it's creation depending on GetDerivedClasses by ShouldCreateSubsystem implementation like in UCommonUIActionRouterBase.

In contrary UCommonInputSubsystem doesn't support it and having it derived causes both child and parent to Initialize. This causes Input to be processed twice!

The only solution here is to manually unregister the parent Subsystem.

Gamepad device wrong assignment handling solution

Since Unreal 5.0+ there's an issue with how Unreal assigns devices to players. There are several abstractions like PlatformUser, ControllerId, DeviceId and so on. Additionally each external device has it's own plugin. I prepared a solution for XInputDevice Plugin (Xbox controllers) (published on Unreal Forum):

I finally found a way to force proper controller assignments in UE 5.5. This is purely Xinput solution so only for Xbox Controllers. You need to take Xinput Plugin from engine and place it in project under Plugins/Runtime/Windows/XinputDevice (or edit in engine if from GitHub).
Next edit the GetPlatformUserAndDevice method as below.
You also need to add “EngineSettings” module to plugin’s build.cs.
This is a temporary fix that will most likely fail to work on consoles and other gamepads. I didn’t test dynamic reassignemnts as well.
/*
 * Fix/Hack for Gamepad mapping so it omits assigning the first controller to first player when requested
 */
void XInputInterface::GetPlatformUserAndDevice(int32 InControllerId, EInputDeviceConnectionState InDeviceState, FPlatformUserId& OutPlatformUserId, FInputDeviceId& OutDeviceId)
{
	bool bSkipPlayer1 = UGameMapsSettings::GetGameMapsSettings()->GetSkipAssigningGamepadToPlayer1();
	if (bIsPrimaryDevice)
	{
		if (bSkipPlayer1)
		{
			InControllerId = InControllerId + 1;
		}
		IPlatformInputDeviceMapper& DeviceMapper = IPlatformInputDeviceMapper::Get();
		DeviceMapper.RemapControllerIdToPlatformUserAndDevice(InControllerId, OUT OutPlatformUserId, OUT OutDeviceId);

		//Check if this controller was already assigned because it can be in Unknown state for some reason... And we don't want to pass it every time as we're going to be spammed with UI Editor messages
		TArray<FInputDeviceId> Devices;
		DeviceMapper.GetAllInputDevicesForUser(OutPlatformUserId, Devices);
		bool bDeviceIsAdded = Devices.Contains(OutDeviceId);
		if (InDeviceState == EInputDeviceConnectionState::Connected || InDeviceState == EInputDeviceConnectionState::Disconnected || !bDeviceIsAdded)
		{
			DeviceMapper.Internal_MapInputDeviceToUser(OutDeviceId, OutPlatformUserId, InDeviceState);
		}
	}
	else
	{
		OutDeviceId = FInputDeviceId::CreateFromInternalId(InControllerId);
	}
}

Read more