chrisdill / raylib-cs

C# bindings for raylib, a simple and easy-to-use library to learn videogames programming

Home Page:http://www.raylib.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

In the `InputGamepad` demo the gamepad image is never displayed. There are multiple contributing factors and related problems.

WraithGlade opened this issue · comments

I'm using the 4.5.0.4 release version of the Raylib C# bindings, i.e. the most recent stable version (not the bleeding edge dev commits).

I've been messing around with the C# demos and have noticed that the InputGamepad demo never shows the nice/useful animated diagram image of the Xbox or PS controller that is supposed to show and highlight which button you are pressing etc.

As I discovered in the process of trying to get the demo working fully on C#, there are two associated problems contributing to this:

Problem 1

The resource (aka asset) loading function calls never actually find the images they are supposed to because the files are not located in paths that are relative to the final executable file. The only way I could get it working on mine was to add a post-build event to the project's properties that manually copies the images to the bin folder each time the project is built.

Here is the failing image loading code:

Texture2D texPs3Pad = LoadTexture("resources/ps3.png");
Texture2D texXboxPad = LoadTexture("resources/xbox.png");

And here is the post-build event batch file code I added (by right clicking the project and changing the associated properties) to fix it:

xcopy "$(ProjectDir)\resources" "$(TargetDir)\resources" /I /Y /E

It should be noted that I have been putting each of the demos I mess with into a separate project (each set up to use Raylib-cs, by copying the projects and then tweaking their names etc), because the giant examples project associated with Raylib C# that runs every demo in sequence is extremely unwieldy for actually experimenting or testing anything and hence is nearly useless for anything but getting a one-time overview of the demos. The demos really should all be in separate well-organized projects (within one project for every single file that has a Main function) all within one solution, which is far more useful and manageable than running them all in sequence.

PS: Visual studios built-in "Resource" system seems to not really be compatible with Raylib's C-like old school way of loading files. Don't let the coincidental name mislead you. Trying to get the art files loaded via Visual Studio's resource system was something I tried but was a waste of time (though probably technically possible with enough effort). Unless I'm missing something, the above post-build batch file approach seems to work better as far as I can tell so far.

Problem 2

I'm using an Xbox One controller for PC connected by wired USB. (Either that or it is 360, but I think it is Xbox One.)

(Side-note: GetGamepadName_ and GetGamepadName always return an empty string if used before the WindowShouldClose function call. This is not very logical behavior considering that the ability to retrieve a gamepad's name should be possible before the main loop of a game begins, since that is actually a more likely intent during initialization except during connects/disconnects. However, the C version of Raylib behaves the same except that it returns "(null)" instead of "", so this is a problem with Raylib's design rather than the C# bindings.)

However, even within the WindowShouldClose loop, the name that is returned differs greatly from the constants defined at the top of the file. The name of the Xbox controller for me is "Xbox Controller" and certainly not just "Xbox". Thus, the following constants in the InputGamepad demo file are likely wrong (perhaps outdated?):
wrong:

    public const string XBOX360_NAME_ID = "Xbox";
    public const string PS3_NAME_ID = "PLAYSTATION(R)3";

Is this specific to just my own controller or are these constants universally wrong across the current up-to-date Raylib bindings?

The analogous part of the original C based Raylib library has the following at the top:

#define XBOX360_LEGACY_NAME_ID  "Xbox Controller"
#if defined(PLATFORM_RPI)
    #define XBOX360_NAME_ID     "Microsoft X-Box 360 pad"
    #define PS3_NAME_ID         "PLAYSTATION(R)3 Controller"
#else
    #define XBOX360_NAME_ID     "Xbox 360 Controller"
    #define PS3_NAME_ID         "PLAYSTATION(R)3 Controller"
#endif

None of these match the strange non-descriptive/misleading "Xbox" string that Raylib-cs uses in its example code.

Problem 3 (bonus)

In the course of writing this issue on GitHub, I also realized that the analog stick axes for the Raylib C# bindings have inverted the up/down (vertical) movement for both left and right analog sticks, like what one would experience on a flight simulator game or if a game designer otherwise has poor tastes in controller input behavior. Inverting vertical and leaving horizontal normal causes analog stick input to be logically inconsistent as a default since one direction is normal and the other is backwards, which is much harder to reason about. Right and down are supposed to be positive (if I'm not mistaken), just like how pixels on computers use right and down as the positive directions too.

Raylib's original library in C does not do this. It has both directions normal, not inverted.

Oh, actually, I just realized that the C# binding's display code here is also different from Raylib's actual internal axes. So, the input is correct but is being inverted by a negation when displayed, making it misleading. The C based version of the code doesn't do this.

Changing the below minus operator (for BOTH the left and right analog stick sections) into a plus fixes the problem:

                    // Draw axis: left joystick
                    DrawCircle(259, 152, 39, Color.BLACK);
                    DrawCircle(259, 152, 34, Color.LIGHTGRAY);
                    DrawCircle(
                        259 + (int)(GetGamepadAxisMovement(0, GamepadAxis.GAMEPAD_AXIS_LEFT_X) * 20),
                        152 - (int)(GetGamepadAxisMovement(0, GamepadAxis.GAMEPAD_AXIS_LEFT_Y) * 20),
                        25,
                        Color.BLACK
                    );

Problem 4 (another bonus)

The drawing code for InputGamepad draws both axes as if they are the left analog stick, causing the left stick to move both the right and left sticks but causing the right stick to never be represented on the gamepad visualization.

This bug originates in the following code:

                    // Draw axis: left joystick
                    DrawCircle(259, 152, 39, Color.BLACK);
                    DrawCircle(259, 152, 34, Color.LIGHTGRAY);
                    DrawCircle(
                        259 + (int)(GetGamepadAxisMovement(0, GamepadAxis.GAMEPAD_AXIS_LEFT_X) * 20),
                        152 - (int)(GetGamepadAxisMovement(0, GamepadAxis.GAMEPAD_AXIS_LEFT_Y) * 20),
                        25,
                        Color.BLACK
                    );

                    // Draw axis: right joystick
                    DrawCircle(461, 237, 38, Color.BLACK);
                    DrawCircle(461, 237, 33, Color.LIGHTGRAY);
                    DrawCircle(
                        461 + (int)(GetGamepadAxisMovement(0, GamepadAxis.GAMEPAD_AXIS_LEFT_X) * 20),
                        237 - (int)(GetGamepadAxisMovement(0, GamepadAxis.GAMEPAD_AXIS_LEFT_Y) * 20),
                        25, Color.BLACK
                    );

Most likely the code was copied but the ALL_CAPS enum values were not changed in the process.

The second part should instead be:

                    // Draw axis: right joystick
                    DrawCircle(461, 237, 38, Color.BLACK);
                    DrawCircle(461, 237, 33, Color.LIGHTGRAY);
                    DrawCircle(
                        461 + (int)(GetGamepadAxisMovement(0, GamepadAxis.GAMEPAD_AXIS_RIGHT_X) * 20),
                        237 - (int)(GetGamepadAxisMovement(0, GamepadAxis.GAMEPAD_AXIS_RIGHT_Y) * 20),
                        25, Color.BLACK
                    );

Closing remarks

I really recommend that you place all demos into separate projects and maintain them that way.

Running all of the demos in sequences as one project maximizes the chances of not carefully checking whether each demo is behaving correctly or not. It encourages rushing. It also makes it extremely impractical to experiment with the demo files while learning the library and forces the user to create separate projects on their own for each logically separate program/demo.

The fact that the vertical axes are inverted like they would be for a flight simulator seem also makes me concerned that these Raylib bindings have made subjective/personal changes to the behavior of the library and calls into question whether the bindings can be relied upon to not introduce lots of subtly wrong differences in behavior. Alternatively, perhaps this is an outdated oddity based on old Raylib code from an older version of the example. I don't know.

I wouldn't be surprised if there's a bunch more of these problems lurking in the code now potentially, because gamepad input is one of the most essential things to any game project and the gamepad demo file here is in quite bad shape (essentially untested, frankly).

Perhaps I should have done this issue submit as a commit including the fixes instead, but the problem with that is that I don't know what your intentions for these differences in behavior actually are. Are these differences supposed to exist in the bindings? It's impossible for me to know.

PS: Months ago I was going to use the Raylib C# bindings for a project but my attention got diverted onto other projects for a while, so its been a while since when I first visited this repo and me diving in deeper here and discovering these problems with the bindings that I'd previously overlooked.

Anyway though, thank you for your time and efforts in making this useful library available in C# etc of course in any case! 😁😎

@WraithGlade Thanks for the detailed issue!

Moved the issue into the main Raylib-cs repo as I am in the process of moving examples back into it with the goal of making setup and testing easier. Just added a note to the examples repo to make this clear.

Resource loading

The resource issue is something that I should document better. The examples use a setting in the project file so the working directory is the same directory as the project.

<RunWorkingDirectory>$(MSBuildThisFileDirectory)</RunWorkingDirectory>

As for running individual examples, I want to improve this as well. I don't think separate projects is the right approach or necessary to achieve this though. Tried some ideas for this in the past but never got to fully implementing it. I think to start adding a proper setup for command line arguments and from there experiment more with a ui to launch them.

Gamepad example

As for the gamepad example problems. I agree it has not been tested enough and I want to improve the process on this to reduce further issues even if it means releases take longer to develop.

And finally by all means open a pr with fixes for the example or open more issues if you run into any more subtle behaviour issue.

Thanks for the information!

I'll submit a fix for the InputGamepad today if the problems are still in the master file.

I just messed with the original InputGamepad file some more and it turns out that the images do actually load in your original (even without the post-build event batch file I added), but the problem was that the incorrect names of the controllers stopped the image from displaying. I made multiple changes to my own version of the file when I was messing with it and had it in a separate project file, so perhaps it isn't necessary to post-build copy after all.

I'm going to see if I can get my separate-project version working without the batch file later.

In the meantime, I'm going to eat dinner and will work on this again later.

Hey, I submitted a patch for this now, but the diff comparison is messed up, but I don't have any more time to work on it today. Sorry about that.

I changed the code so that it accounts for multiple possible names for Xbox controllers too, since that seems wise.

Anyway, have a good night!

@WraithGlade Closing as this should now be resolved using your fixes. Example could still be improved but the behaviour should now match raylib. I have also setup some basic command line support for examples so you can now pass in the example name and if it finds it it will run just that one example.

Thanks again for your work on this!