@indiegems/gem-web-sdk 12.0.0 → 13.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/dist/function.d.ts +9 -1
- package/dist/game.d.ts +52 -13
- package/dist/gem-web-sdk.bridge.js +1 -1
- package/dist/gem-web-sdk.esm.js +2 -2
- package/dist/gem-web-sdk.function.js +2 -2
- package/dist/gem-web-sdk.game.js +2 -2
- package/dist/gem-web-sdk.min.js +2 -2
- package/dist/gem-web-sdk.sdk.js +2 -2
- package/dist/gem-web-sdk.server-api.js +1 -1
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +47 -14
- package/dist/sdk.d.ts +62 -14
- package/dist/server-api.d.ts +9 -1
- package/dist/server.d.ts +10 -1
- package/docs/api/api-reference-function.md +170 -2
- package/docs/api/api-reference-game.md +707 -162
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +943 -172
- package/docs/arcade-avatars.md +471 -0
- package/docs/brand-art.md +3 -3
- package/docs/changelog.md +37 -0
- package/docs/getting-started.md +3 -2
- package/docs/inventory-and-catalogs.md +1 -1
- package/docs/manifest.json +19 -14
- package/docs/saving-player-progress.md +16 -10
- package/docs/server-bundles.md +2 -2
- package/docs/what-a-game-must-do.md +146 -99
- package/package.json +4 -2
- package/rigs/adult_human.glb +0 -0
- package/schema/bridge.v1.json +247 -20
- package/schema/capabilities.public.json +12 -0
|
@@ -363,13 +363,18 @@ often rather than to retry harder.
|
|
|
363
363
|
## For something small, reach for settings instead
|
|
364
364
|
|
|
365
365
|
`gem.me.settings` needs no declaration and is one call: `put(key, value)` with a **string**
|
|
366
|
-
value, up to **8 KiB** each and **32** keys per player per game. It is the right home for a
|
|
367
|
-
|
|
366
|
+
value, up to **8 KiB** each and **32** keys per player per game. It is the right home for a small
|
|
367
|
+
value that follows the player, such as a level counter, and for the player's settings that do too.
|
|
368
|
+
|
|
369
|
+
**Which store a setting goes in** ([game settings](what-a-game-must-do.md#game-settings) has the
|
|
370
|
+
whole model): Device configuration — volume, graphics quality, look sensitivity, a touch-stick
|
|
371
|
+
style — goes in `gem.deviceSettings`; a choice that must follow the player across devices —
|
|
372
|
+
difficulty, key bindings, accessibility toggles the game owns — goes in `gem.me.settings`. Either
|
|
373
|
+
way, save only what the player chose, never a value your game started from `gem.prefs.recommended`.
|
|
368
374
|
|
|
369
375
|
Reach for a save when the payload is a document rather than a value — a world, an inventory, a
|
|
370
|
-
replay.
|
|
371
|
-
|
|
372
|
-
belongs in a save, and nowhere else.
|
|
376
|
+
replay. Device configuration belongs in neither; that is the next section. Progress a player would
|
|
377
|
+
be upset to lose belongs in a save, and nowhere else.
|
|
373
378
|
|
|
374
379
|
### Device settings: local to this device, and only that
|
|
375
380
|
|
|
@@ -380,9 +385,10 @@ browser storage cannot do this: every build of a web game runs on its own origin
|
|
|
380
385
|
game kept in `localStorage` is gone the moment you publish again. The Arcade's own storage outlives
|
|
381
386
|
your builds; that is the whole mechanism.
|
|
382
387
|
|
|
383
|
-
**What goes here:** a master volume and the music/SFX mix, a graphics
|
|
384
|
-
|
|
385
|
-
about **this screen
|
|
388
|
+
**What goes here:** device configuration — a master volume and the music/SFX mix, a graphics
|
|
389
|
+
quality or resolution scale, a look sensitivity, a touch-stick style — and a tip the player
|
|
390
|
+
dismissed or a "skip intro" toggle. Configuration that is about **this screen, these speakers and
|
|
391
|
+
this input**. Key bindings and a difficulty follow the player, so they go in `gem.me.settings`.
|
|
386
392
|
|
|
387
393
|
⚠️ **What never goes here: progression, unlocks, currency, anything a player would mind losing,
|
|
388
394
|
and anything that must follow them to another device.** This is not a save and it is not a
|
|
@@ -433,8 +439,8 @@ game, and a key you experiment with on `dev` cannot break a shipped build.
|
|
|
433
439
|
| You want | Where it goes | Follows the player? |
|
|
434
440
|
| -------- | ------------- | ------------------- |
|
|
435
441
|
| a world, an inventory, a replay | `gem.me.saves` — a declared slot | yes |
|
|
436
|
-
| a difficulty,
|
|
437
|
-
| a volume, a quality
|
|
442
|
+
| a difficulty, key bindings, an accessibility toggle your game owns, a level counter | `gem.me.settings` — a string per key | yes |
|
|
443
|
+
| a volume, a graphics quality, a look sensitivity, a touch-stick style, a dismissed tip | `gem.deviceSettings` — one object per device | **no** |
|
|
438
444
|
|
|
439
445
|
## Starting over as a new player while you test
|
|
440
446
|
|
package/docs/server-bundles.md
CHANGED
|
@@ -80,8 +80,8 @@ To check the download yourself: the package npm serves is, byte for byte, the fi
|
|
|
80
80
|
also publishes on its download host, with the hash beside it:
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
|
-
curl -fsSL -O https://downloads.gemarcade.com/sdk/
|
|
84
|
-
curl -fsSL https://downloads.gemarcade.com/sdk/
|
|
83
|
+
curl -fsSL -O https://downloads.gemarcade.com/sdk/v13.1.0/gem-web-sdk.tgz
|
|
84
|
+
curl -fsSL https://downloads.gemarcade.com/sdk/v13.1.0/gem-web-sdk.tgz.sri
|
|
85
85
|
openssl dgst -sha384 -binary gem-web-sdk.tgz | openssl base64 -A
|
|
86
86
|
```
|
|
87
87
|
|
|
@@ -708,46 +708,54 @@ the `var()` above, or read `gem.viewport`. The release checklist looks for the b
|
|
|
708
708
|
The Arcade keeps **one control of its own over every game**, on every device — a phone's full-bleed
|
|
709
709
|
game, a framed or full-window game on a desktop, a tablet or a TV in native fullscreen:
|
|
710
710
|
|
|
711
|
-
- **The Gem button**, the Gem Arcade's gem,
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
711
|
+
- **The Gem button**, the Gem Arcade's gem, always there (a touch target, about 50px across), in
|
|
712
|
+
the **top-left corner** unless your game [asks for another](#moving-the-gem-gemsetgemcorner). It
|
|
713
|
+
opens [the Arcade's menu](#the-arcades-menu): Return to the Arcade, the player's Gems, Tickets and
|
|
714
|
+
achievement score, mute, the player's Arcade-wide settings and the rest. Not your settings: those
|
|
715
|
+
are [your own button](#game-settings).
|
|
715
716
|
- **Nothing else.** The player's wallets (Gems, Tickets, their achievement score) are **not** drawn
|
|
716
717
|
over your game: the player opens the Gem menu to see them, and every platform-currency payout
|
|
717
718
|
flies into the Gem button ([the Arcade's wallet](#the-arcades-wallet-in-the-gem-menu-never-drawn-by-you)).
|
|
718
719
|
When the player switches the frame-rate readout on, it sits beside the gem and is reserved the
|
|
719
720
|
same way.
|
|
720
721
|
|
|
721
|
-
|
|
722
|
+
### The top of the screen
|
|
722
723
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
| **Top-left corner** | the Arcade's | Its Gem button. Nothing of yours under it. |
|
|
726
|
-
| **Top-left, right of the Gem** | yours | Your **Home / Back** buttons, drawn when a screen needs them, level with the Gem and just to its right. They move around _inside_ your game — back to your level select or title screen — and never leave it. |
|
|
727
|
-
| **Centre of the top row** | yours | Your **information**: score, moves, a timer, a level name. |
|
|
728
|
-
| **Top-right** | yours | Your **settings** button ([Game settings](#game-settings)). |
|
|
724
|
+
**The top of the screen has one layout, in every game, wherever the Gem sits.** A player learns
|
|
725
|
+
where "back" and "settings" are once, across every game:
|
|
729
726
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
727
|
+
| Where | Whose | What goes there |
|
|
728
|
+
| ------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
729
|
+
| **The Gem's corner** | the Arcade's | Its Gem button — top-left unless you [ask for another corner](#moving-the-gem-gemsetgemcorner). Nothing of yours under it. |
|
|
730
|
+
| **Top-right** | yours | Your **settings** button ([Game settings](#game-settings)) at the end of the row, and your **Back / Home** button just to its left, level with it and about 16px apart, so a fingertip lands on one and not the other. |
|
|
731
|
+
| **Centre of the top row** | yours | Your **information**: a title, score, moves, a timer, a level name. |
|
|
733
732
|
|
|
734
|
-
**
|
|
735
|
-
|
|
736
|
-
|
|
733
|
+
- **Back / Home is drawn when a screen needs it, always in that spot.** It moves around _inside_
|
|
734
|
+
your game — back to your level select or title screen — and never leaves it: leaving is the
|
|
735
|
+
Arcade's menu, so a player always knows which control does which.
|
|
736
|
+
- **Whichever of your controls shares the Gem's corner sits level with the Gem, on the side toward
|
|
737
|
+
the centre.** With the Gem top-left, nothing of yours does. With the Gem top-right, your Back and
|
|
738
|
+
settings sit just left of it.
|
|
739
|
+
- **Your settings button is there on every screen.** It goes top-right even where no Arcade control
|
|
740
|
+
is: another embedder may keep one in that corner, so place it from the reserved areas below and it
|
|
741
|
+
moves aside for whatever is there.
|
|
737
742
|
|
|
738
|
-
|
|
739
|
-
them. **Never hard-code their size or position.** They
|
|
740
|
-
|
|
743
|
+
The parts of your frame under the Gem and any other Arcade control are **reserved**: keep
|
|
744
|
+
essential UI and every touch target out of them. **Never hard-code their size or position.** They
|
|
745
|
+
differ by phone, orientation and mode. The Gem sits at the edge of the safe area and never rises
|
|
746
|
+
into the camera band, but that edge is somewhere else on every device — read them, as below.
|
|
741
747
|
|
|
742
748
|
An inset cannot say that — as a left or top inset, a button a few dozen pixels wide would take a whole
|
|
743
749
|
strip of your screen. So `gem.viewport` also carries the rectangles, in the same CSS pixels of your
|
|
744
750
|
frame, measured from its top-left corner — one for the Gem button, and one for the frame-rate
|
|
745
|
-
readout while the player shows it
|
|
751
|
+
readout while the player shows it. Each is grown out to the frame edges it sits nearest, because the
|
|
752
|
+
sliver between the Gem and the edge is no place for a button either. On a notched phone in portrait,
|
|
753
|
+
with the Gem top-left:
|
|
746
754
|
|
|
747
755
|
```json
|
|
748
756
|
{
|
|
749
757
|
"insets": { "top": 47, "right": 0, "bottom": 34, "left": 0 },
|
|
750
|
-
"reserved": [{ "x":
|
|
758
|
+
"reserved": [{ "x": 0, "y": 0, "width": 60, "height": 99 }]
|
|
751
759
|
}
|
|
752
760
|
```
|
|
753
761
|
|
|
@@ -755,35 +763,37 @@ readout while the player shows it:
|
|
|
755
763
|
`--gem-safe-*` before anything else hears the push: `--gem-reserved-tl-width` and
|
|
756
764
|
`--gem-reserved-tl-height`, and the same pair for `tr`, `bl` and `br` — how far that corner's
|
|
757
765
|
reserved rectangles reach in from its two edges. A rectangle belongs to the corner its centre lies
|
|
758
|
-
nearest to.
|
|
766
|
+
nearest to. Lay the top row out between the two top corners, and it is right wherever the Gem is:
|
|
759
767
|
|
|
760
768
|
```css
|
|
761
|
-
/*
|
|
762
|
-
#
|
|
769
|
+
/* The top row: clear of whatever the Arcade reserves in either top corner */
|
|
770
|
+
#top-row {
|
|
771
|
+
position: absolute;
|
|
763
772
|
top: var(--gem-safe-top, env(safe-area-inset-top, 0px));
|
|
764
773
|
left: max(var(--gem-safe-left, 0px), var(--gem-reserved-tl-width, 0px));
|
|
774
|
+
right: max(var(--gem-safe-right, 0px), var(--gem-reserved-tr-width, 0px));
|
|
775
|
+
display: flex;
|
|
776
|
+
align-items: center;
|
|
777
|
+
gap: 16px;
|
|
765
778
|
}
|
|
766
|
-
/* your information
|
|
767
|
-
#info {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
var(--gem-reserved-tr-height, 0px)
|
|
771
|
-
);
|
|
772
|
-
left: max(var(--gem-safe-left, 0px), var(--gem-reserved-tl-width, 0px));
|
|
773
|
-
right: var(--gem-safe-right, 0px);
|
|
779
|
+
/* your information takes the middle; Back, then settings, at the right end */
|
|
780
|
+
#top-row .info {
|
|
781
|
+
flex: 1;
|
|
782
|
+
text-align: center;
|
|
774
783
|
}
|
|
775
784
|
```
|
|
776
785
|
|
|
777
786
|
The per-corner properties say how far a corner's rectangles reach, not how much room is left beside
|
|
778
|
-
them. To know whether
|
|
779
|
-
script: the room
|
|
780
|
-
edge among them.
|
|
787
|
+
them. To know whether your top row fits between the Arcade's controls, read the rectangles from
|
|
788
|
+
script: the room is what lies between the top rectangles on the left and those on the right.
|
|
781
789
|
|
|
782
790
|
```js
|
|
783
|
-
const
|
|
784
|
-
const
|
|
785
|
-
const
|
|
786
|
-
|
|
791
|
+
const w = window.innerWidth;
|
|
792
|
+
const top = (gem.viewport?.reserved ?? []).filter((r) => r.y === 0);
|
|
793
|
+
const isLeft = (r) => r.x + r.width / 2 < w / 2;
|
|
794
|
+
const from = Math.max(0, ...top.filter(isLeft).map((r) => r.x + r.width));
|
|
795
|
+
const to = Math.min(w, ...top.filter((r) => !isLeft(r)).map((r) => r.x));
|
|
796
|
+
row.classList.toggle('below-arcade-row', to - from < row.scrollWidth + 8);
|
|
787
797
|
```
|
|
788
798
|
|
|
789
799
|
All eight properties are written on every push, `0px` for a clear corner, so a rotation never leaves
|
|
@@ -791,8 +801,39 @@ a stale value; they are never set under an embedder that sends no viewport, whic
|
|
|
791
801
|
fallback is for. `gem.viewport.reserved` is `[]` when nothing is covered.
|
|
792
802
|
|
|
793
803
|
**Lay out against it whenever it changes.** The Gem button's rectangle is there for the whole
|
|
794
|
-
session, and it moves with a rotation, a resize
|
|
795
|
-
only the one
|
|
804
|
+
session, and it moves with a rotation, a resize, the frame-rate readout and a change of corner. Read
|
|
805
|
+
every corner, not only the one you asked for: a layout that honours all four is right under any
|
|
806
|
+
embedder.
|
|
807
|
+
|
|
808
|
+
### Moving the Gem: `gem.setGemCorner`
|
|
809
|
+
|
|
810
|
+
**Leave the Gem top-left unless your own HUD needs that corner.** If it does, ask for another:
|
|
811
|
+
|
|
812
|
+
```js
|
|
813
|
+
// This game's minimap needs the top-left, so the Gem goes bottom-left.
|
|
814
|
+
gem.setGemCorner('bottom-left'); // 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
- **One corner per game**, at most one per orientation (one in portrait, another in landscape).
|
|
818
|
+
Call it once at boot, and again on a rotation if the two differ. A Gem that moves from screen to
|
|
819
|
+
screen is a way out the player has to look for.
|
|
820
|
+
- **In landscape the bottom corners are where a player's thumbs hold the sticks.** Ask for one only
|
|
821
|
+
if your controls do not live there.
|
|
822
|
+
- **Lay out around the reserved rectangles you receive, never around the corner you asked for.**
|
|
823
|
+
The Arcade moves the Gem on its own terms: not while the player is using it or its menu is open,
|
|
824
|
+
no more often than every few seconds, and a corner the player chooses for themselves, should the
|
|
825
|
+
Arcade ever offer that, wins over yours. The rectangles — and the `--gem-reserved-*` properties —
|
|
826
|
+
say where the Gem is now; a game that reads them is right whatever the Arcade decided, and a game
|
|
827
|
+
that assumed its corner is not.
|
|
828
|
+
- **The top row does not change.** Back and settings stay top-right and information top-centre,
|
|
829
|
+
wherever the Gem goes ([the top of the screen](#the-top-of-the-screen)).
|
|
830
|
+
- **It is screen state, not a toggle**: each call is the whole answer, held before the Arcade
|
|
831
|
+
connects and sent again after any reconnect. A value that is not one of the four corners is
|
|
832
|
+
ignored, with a warning on the console.
|
|
833
|
+
|
|
834
|
+
`gem.setGemCorner` returns `true` when the Arcade was told, and `false` before it connects and
|
|
835
|
+
under an embedder that cannot move its Gem. On `false` the Gem stays where the embedder puts it,
|
|
836
|
+
and the rectangles say where that is: there is nothing else to do.
|
|
796
837
|
|
|
797
838
|
**Older SDKs get it folded into the top inset.** A game on an SDK from before reserved areas cannot
|
|
798
839
|
read the rectangles, so the arcade sends it none and instead raises `insets.top` — and so
|
|
@@ -891,22 +932,38 @@ arrives.
|
|
|
891
932
|
## Game settings
|
|
892
933
|
|
|
893
934
|
**Your settings belong to your game: draw a settings button of your own, and the screen it opens.**
|
|
894
|
-
The Arcade
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
935
|
+
The Arcade's menu never opens your settings — it holds the player's _Arcade-wide_ settings
|
|
936
|
+
instead — so a game with no button of its own has settings nobody can reach.
|
|
937
|
+
|
|
938
|
+
This is the whole model, and every other page that mentions settings links here:
|
|
939
|
+
|
|
940
|
+
- **The Arcade's settings are the player's Arcade-wide defaults for this device.** Your game reads
|
|
941
|
+
them from `gem.prefs.recommended` (and `muted` and `reducedMotion` from `gem.prefs`) and applies
|
|
942
|
+
them again on `onPrefs`. The Arcade never asks your game anything about them, and never shows
|
|
943
|
+
anything your game says.
|
|
944
|
+
- **Every game draws its own settings button, top-right ([the top of the
|
|
945
|
+
screen](#the-top-of-the-screen)), and its own settings screen.** The Arcade's menu never opens it.
|
|
946
|
+
- **A setting starts from `recommended` until the player changes it in your game, and then the
|
|
947
|
+
player's choice in your game wins.** Save only what the player chose, never a recommended value.
|
|
948
|
+
- **Where a choice is kept.** Device configuration — volume, graphics quality, look sensitivity, a
|
|
949
|
+
touch-stick style — goes in `gem.deviceSettings`; a choice that must follow the player across
|
|
950
|
+
devices — difficulty, key bindings, accessibility toggles the game owns — goes in
|
|
951
|
+
`gem.me.settings`.
|
|
952
|
+
- **Mute and vibration are in your settings screen too**, wired both ways: your mute follows
|
|
953
|
+
`prefs.muted` and reports the player's own with `gem.reportMuted`
|
|
954
|
+
([sound](#sound-your-own-mute-control-and-the-arcades)), and your vibration toggle shows only
|
|
955
|
+
while `gem.vibration.available` ([vibration](vibration.md)).
|
|
956
|
+
|
|
957
|
+
**The button goes in the top-right corner**, the same corner in every game, so a player never has
|
|
958
|
+
to look for it, with your Back button just to its left when a screen has one. Place it with the
|
|
959
|
+
reserved-area properties so it never sits under an Arcade control: if the Gem is in that corner, the
|
|
960
|
+
button sits level with it, just inside it.
|
|
901
961
|
|
|
902
962
|
```css
|
|
903
963
|
#settings-button {
|
|
904
964
|
position: absolute;
|
|
905
|
-
top:
|
|
906
|
-
|
|
907
|
-
var(--gem-reserved-tr-height, 0px)
|
|
908
|
-
);
|
|
909
|
-
right: var(--gem-safe-right, 0px);
|
|
965
|
+
top: var(--gem-safe-top, env(safe-area-inset-top, 0px));
|
|
966
|
+
right: max(var(--gem-safe-right, 0px), var(--gem-reserved-tr-width, 0px));
|
|
910
967
|
}
|
|
911
968
|
```
|
|
912
969
|
|
|
@@ -931,8 +988,8 @@ same push as `muted` and `reducedMotion`, and again whenever the player changes
|
|
|
931
988
|
| `graphicsQualityAuto` | `true` / `false` | `true`: the Arcade picked that tier for this device, so you may adapt from it. `false`: the player picked it, so hold it. |
|
|
932
989
|
|
|
933
990
|
`lookSensitivity` to `touchMovement` are the camera and controls settings: see
|
|
934
|
-
[camera and look controls](#camera-and-look-controls) for
|
|
935
|
-
|
|
991
|
+
[camera and look controls](#camera-and-look-controls) for `gem.controls.look()`, which applies the
|
|
992
|
+
two sensitivities and the invert for you. An
|
|
936
993
|
older Arcade sends `recommended` without them; the SDK then fills each with its default (`100`,
|
|
937
994
|
`100`, `false`, `'floating'`). The last two are the starting graphics quality: see
|
|
938
995
|
[graphics quality](#graphics-quality-the-arcade-picks-the-start-your-game-adapts). An older Arcade
|
|
@@ -981,8 +1038,11 @@ volumeSlider.oninput = () => {
|
|
|
981
1038
|
};
|
|
982
1039
|
```
|
|
983
1040
|
|
|
984
|
-
|
|
985
|
-
|
|
1041
|
+
`chosen` here is this device's configuration, so it is kept in
|
|
1042
|
+
[device settings](#device-settings-what-this-device-remembers-about-your-game). A choice that must
|
|
1043
|
+
follow the player to another device — a difficulty, key bindings, an accessibility toggle your game
|
|
1044
|
+
owns — starts from `recommended` the same way and is kept in `gem.me.settings` instead
|
|
1045
|
+
([saving player progress](saving-player-progress.md#for-something-small-reach-for-settings-instead)).
|
|
986
1046
|
|
|
987
1047
|
**Read `muted` and `reducedMotion` as before.** They are not recommendations and they did not move:
|
|
988
1048
|
`muted` is the Arcade's mute, which the SDK applies for you
|
|
@@ -995,30 +1055,12 @@ If your game turns a camera — a drag on a phone, a mouse under pointer lock, a
|
|
|
995
1055
|
stick — the player has four settings for it in the Arcade, per device, the way Roblox players do:
|
|
996
1056
|
**look sensitivity**, **gamepad look sensitivity**, **invert look up/down** and **touch movement**
|
|
997
1057
|
(a floating or a fixed thumbstick). They reach you in `gem.prefs.recommended`
|
|
998
|
-
([the table above](#start-from-the-players-arcade-wide-defaults-gemprefsrecommended))
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
means you apply the Arcade's value (or the player's own choice in your settings, which still wins);
|
|
1003
|
-
`'game'` means your game decides it, and the Arcade's Settings shows that row as "Set by this game";
|
|
1004
|
-
leave out a setting your game does not use, and the Arcade says so beside it. Call it once at boot:
|
|
1005
|
-
|
|
1006
|
-
```js
|
|
1007
|
-
gem.controls.declare({
|
|
1008
|
-
lookSensitivity: 'player',
|
|
1009
|
-
gamepadLookSensitivity: 'player',
|
|
1010
|
-
invertLookY: 'player',
|
|
1011
|
-
touchMovement: 'game', // this game always draws a fixed stick
|
|
1012
|
-
});
|
|
1013
|
-
```
|
|
1014
|
-
|
|
1015
|
-
It is screen state, like [`gem.setWalletVisible`](#the-arcades-wallet-in-the-gem-menu-never-drawn-by-you):
|
|
1016
|
-
each call is the whole answer, the latest wins, and the SDK holds it and sends it again after a
|
|
1017
|
-
reconnect. It returns `false` before the bridge is up (it is held and sent on connect) and under an
|
|
1018
|
-
embedder that does not show these settings; it never throws. A key or a value other than these is
|
|
1019
|
-
dropped.
|
|
1058
|
+
([the table above](#start-from-the-players-arcade-wide-defaults-gemprefsrecommended)) as the
|
|
1059
|
+
player's Arcade-wide defaults, and, like every [game setting](#game-settings), they are where your
|
|
1060
|
+
own settings start. There is nothing to tell the Arcade about them: its Settings show the player's
|
|
1061
|
+
defaults, never what a game uses.
|
|
1020
1062
|
|
|
1021
|
-
**
|
|
1063
|
+
**Turn look input into a camera turn — `gem.controls.look()`.** It is pure math: it listens to
|
|
1022
1064
|
nothing and moves no camera. Give it the deltas your input code already has; it returns radians to
|
|
1023
1065
|
**add**, `yaw` positive turning right and `pitch` positive looking up, at Roblox's base speeds
|
|
1024
1066
|
(`gem.controls.LOOK_BASE`: touch 1° across and 0.66° up/down per CSS px, mouse 0.5° and 0.385°,
|
|
@@ -1067,15 +1109,16 @@ function frame(dt) {
|
|
|
1067
1109
|
Do not copy them into variables at boot.
|
|
1068
1110
|
- **The player's choice in your game wins.** If your settings screen has its own look speed or
|
|
1069
1111
|
invert, pass it — `gem.controls.look({ sensitivity: 1.5, invertY: true })`, or later
|
|
1070
|
-
`look.set({ sensitivity: 1.5 })` — and that input stops following the Arcade.
|
|
1112
|
+
`look.set({ sensitivity: 1.5 })` — and that input stops following the Arcade. Keep it in
|
|
1113
|
+
`gem.deviceSettings`: a look speed is this device's configuration.
|
|
1071
1114
|
`look.set({ sensitivity: undefined })` hands it back. `sensitivity` and `gamepadSensitivity` are
|
|
1072
1115
|
multipliers (`1` is the base speed); the Arcade's percentages are divided by 100 for you.
|
|
1073
1116
|
- **Pass `pitch` for touch**, the camera's current pitch in radians (0 at the horizon, positive
|
|
1074
1117
|
looking up), or the slowdown near vertical is skipped. A mouse and a stick are never slowed.
|
|
1075
1118
|
- **`touchMovement` is yours to draw.** The SDK has no thumbstick: read
|
|
1076
|
-
`gem.prefs.recommended?.touchMovement` and draw a floating or a fixed one
|
|
1077
|
-
|
|
1078
|
-
- **No camera, no call.** A game with no camera to turn
|
|
1119
|
+
`gem.prefs.recommended?.touchMovement` and draw a floating or a fixed one. A game with only one
|
|
1120
|
+
kind draws that one and ignores the field.
|
|
1121
|
+
- **No camera, no call.** A game with no camera to turn never calls `look`.
|
|
1079
1122
|
|
|
1080
1123
|
## Graphics quality: the Arcade picks the start, your game adapts
|
|
1081
1124
|
|
|
@@ -1139,7 +1182,8 @@ What it does, so you know what to expect:
|
|
|
1139
1182
|
|
|
1140
1183
|
## The Arcade's menu
|
|
1141
1184
|
|
|
1142
|
-
The Arcade keeps one button over every game, on every device: the Gem,
|
|
1185
|
+
The Arcade keeps one button over every game, on every device: the Gem, top-left unless your game
|
|
1186
|
+
[asks for another corner](#moving-the-gem-gemsetgemcorner). Its menu is
|
|
1143
1187
|
how a player leaves (**Return to the Arcade**), sees their Gems, Tickets and achievement score, mutes,
|
|
1144
1188
|
goes fullscreen, reports a problem and changes their Arcade-wide settings — the ones that reach you as [`gem.prefs.recommended`](#game-settings). You can add up to
|
|
1145
1189
|
four entries of your own:
|
|
@@ -1150,10 +1194,8 @@ gem.setMenu({
|
|
|
1150
1194
|
});
|
|
1151
1195
|
```
|
|
1152
1196
|
|
|
1153
|
-
- **
|
|
1154
|
-
|
|
1155
|
-
passes it keeps working, and another embedder may still show an entry for it — but in the Arcade
|
|
1156
|
-
it adds nothing.
|
|
1197
|
+
- **Your settings are not in it.** They open from [your own button](#game-settings), and the
|
|
1198
|
+
Arcade's menu never opens them.
|
|
1157
1199
|
- **`items` are up to four entries of your own**, shown in a group below the Arcade's. `id` is your
|
|
1158
1200
|
name for the entry (1 to 32 characters); `label` is what the player reads, **already in their
|
|
1159
1201
|
language** (`gem.prefs.locale`) — the Arcade does not translate it, cuts it at 40 characters, and
|
|
@@ -1173,9 +1215,9 @@ gem.setMenu({
|
|
|
1173
1215
|
settings button is there either way; what such a game also needs is a way out of its own, a control
|
|
1174
1216
|
that calls `gem.exit()`.
|
|
1175
1217
|
|
|
1176
|
-
Your own **back or home** button — the one that moves around inside your game — goes **top-
|
|
1177
|
-
|
|
1178
|
-
leaving is the Arcade's menu, so a player always knows which control does which.
|
|
1218
|
+
Your own **back or home** button — the one that moves around inside your game — goes **top-right,
|
|
1219
|
+
just left of your settings button** ([the top of the screen](#the-top-of-the-screen)). It never
|
|
1220
|
+
leaves the game: leaving is the Arcade's menu, so a player always knows which control does which.
|
|
1179
1221
|
|
|
1180
1222
|
## Sound: your own mute control, and the Arcade's
|
|
1181
1223
|
|
|
@@ -1265,8 +1307,8 @@ belong to the Arcade. **Your game never draws one**, and never shows a pending s
|
|
|
1265
1307
|
Arcade takes every figure from the platform and shows them itself.
|
|
1266
1308
|
|
|
1267
1309
|
**The Gem Arcade draws no wallet over your game** (since 2026-10-11). Nothing of it sits in your top
|
|
1268
|
-
row, at rest or when a balance moves. The player opens the Gem menu — the Gem button
|
|
1269
|
-
|
|
1310
|
+
row, at rest or when a balance moves. The player opens the Gem menu — the Gem button, in whichever
|
|
1311
|
+
corner it sits — to see their Gems, Tickets and achievement score, and every payout
|
|
1270
1312
|
[`gem.cueReward`](inventory-and-catalogs.md#showing-the-reward-land-in-the-arcades-wallet) announces
|
|
1271
1313
|
flies into the Gem button. A payout you do not cue flies too, from the middle of your frame, so cue
|
|
1272
1314
|
the ones the player earns on screen and the flight starts where they are looking.
|
|
@@ -1460,9 +1502,12 @@ This is a platform rule, not advice — see
|
|
|
1460
1502
|
|
|
1461
1503
|
### Arcade avatars: the character the player wears, and changing it mid-game
|
|
1462
1504
|
|
|
1463
|
-
Some games draw **Arcade avatars
|
|
1464
|
-
|
|
1465
|
-
|
|
1505
|
+
Some games draw **Arcade avatars**: the player's look in your game is something they own and wear
|
|
1506
|
+
in the Arcade. **The whole guide for 3D avatars — rig families, declaring the ones your game
|
|
1507
|
+
accepts, uploading and selling your own, the purchase flow and loading the model — is
|
|
1508
|
+
[arcade-avatars.md](arcade-avatars.md).** This section is the summary. The Arcade's avatar picker
|
|
1509
|
+
has **two tabs**, and they are **two separate worn choices** — wearing one never changes the
|
|
1510
|
+
other:
|
|
1466
1511
|
|
|
1467
1512
|
| Tab | What the player wears | On `gem.player` and every room member |
|
|
1468
1513
|
|---|---|---|
|
|
@@ -1476,6 +1521,8 @@ are **two separate worn choices** — wearing one never changes the other:
|
|
|
1476
1521
|
(an idle, say). It never changes at its URL, so cache it by URL.
|
|
1477
1522
|
- **`avatar3dIconUrl`** is a rendered portrait of that character, for a roster or a menu where
|
|
1478
1523
|
loading the model is too much. It is **not** the 2D portrait.
|
|
1524
|
+
- **`avatars3d`** lists every worn 3D character, one per **rig family**; the three fields above
|
|
1525
|
+
are your game's first declared family's. `gem.avatars3d.worn()` reads it for you.
|
|
1479
1526
|
|
|
1480
1527
|
**Draw the 3D character, falling back to what you did before.** A game that drew characters
|
|
1481
1528
|
before the 3D tab existed keyed its own models on `avatarIconId`. Keep that mapping as the
|
|
@@ -1522,7 +1569,7 @@ is missing now (a player who takes off their 3D character has no `avatar3dId`).
|
|
|
1522
1569
|
|
|
1523
1570
|
**A "Change avatar" button belongs in your own UI**, and `gem.openAvatarPicker()` opens the
|
|
1524
1571
|
Arcade's picker from it. Pass `{ tab: '3d' }` or `{ tab: '2d' }` to open on a tab; omit it and
|
|
1525
|
-
the Arcade decides (a game
|
|
1572
|
+
the Arcade decides (a game that declares avatar families gets the 3D tab). The Arcade pauses your game
|
|
1526
1573
|
while the picker is open, saves the player's choice, sends the player again, and resumes you:
|
|
1527
1574
|
|
|
1528
1575
|
```js
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indiegems/gem-web-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.1.0",
|
|
4
4
|
"description": "postMessage bridge contract between gem-web and browser-playable games",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"import": "./dist/gem-web-sdk.quality.js"
|
|
41
41
|
},
|
|
42
42
|
"./schema": "./schema/bridge.v1.json",
|
|
43
|
-
"./brand/*": "./brand/*"
|
|
43
|
+
"./brand/*": "./brand/*",
|
|
44
|
+
"./rigs/*": "./rigs/*"
|
|
44
45
|
},
|
|
45
46
|
"files": [
|
|
46
47
|
"bin",
|
|
@@ -50,6 +51,7 @@
|
|
|
50
51
|
"schema/bridge.v1.json",
|
|
51
52
|
"schema/capabilities.public.json",
|
|
52
53
|
"brand/*.png",
|
|
54
|
+
"rigs/*.glb",
|
|
53
55
|
"docs/*.md",
|
|
54
56
|
"docs/manifest.json",
|
|
55
57
|
"docs/api/*.md",
|
|
Binary file
|