FBNeo icon indicating copy to clipboard operation
FBNeo copied to clipboard

Dual-stick controls for Forgotten Worlds

Open pjft opened this issue 11 months ago • 68 comments

Hi all.

A few years ago we implemented dual-stick controls for a few arcade games, but one notable omission was Forgotten Worlds, as the CPS driver was partially off-limits, and not up to the same controls structure as most others - particularly having a "Aim" button, that all the other logic was based on.

At the time, @dinkc64 and myself chatted and suggested that maybe further down the line we could look into this together. I recalled that this weekend and thought I'd check, but it seems that the Forgotten Worlds controls are still the same.

There's no urgency whatsoever, I'm just adding this here as a placeholder, both to show my availability in exploring adding them there at some point, and asking if there'd be interest or availability in updating the CPS drivers to at least conform with the Rotation/Aim logic from the other drivers.

Hope this is not taken as rude, as that's not my intention, and I hope everyone is doing great!

pjft avatar Mar 04 '24 19:03 pjft

Hi pjft, nice to run into you again! I think this is totally doable, but we're dealing with an insane amount of resolution on the spinner thing compared to the other games.. I think its 64 (its a guess), so, we might have to do about this by using the analog thumbstick or something like that. It's been a while since I worked on this sorta thing, what do you think?

best regards,

  • dink

dinkc64 avatar Mar 04 '24 23:03 dinkc64

Hi! Thanks for the reply - nice hearing from you as well :)

Indeed, the game seems to have 32 directions from my immediate test, so yes, it would require analog. The closest we did back then was Calibre 50, which had 16 directions.

https://github.com/finalburnneo/FBNeo/pull/950/files

And you're right, it's been 2 years since we last looked into this, so it'd require a bit of a warmup before things start to actually work, but happy to help or even follow your guidance as you know the code and logic better than I do. :)

I was thinking something similar to that logic, though double the complexity, after all.

Now, the logic back then was based on converting the analog inputs to the existing Joy2Rotate logic that already existed, where the "Aim" button logic was already implemented.

Since there is no such logic for Forgotten Worlds, we have two options:

  • Either we implement a "Joy2Rotate" logic somehow, providing for an "Aim" button as well, and then implement the analog layer on top; or
  • We implement a direct analog-to-direction conversion.

Unsure what would be best here, to be honest, though I would need help with figuring out the actual values and addresses in memory for the actual rotation direction. That was already abstracted in Joy2Rotate for the other games, so my job was easier.

It's hard when coding on a Raspberry Pi and using printfs and GDB - I should probably set up a proper development environment at some point!

Let me know your thoughts. I'm happy to take a first look at the CPS driver and see what little I understand of it, and ask questions if it helps?

You let me know - I don't want to be a nuisance here, but I'd be happy to help make this work.

Thanks, and have a great week!

pjft avatar Mar 05 '24 09:03 pjft

Hi there, I'll try to do my best here, did some hunting and found the memory locations:

Memory locations of the player are: p1: ffcf20, 0-1f, 0 is pointing to the right (3-oclock), and increments clockwise p2: ffcf70, ""

I'm thinking we should do this a little smarter than calibr50's Joy2rotate16, maybe convert the x,y to radians then degrees, then divide it down + offset it to get the value we want (0-1f)? :) I think atan2 might help us here. I'll be able to get the exact math/code or this later today

p.s. don't worry about nuisance thing, that doesn't apply when doing great things :)

best regards,

  • dink

dinkc64 avatar Mar 05 '24 15:03 dinkc64

Oh, wow - you're already ahead of me! All I got up to was set up a spare Pi to compile and test the code, go through the CPS driver code and identified where the dial controls were being registered, reviewed the Calibre 50 code and also booted it up to play it for a brief moment to see how it went!

I had never used atan2, but from reading about it it does seem to do the trick - much more elegantly than the mess of a code I put together for caliber 50!

The analog axis goes from -1 to 1, so using atan2(x,y) will return the angle (or, if it performs as in a code sample I tried out, a fraction of Pi). It seems that:

// up
ATAN2(0, 1) = 0
// right
ATAN2(1, 0) = Pi/2
// down
ATAN2(0, -1) = -Pi/2
// left
ATAN2(-1, 1) = Pi (or -Pi)

so, if we start pointing to the right, we need to transpose the angles here to make for a clean "divide it down + offset" logic. I'm thinking that maybe (don't quote me on that) simply swapping the x and y axis on the ATAN2 function will do the trick to transpose them 90 degrees, so

ATAN2(analog-y-value, analog-x-value) so that when you're aiming right it returns

ATAN2(0,1) = 0

Either way, it seems you're way ahead of me here and probably already were thinking along these lines, so I think that that is a good way to go. How can I help?

Thanks!

Best regards.

pjft avatar Mar 05 '24 16:03 pjft

Hmmmm thinking out loud, won't deadzones be a major problem if you need 32 directions ? (thinking about directions next to cardinals)

barbudreadmon avatar Mar 05 '24 16:03 barbudreadmon

Hm. Good point.

I am assuming we could just register the input if sqrt(x^2+y^2) > dead_zone_threshold, or are you referring to something different?

Thanks!

pjft avatar Mar 05 '24 17:03 pjft

Not sure myself, i'm just wondering if there might not be some bad interactions due to faulty analog sticks and the quirks used to get around it. I might just be overthinking this.

barbudreadmon avatar Mar 05 '24 17:03 barbudreadmon

You're probably right on the risks there - I suppose we'll have to see when we get there.

pjft avatar Mar 05 '24 17:03 pjft

good idea, so, I've thought about this for a few hours (before doing anything), and decided that we should modify pst90s/d_seta's calibr50 to the new degrees method before hooking up forgotten worlds - that way we'll have a known working system that just needs to be shoehorned into the cps driver :)

barbudreadmon, deadzone is not a problem and will be accounted for :)

dinkc64 avatar Mar 06 '24 01:03 dinkc64

yay! I got it to return the proper 0-f value calibr 50 wants w/maths :) going to integrate it into the rotation code next, after a little break.

dinkc64 avatar Mar 06 '24 02:03 dinkc64

Here's a modified d_seta with the new analog handling code, it seems to work ok :) the -9 offset when scaling from degrees to 0-f is accurate for one of my gamepads, but another crappier one prefers -8. hmm, I wonder how it fares on your side?

seta.zip

best regards,

  • dink

dinkc64 avatar Mar 06 '24 06:03 dinkc64

Wow, this is a great update to wake up to!

Definitely, I'll test it out and get back to you. If the previous version worked well on both controllers, though, then we probably would be able to replicate it somehow. I'll check the code as well and report back.

Thank you for looking into this so promptly! Get some rest please:)

pjft avatar Mar 06 '24 07:03 pjft

Thanks for putting this together. I tested it out, it played well, but one thing wasn't working well for me which was the lack of a deadzone. Also, your -8 and -9 comment got me thinking that the reason for that to happen is because, in the scale of 0-f, "0" isn't really the range between 0-22.5 degrees, but rather -11.25 to 11.25.

I made a small adjustment to reflect that, see how it works in your controllers. Also, I added a tentative deadzone approach, but ideally this would be handled by ProcessAnalog and only after that would we do the rotation logic, to have it be standardized across all controls. Thoughts?

I also tweaked the atan2 logic so that we don't need to subtract it from 90, and then do the -8/-9 :)

I added PJFT to the comments there where I changed things.

See how it fares on our end, and great job! I hope this helps.

d_seta.cpp.zip

pjft avatar Mar 06 '24 09:03 pjft

Hi, It works nicely here :) Thanks!

Feel free to change anything if you think it would improve things, when you think it's all good I'll go ahead and shove it into the cps driver, unless you'd like to do that part.

I had the deadzone setup while it was still in the integer domain, (the d_min(40, port)), the 40 was probably just way too small, though! (40 was just a guess) Anything under 40 would just return 0, which explains the test for both floats being 0.0. That's 40 out of +-1023 for most thumbsticks. Changing that to 225 (0.22 * 1023..?) would end up with the same effect, I think :)

best regards,

  • dink

dinkc64 avatar Mar 06 '24 14:03 dinkc64

Thanks!

I missed that the "40" value was supposed to be the deadzone, haha. Makes sense, now that I read it, though!

I think I'll try to see if I can get it to work with ProcessAnalog for the analog input handling, just for consistency's sake.

If you have a clear idea of how to do it in the CPS code, I'd very much appreciate it, as that's the code I'm very much not familiar with! Of course, if you don't have the time, I'm happy to stumble through it with your guidance - let me get this sorted first though!

Thanks, and have a great day.

pjft avatar Mar 06 '24 15:03 pjft

Ok, here we go. Please double check that I didn't break anything or that I didn't leave any debug code behind, just in case :)

I tested it and it worked as intended for 2 players, with the deadzone from ProcessAnalog! I think I'm comfortable with this version of the code being used to implement the 32-rotation logic for Forgotten Worlds, though as the driver isn't structured the same way as this one it might need a few more changes.

From what I remember from 2 years ago, per your suggestion we made sure to add an "analog auto-fire" option on the Dip Switches section as well, at least, so that it could be toggled by users.

d_seta_v2.cpp.zip

Let me know your thoughts!

Regards.

pjft avatar Mar 06 '24 15:03 pjft

deeee-cent!! : ) The rotation works great, and I especially like how you improved code clarity, too :)

best regards,

  • dink

dinkc64 avatar Mar 06 '24 16:03 dinkc64

By all means - happy to help! :) Thanks for being willing to look into this!

pjft avatar Mar 06 '24 16:03 pjft

A little update: got it in, but its a huge mess. :) A neat thing about this one, compared to the other games - well, at first it could never lock onto where it wanted to go, it kept under/overshooting, it turns out - the game could take 0-4 frames to move the rotation, depending on certain things. Since the accumulator for the dial is "absolutely massive", it turns out you can make all of the steps at once, and wait for it to get there - yay!

I'll try to clean it up and post the code tomorrow morning (detroit time)

best regards,

  • dink

dinkc64 avatar Mar 07 '24 06:03 dinkc64

Here's a messy test, it only responds to the analogs right now (not impl.'d yet: button+dpad, dip-autofire, autofire) Maybe you can experiment and try to get it to respond to analogs better? I'm out of ideas for tonight! test.zip

dinkc64 avatar Mar 07 '24 06:03 dinkc64

Hi! Good morning :) Thank you so much for this - let me test it out and get back to you. Had an early day at work, and still haven't had a moment, but I'll see what I can get to.

pjft avatar Mar 07 '24 14:03 pjft

thanks :) Actually, now that I try it with a fresh mind, things seem OK, what do you think? Maybe I'll try to hook up the autofire and button+dpad rotation

best regards,

  • dink

dinkc64 avatar Mar 07 '24 15:03 dinkc64

From reading the code (only that), it seems ok but I'm compiling it and will try it out now.

The only comment I have is that I didn't expect that we'd get rid of the "analog +/-" button inputs that I imagine players are used to, especially in the absence of analog controllers. Maybe this was changed because we adapted the previous code from calibr50, but thought I'd bring it up in case it wasn't a deliberate change.

I'll be honest - it rotates wonderfully, this is fantastic work! Well done :)

One thing that might be on me - it seems to be using the left analog stick to rotate, rather than the right one, but I imagine it's probably on my settings as I'm using the libretro core. Just confirming on your end that it's all working as intended - I know Calibre 50 used the right stick.

Thank you so much! Let me know if there's anything else to test, and when I have the chance I'll also spend some more quality time with this.

Best.

pjft avatar Mar 07 '24 15:03 pjft

Yeah, after I manually configured the controls to be mapped to the place I expected them to, and played a credit.

Let me tell you that this control scheme makes the game so much more enjoyable - it does beg the question of what would the original developers think about the ideal control mapping if they had dual analog stick controllers back then :) It fits right in!

Thank you so much!

pjft avatar Mar 07 '24 15:03 pjft

Here's something worth a watch: Todd Tuckey from TNT Amusements did a video about a Forgotten Worlds arcade cabinet; going through the electronics and control panel and things :)

https://youtu.be/0V9eNfil_dw?si=_4Y-UQKThNTZKQee&t=173

(the link skips past all the non-relevant intro stuff)

best regards,

  • dink

dinkc64 avatar Mar 07 '24 16:03 dinkc64

I'll watch it later for sure - thanks for the recommendation! :)

pjft avatar Mar 07 '24 16:03 pjft

Thanks for testing it out :) Feel free to hook up or work on anything, I won't be able to work on it until tonight around 8pm Detroit time (EST)

I really just did a quick throw-it-in job, so I could get to the point and work on the rotation code. We can put back (or remove) any features you think should be there, anything I did wasn't set in stone, of course :)

best regards,

  • dink

dinkc64 avatar Mar 07 '24 16:03 dinkc64

Ok, so I tried a few things but I'm kind of stuck now.

I fixed a few leftover warnings from the DIP switch changes, and added back the rotate left/right with buttons.

I think I prepared the dip switch for the autofire, but now I don't quite know how to actually force the autofire to happen. In Calibre 50 it was easier to read the code and find out where this was being read from/set to, but here I'm a bit lost.

Still, assuming I haven't broken anything, see if this helps.

The default to the left stick will require a change in Libretro's fork, in retro_input.cpp.

https://github.com/libretro/FBNeo/blob/925fe1e34894e521dde3bf029876f660d4d758c3/src/burner/libretro/retro_input.cpp#L1861

Unsure of how to proceed in that regard @barbudreadmon ?

See if the code helps here. Thanks.

cps.zip

pjft avatar Mar 07 '24 18:03 pjft

--- just to add, thanks for sharing that video, it was really nice watching all the internals and him going through how the rotary dial worked! Watching him playing it with the rotary dial seems to suggest that we're a lot better off with the analog twin-stick controls :)

pjft avatar Mar 07 '24 21:03 pjft

Haha right on, but well - nothing beats a game of tempest with a real spinner! :)

I added your latest changes to mine, hooked up autofire & button+dpad rotate, cleaned a few things up.

There's a problem though, the game has 2 spinner modes: 1 when shooting, and the other while not shooting.
The second -- not shooting mode, is real slow to move, where as spinner + shooting updates almost instantly. I can get one working perfect, and the other goes bonkers. Err, I can get both working perfectly, but not together. Guess I'll sleep on it and see what happens tomorrow! :)

best regards,

  • dink

dinkc64 avatar Mar 08 '24 06:03 dinkc64