Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

AppGameKit/AppGameKit Studio Showcase / [Plugin] ZInput - Comprehensive Game Controller Input System (Windows)

Author
Message
SFSW
22
Years of Service
User Offline
Joined: 9th Oct 2002
Location:
Posted: 2nd Jan 2024 21:48 Edited at: 21st Feb 2024 18:23
Attached is the ZInput controller system I use with AGK-Studio (and should also be fully compatible with AGKV2 'classic') for my projects. This is not my work, but I have asked for permission from the author to post this to share with other developers so it can also be accessible to them. The author is 'Rudolpho' here on the forum.

ZInput is a comprehensive control device input system that provides a wide array of options with a lot of customizing capabilities for range and device management. ZInput is only compatible with the Windows desktop platform (it is a native Windows library using default Windows API's after all). This post provides the download and some basic instructions on getting up and running with the library.

The benefits of ZInput include full DI8 input support beyond XInput and other systems that may limit the number of devices, axis inputs, button inputs, and/or POV/HAT inputs. ZInput can detect pretty much any device that Windows detects while also handling the full array of signals they provide. This includes gamepads, joysticks, throttles, rudders, wheels, button panels, and more. A common command interface is used to retrieve such signals, so the developer does not have to contend with split commands (such as 'slider', 'twist', 'RX/Y/Z', etc). All variable analogue signals are 'axis', all buttons are 'buttons', and all POV/HAT's are 'POV's'.

ZInput supports many simultaneous devices (far beyond the typical 6-8), 128 buttons (well beyond the typical 32 max), multiple POV/HAT's (beyond the typical 1), and as many axis inputs as can return a signal.

Another key benefit of ZInput is no dependency on outside libraries (such as 'xinput1_3.dll'), so only default Windows libraries are needed. As a result, with ZInput, you do not introduce any dependencies that require installation of outside runtimes before players can enjoy your game. This helps prevent delays, confusion, and error messages/crashes.

To install ZInput, place the files in the attached ZIP inside the plugins folder within a folder named ZInput (the ZIP contains a ZInput folder, so you can extract directly to the \plugins folder). Then in your code, it's a good idea to create some small arrays for storing device ID information for use later (we'll start with 20):



Then include this line to activate it:



A good next step is to set the error reporting mode. This tells ZInput how to handle errors if any are encountered. Here is an example:



0 is for debug with a message popup whenever there's an error, 1 is for silent and retrieving errors manually (ie error$=ZInput.GetLastErrorMessage(1)). More details and options are available in the 'Commands.txt' file included in the ZIP as the file is noted.

Next, we must initialize ZInput:



Next, it's a good idea to get the total number of available devices:



This will return the total number, if any, above zero. Keep in mind that device and signal indexes start at 0 while counts will be above 0. Next, we need to run through all of the devices to 'acquire' them. This can be done with a quick 'for i' loop and remember to start at index 0 (so if there are no devices, devicetotal will be 0, so -1 below and the loop will pass):



With that out of the way, we can start to retrieve details about each device. Again, we'll go through a 'for i' loop to retrieve and analyze each device's type (or you can combine these operations with the first 'for i' loop above):



Now that we have a list of devices we want to listen to, we can retrieve values from them at any time. Here is some example code to do so:



And that's basically it. You change the range for an axis input on the fly, listen to only select axis channels or buttons, or mix and match values for custom mappings. If you need extensive support for control devices for your project, ZInput provides an excellent array of options and can detect just about anything.

Attachments

Login to view attachments
Scribble
7
Years of Service
User Offline
Joined: 2nd Apr 2017
Location:
Posted: 7th Jan 2024 11:33
Thanks a lot for the plugin. This will definitely be useful later.

But, where is the plugin folder exactly? I'm using AppGameKit Classic. Do we need to create the plugin folder somewhere? I cannot see a plugin folder anywhere

Also, do we put the Zinput folder inside the plugin folder, or do we put the content of Zinput folder inside the plugin folder?
SFSW
22
Years of Service
User Offline
Joined: 9th Oct 2002
Location:
Posted: 7th Jan 2024 17:27
For AGKV2 'classic', the plugins folder is:

AGK2\Tier 1\Compiler\Plugins

And you just place the 'ZInput' folder with its contents as a whole inside that folder.
Virtual Nomad
Moderator
18
Years of Service
User Offline
Joined: 14th Dec 2005
Location: SF Bay Area, USA
Posted: 7th Jan 2024 18:07 Edited at: 7th Jan 2024 18:10
SFSW,

you might have a little more of a Support role than anticipated for this since it was mentioned in the Newsletter but i look forward to putting Rudolpho's work to good use someday.

thanks for passing this along and let them know we appreciate it?

ah, at a glance it seems available in both 32 and 64 but versions, right? that's gonna matter to some.
SFSW
22
Years of Service
User Offline
Joined: 9th Oct 2002
Location:
Posted: 7th Jan 2024 20:19
No problem, happy to help as I can.

Yes, both 32 and 64-bit versions are included.
Scribble
7
Years of Service
User Offline
Joined: 2nd Apr 2017
Location:
Posted: 10th Jan 2024 11:53
@SFSW
Thanks for the clarifications.
Now I've just realized I've never used even a single plugin in AppGameKit even after like what, 8 years? I must have not been living right.
haliop7
2
Years of Service
User Offline
Joined: 1st Mar 2022
Location:
Posted: 9th Feb 2024 20:52
perfect!
tarkusAB
8
Years of Service
User Offline
Joined: 15th May 2016
Location: Honolulu, Hawaii
Posted: 20th Feb 2024 03:52
Doesn't work for me. Keeps crashing if I add the line devicetotal=ZInput.GetInputDeviceCount()

I get no error report or prompt, even with error reporting mode set to 0. The game just closes.
SFSW
22
Years of Service
User Offline
Joined: 9th Oct 2002
Location:
Posted: 20th Feb 2024 17:31
The only time I've encountered something like that happening was when an external third-party software was interfering between Windows and an AppGameKit project. Check to see if you have something like DS4Windows or similar software running in the background.

Please try the attached replacement files to help debug the issue. It will generate a log file named 'zinput.log" in either the same directory as the executable or if Windows folder restrictions apply, the %localappdata% folder.

Attachments

Login to view attachments
tarkusAB
8
Years of Service
User Offline
Joined: 15th May 2016
Location: Honolulu, Hawaii
Posted: 21st Feb 2024 04:18
I don't use any such program. I double-checked to make sure. I am using an Xbox Series X/S controller connected via an official wireless Xbox USB dongle. AppGameKit Classic on Win11.

There were two changes with this new DLL:
* It created a log file in my game's directory, but it was completely empty.
* I also now get a prompt: "A critical error has occurred in a ZInput routine an needs to close" and it asked if I wanted to create a DMP file (I did).

I used WinDbg to review the DMP file. Here are some details on the crash
READ_ADDRESS: 00000000000011a8
ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.
FAILURE_BUCKET_ID: INVALID_CLASS_PTR_READ_c0000005_Windows64.dll!Unknown

I'm really only interested in this for the force feedback functionality. I tried the XInput DLL that someone else made, but that's been having its own issues. That's another story.
SFSW
22
Years of Service
User Offline
Joined: 9th Oct 2002
Location:
Posted: 21st Feb 2024 16:42 Edited at: 21st Feb 2024 18:24
Ok, thanks for testing and for the information. I think this initialization step will solve the problem and I've edited my original post above to include these lines:

s=ZInput.Initialize(0)
ZInput.RescanForInputDevices()

With those in place right after (or before) setting the error mode, it should work fine.
tarkusAB
8
Years of Service
User Offline
Joined: 15th May 2016
Location: Honolulu, Hawaii
Posted: 23rd Feb 2024 06:38
Great. That fixed it. Thank you very much.

I'm now able to start force feedback, which is great! though I can't seem to find a way to make the controller stop vibrating once it starts. Setting either motor amplitude to 0 also makes it shake.

Force starts
ZInput.SetInputDeviceForceFeedback(device, 1, 1)

Should make it stop but does nothing. If activated on its own, also starts a rumble.
ZInput.SetInputDeviceForceFeedback(device, 1, 0)
SFSW
22
Years of Service
User Offline
Joined: 9th Oct 2002
Location:
Posted: 23rd Feb 2024 16:50
Always be sure to use the correct type for each value (ie 0.0-1.0). So instead of an integer, use:

ZInput.SetInputDeviceForceFeedback(device, 1, 0.0)

That will correctly direct the rumble/FF signal to stop.
tarkusAB
8
Years of Service
User Offline
Joined: 15th May 2016
Location: Honolulu, Hawaii
Posted: 24th Feb 2024 22:39
It's really too bad, but I can't understand this force feedback behavior. It's behaving completely unituitively.

This is my code at the start


I know 10 is the number for my gamepad as I checked the list.

If I run this at the start:
ZInput.SetInputDeviceForceFeedback(device, 1, 0.0)

It makes the controller rumble and NOTHING will stop it. I would think 0.0 would stop it but it STARTS it.

If I don't do that, and say, run this in the middle of my code when i press a button:
ZInput.SetInputDeviceForceFeedback(device, 1, 1.0)

The controller doesn't rumble at all.

SFSW
22
Years of Service
User Offline
Joined: 9th Oct 2002
Location:
Posted: 24th Feb 2024 23:35 Edited at: 25th Feb 2024 05:02
Some recommendations:

- 'Acquire' all input devices before sorting through and selecting the ones you want to poll data from. I would recommend this step right after you set the devicetotal value. The 'for i' loop example in my first post above is a good way to do this:



- Remember that device indexes are difference than pointers or other different integer returns. When you are 'acquiring' devices, you're actually activating them rather than retrieving any index for them. So don't base your device polling on any return from the activation, but instead, use the device index (which starts at 0). This is why storing the device's index in an array ('deviceindex[]') is such an important step in the above sample code (you'll want this for managing multiple devices simultaneously anyway). And also keep in mind that 'DeviceCount' in the example will start counting devices starting in array index 1 (so 0 would mean no devices were added/detected). For a quicker approach to that with no selective sorting if you just want to toy around with FF motors:



- Next, verify the device you want is actually in device index 10 (in array index 11 if using the above example, or wherever else the device's index may be stored), if you start counting device indexes from 0. If it is in device index 10, then your calls should work when applying array index 11 from the list index (again, not stored when 'acquiring' to activate). So then your code might look like this:



Or if it is device index 9 as the 10th device in the list stored in array index 10, you would use:



Once you line up the actual device index (based on its index in a loop search through all detected devices, not when activating/acquiring) with the device you want to use and apply that to your commands, then they'll work with the correct device.
tarkusAB
8
Years of Service
User Offline
Joined: 15th May 2016
Location: Honolulu, Hawaii
Posted: 2nd Mar 2024 20:34
Still not working. Here is a test program I wrote. This code gives me no FF at all.

My controller is at deviceindex[11] as verified from printing the device names onto the screen.

Just in case, I even tried enabling/disabling FF on all deviceindex[0] through [20], but nothing.

Occasionally, depending on where I put the code to rumble in my proper game code, it will sometimes initiate a rumble, but it will never stop it. I use the same code in my game as I do here.

SFSW
22
Years of Service
User Offline
Joined: 9th Oct 2002
Location:
Posted: 3rd Mar 2024 19:40 Edited at: 6th Mar 2024 19:52
Are you certain you have a rumble motor in the second index (1) when you call SetInputDeviceForceFeedback? I would recommend modifying your code to include the following, which includes polling for available motors:



That will keep things lined up regardless of any device index shifting and will apply the directive to detected motors.
tarkusAB
8
Years of Service
User Offline
Joined: 15th May 2016
Location: Honolulu, Hawaii
Posted: 6th Mar 2024 05:35 Edited at: 6th Mar 2024 05:39
Yes there is a motor at index 0 and 1. Copied and pasted the code, no rumble.

I don't know what the problem is. It must be a local issue.

I want to be clear that I have gotten the controller to vibrate before with your DLL. It seems to depend on where i called it in my 30k line long program, but I could not understand the relation or how to control it. It was the same problem I was having with the XInput DLL.

Oh well. Frankly, if I'm having issues, I don't think it will be viable to ship a third-party force feedback solution for our AppGameKit game.

Thanks for your help anyways. The other functions I tried seem to work just fine
SFSW
22
Years of Service
User Offline
Joined: 9th Oct 2002
Location:
Posted: 6th Mar 2024 19:50 Edited at: 6th Mar 2024 19:53
One other detail that occurred to me that may have been left out of your test code, are you updating every device with each loop? That is, placing this somewhere in your main loop:



Without that, it's possible you'll encounter intermittent functionality. If that still doesn't help, I'd be curious to learn what controller you are trying to use and on what version of Windows.

Edit: I've updated my revision to your test code to include the device update routine.
tarkusAB
8
Years of Service
User Offline
Joined: 15th May 2016
Location: Honolulu, Hawaii
Posted: 16th Mar 2024 19:30
Doesn't work. Xbox Series controller on Win 11 but I don't care to get this working anymore.

Login to post a reply

Server time is: 2024-11-21 10:13:07
Your offset time is: 2024-11-21 10:13:07