Greg Turbo's Blog

Analog Value Parity in Platform Fighters

Intro

As I make add features and improvements to the Frame1 firmware and the Glyph firmware (the latter based on Haybox), I've wanted to document them for the sake of other builders/modders. This one in particular is relevant to literally every type of controller for a platform fighter. It exists in other games, but we will focus on Smash Ultimate and Rivals 2 (because those are the two that needed to be "solved").

The Problem

Let's say you're using a rectangle-type controller (all buttons) for Ultimate. You have all your numbers set correctly for all your modifier buttons and angles when you use a GameCube adapter1. If you then plug into the console directly over USB in Switch Mode2 using the same exact values, your inputs will not work properly. Haybox and older versions of the Frame1 firmware solved this by simply multiplying it by a value (~1.3x) and calling it a day. In a game like Ultimate where the majority of analog stick zones are large and accessible, this should have been enough. I didn't dig into the specifics until a pikachu player noted that their double up-b input only worked in GC modes.

In order to gather the info I needed, I had to download a specific branch of an Ultimate training mod. By stepping through and recording every analog value vs the modded input display, I was able to get an idea of what was really happening.

said ult mod in action
somehow the only picture I have of this mod

The deadzones and maximum value of the analog values are different between the Switch and GCC modes, with GCC being the smaller of the two. Simply multiplying one by a number unfortunately wouldn't be enough.

GCC

Switch

value in code (+128)"raw" outputSmash output
000
100
200
300
400
500
600
700
800
900
1000
1100
1200
1300
1400
150.017852
160.035714
170.053566
180.071419
190.0892711
200.1071213
210.1249716
220.1428618
230.1607120
240.1785623
250.1964225
260.2142727
270.2321230
280.2499832
290.2678334
300.2857137
310.3035739
320.3214241
330.3392743
340.3571346
350.3749848
360.3928350
370.4106953
380.4285755
390.4464257
400.4642860
410.4821362
420.4999864
430.5178467
440.5356969
450.535471
460.5714374
470.5892876
480.6071478
490.6249980
500.6428483
510.660785
520.6785587
530.696490
540.7142992
550.7321494
560.7499997
570.7678599
580.7857101
590.80355104
600.82141106
610.83926108
620.85714111
630.875113
640.89285115
650.9107118
660.92856120
670.94641122
680.96426124
690.98212127
701127
value in code (+128)"raw" outputSmash output
000
10.007870
20.015750
30.023620
40.03150
50.039370
60.047240
70.055120
80.062990
90.070860
100.078740
110.086611
120.094492
130.102363
140.110234
150.118116
160.125987
170.133858
180.141739
190.149610
200.1574811
210.1653512
220.1732213
230.181114
240.1889715
250.1968417
260.2047218
270.2125919
280.2204720
290.2283421
300.2362122
310.2440923
320.2519624
330.2598325
340.2677127
350.2755828
360.2834629
370.2913330
380.299231
390.3070832
400.3149533
410.3228234
420.330735
430.3385736
440.3464538
450.3543239
460.3621940
470.3700741
480.3779442
490.3858143
500.3936944
510.4015645
520.4094446
530.4173148
540.4251849
550.4330650
560.4409351
570.4488152
580.4566853
590.4645554
600.4724355
610.480356
620.4881757
630.4960559
640.5039260
650.511861
660.5196762
670.5275463
680.5354264
690.5432965
700.5511666
710.5590467
720.5669169
730.5747970
740.5826671
750.5905372
760.5984173
770.6062874
780.6141575
790.6220376
800.629977
810.6377878
820.6456580
830.6535281
840.661482
850.6692783
860.6771484
870.6850285
880.6928986
890.7007787
900.7086488
910.7165190
920.7243991
930.7322692
940.7401393
950.7480194
960.7558895
970.7637696
980.7716397
990.779598
1000.7873899
1010.79525101
1020.80313102
1030.811103
1040.81887104
1050.82675105
1060.83462106
1070.84249107
1080.85037108
1090.85824109
1100.86612111
1110.87399112
1120.88186113
1130.88974114
1140.89761115
1150.90548116
1160.91336117
1170.92123118
1180.92911119
1190.93698120
1200.94485122
1210.95273123
1220.9606124
1230.96847125
1240.97635126
1250.98422127
1260.9921127
1271127

For reference, the sticks are 8-bit and the values range from 0-255 with ~128 being the center depending on the interface and game.

The game provides 2 values that indicate the internal "stick" position. The "raw" value, and some nice whole numbers (labeled "Smash Output"). The whole numbers were nicer to work with and seemed more reliable, so I went with those as the source of "truth". Some of these numbers are fully inaccessible, since the game's range (0-127) exceeds the accepted non-deadzone analog range of the controller (11-127 or 15-70). In switch mode, a "skip" occurs once every 11 or so values. In GCC mode, there's a skip between every value of 1-2 units. You can see these jumps in the graphs. In practice, it seems to have no affect on the relevant analog ranges the Frame1 uses.3

The Solution

There's probably a decently accurate one-line solution using the data I've gathered, but since we've already got all the data, we might as well use a lookup table.

By using the game engine's values as the source of truth, I created 2 arrays, one for each backend. By plugging in the desired value as the index, it gives you the value that will get you as close as possible to the desired value for the given backend.

The downside to this approach is that it requires all of the numbers in the Ultimate mode code to change. I'll get to migration and code changes in a moment, but if you need to convert your GCC coordinates to the new ones, here's a calculator.

New Value: 0

The only other thing that would have to change in the Haybox to allow this would be for the game mode variable to be passed to the game mode, so each individual game mode can scale or change the values for the backends as they see fit. I've already implemented this in the Glyph fork of Haybox but I also plan on making a PR so it can be added to the mainline repo.

What about Rivals 2?

Rivals 2 has pretty similar ranges between Xinput and GCC modes (110 vs 100). I've used the Xinput mode as the source of truth since it's the larger of the two. This means I just need one lookup table.

All values here are within 0.005 of the Xinput values, as measured by the game's input display.

Outro

First post here, so thank you for reading! While everything here works for my purposes, the Ult engine is admittedly not my area of expertise and Rivals 2 is brand-new. So if I got anything wrong or if something needs clarification feel free to @ me on Twitter or Bluesky


All of the data and code included in this article is free to use for anything you'd like

1 or a Frame1/Pico-Rectangle-based controller in Adapter Mode over USB.

2 "Switch Mode" in HayBox acts like a Pokken controller. Frame1 does the same in addition to a separate USB "Adapter Mode" that works on Switch. When I say "Switch Mode" I'm referring to the Pokken controller mode.

3 These values were captured by going straight in one direction or the other. Some otherwise inaccessible values can be accessed by going beyond the "rim". I do not have the time or the need to figure out how this calculated, but if any ult-engine-understanders want to chime in, please do