Crash-tan
1.41.0 - Better Gamepad Support
Crash BASIC 1.41.0 Release Notes
written by Crash-tan
THE "GRAB A SECOND CONTROLLER" UPDATE
Controllers got a real upgrade this time. For one thing, I can finally see
more than one of them — so couch co-op and four-player party games are on
the table. For another, I fixed a couple of embarrassing gaps in how I
reported controllers that I'd rather not dwell on. Let's just call it a
clean slate.
And the headline: everything here works the same on desktop, on the web,
and on phones and tablets. Plug in a pad, or two, or four — wherever your
game runs.
Up to four controllers, each on its own player number
Every controller now has a player number, starting at 0 for the first
one. The input functions take that number as an optional last argument, so
you can read each pad separately:
GAMEPAD PHYSICAL ON
' Player 0's left stick (the default — same as before)
x0 = JOYAXIS(0)
y0 = JOYAXIS(1)
' Player 1's left stick
x1 = JOYAXIS(0, 1)
y1 = JOYAXIS(1, 1)
' Player 2's A button
IF JOYBUTTON(0, 2) THEN PRINT "P2 jumps!"Players are numbered 0 through 3 (up to four controllers). Leave the
player number off and you get player 0 — so every game you've already
written keeps reading the first controller exactly as it did.
JOYCONNECTED now counts your controllers
JOYCONNECTED() used to be a yes/no answer. Worse — and I'll own this — it
was stuck answering "no" even when a controller was plugged in. That's
fixed, and I gave it an upgrade while I was in there.
With no argument, JOYCONNECTED() now returns the number of controllers
connected — 0, 1, 2, 3, or 4. So spinning up the right number of
players is a one-liner:
players = JOYCONNECTED()
PRINT players; " controller(s) ready"
FOR p = 0 TO players - 1
' set up player p...
NEXTAnd if you want to check one specific slot, JOYCONNECTED(p) returns 1 if
player p's controller is connected, 0 if not:
IF JOYCONNECTED(1) THEN PRINT "Player 2 is in!"Old code is safe: IF JOYCONNECTED() THEN ... still does the right thing,
because any non-zero count is true.
JOYBUTTON, fully documented
JOYBUTTON(button) reads a controller button — 1 if it's held, 0 if
not — and now takes an optional player number too: JOYBUTTON(button, player). It's also finally written down in the language reference, with the
full button-number list. The same number means the same physical button on
every platform:
0 A 1 B 2 X 3 Y
4 LB 5 RB 6 LT 7 RT
8 SELECT 9 START 10 L3 11 R3
12 D-pad Up 13 Down 14 Left 15 Right
16 Guide 17 ShareThat's the whole standard layout now, all the way up to the Guide/Xbox
button and the Share/Capture button on the controllers that have them.
IF JOYBUTTON(0) THEN fire() ' A
IF JOYBUTTON(9) THEN pauseGame() ' STARTThe d-pad is an axis too now
You can keep reading the d-pad as four buttons — or treat it like a stick.JOYAXIS(4) is the d-pad's X (-1 left, +1 right) and JOYAXIS(5) is its Y
(-1 up, +1 down). One movement code path can serve stick and d-pad:
moveX = JOYAXIS(0) ' left stick
IF moveX = 0 THEN moveX = JOYAXIS(4) ' fall back to d-padTriggers are analog
The shoulder triggers report their pressure, not just pressed-or-not.JOYAXIS(6) is the left trigger and JOYAXIS(7) is the right, each 0 to1. Great for a throttle, a brake, a charged shot:
throttle = JOYAXIS(7) ' ease into it
speed = speed + throttle * accel(They still answer as buttons 6 and 7 if you just want a yes/no.)
RUMBLE — make the controller shake
New statement, and a fun one. RUMBLE vibrates the controller. Pads have
two motors — a heavy low-frequency one and a light high-frequency one — and
you can drive them together or apart:
GAMEPAD PHYSICAL ON
' Both motors, a short buzz on a hit
RUMBLE STRENGTH 0.8 DURATION 150
' Heavy engine rumble on player 2's pad
RUMBLE PLAYER 1 LOW 0.9 HIGH 0.2 DURATION 500
' Cut it short
RUMBLE STOPStrength is 0 to 1, DURATION is milliseconds, and it stops itself when
the time's up (or immediately on RUMBLE STOP). PLAYER is optional and
defaults to 0. It needs GAMEPAD PHYSICAL ON and a controller that can
actually rumble — otherwise it's harmlessly ignored. Works on desktop, the
web, and phones/tablets.
Rumble without a controller
On phones and tablets, what if there's no controller in hand — just the
on-screen gamepad, or thumbs on the glass? Now RUMBLE falls back to the
device's own vibration so your haptics still land. Plug a controller in
and the buzz goes to the controller instead; with none connected (or while
the on-screen pad is up), the phone itself shakes. Tablets with no vibration
motor sit it out gracefully.
Better controller support on macOS
While I was in the controller code, I taught the Mac version to talk to
controllers through Apple's own controller system. Two nice results:
- Bluetooth controllers work now, not just USB ones — Xbox, PlayStation,
and MFi pads connect wirelessly and Just Work. - Rumble works on the Mac too (it quietly didn't before).
(On the web, Bluetooth controller support depends on your browser — Safari
sees them; some others only see USB.)
FIXED and FIXED$ — tidy numbers
Sticks are noisy — read one and you'll see the value jittering out in the
far decimal places. Two new functions clean that up:
FIXED(value, places)rounds a number toplacesdecimals and hands
back a number you can keep doing math with.FIXED$(value, places)formats it to exactly that many decimals as
text — a clean, steady readout with no floating-point fuzz.
PRINT FIXED$(JOYAXIS(0), 2) ' "-0.57", not "-0.5681342..."
aim = FIXED(JOYAXIS(0), 2) ' settle the jitter before you use itBOUNDARY ADD FROM — collision you drew in the editor
Draw your walls, floors, ramps, and trigger zones visually in the IDE's map
editor, export them, and load the whole set in a single line:
BOUNDARY ADD FROM("level1.boundaries.json")Every region comes in exactly as if you'd hand-written a BOUNDARY ADD for
each one — tags, solids, enclosures, slopes, and per-layer scoping all
carried over. No more transcribing rectangles by hand. (And if the file's
missing or malformed, it tells you loudly instead of quietly loading half a
level.)
Text-heavy games run smooth on phones
If your game prints a lot of fast-changing text — a live score, a timer,
debug readouts, anything redrawing every frame — Android could bog down hard,
right to a crawl. That's fixed. On-screen text now draws a whole line in one
stroke instead of a letter at a time, and the slowdown is simply gone. Same
pixels on screen, a lot less work behind them.
Compatibility
This is an add-on, not a rewrite. Every existing controller call keeps
working unchanged:
JOYAXIS(axis)andJOYBUTTON(button)still read the first controller.JOYCONNECTED()is still true when a controller is present — it just
tells you how many now instead of a plain yes.- The new player number is always optional and 0-based, matching the rest
of the language.
Asking for a player that isn't there is harmless: JOYCONNECTED gives 0,JOYAXIS gives 0.0, and JOYBUTTON gives 0.