star-sdk-cli 0.1.14 → 0.1.15
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/dist/cli.mjs +101 -42
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -80,7 +80,39 @@ Star.game(ctx => {
|
|
|
80
80
|
|
|
81
81
|
## Common Patterns
|
|
82
82
|
|
|
83
|
-
### Game Over
|
|
83
|
+
### Game Over Screen with Leaderboard
|
|
84
|
+
|
|
85
|
+
Use DOM buttons (via \`ui.render()\` + \`on()\`) for all interactive UI \u2014 never draw buttons on canvas:
|
|
86
|
+
|
|
87
|
+
\`\`\`javascript
|
|
88
|
+
// UI button handler (register once \u2014 survives ui.render calls)
|
|
89
|
+
ctx.on('click', '#lb-btn', () => Star.leaderboard.show());
|
|
90
|
+
ctx.on('click', '#restart-btn', () => startGame());
|
|
91
|
+
|
|
92
|
+
// Gameplay tap handler (single handler, state-based)
|
|
93
|
+
canvas.addEventListener('pointerdown', () => {
|
|
94
|
+
if (state === 'playing') { jump(); }
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
function endGame() {
|
|
98
|
+
state = 'gameover';
|
|
99
|
+
Star.leaderboard.submit(score);
|
|
100
|
+
ctx.ui.render(\`
|
|
101
|
+
<div class="h-full flex flex-col items-center justify-center text-white">
|
|
102
|
+
<div class="text-3xl font-bold mb-2">GAME OVER</div>
|
|
103
|
+
<div class="text-6xl font-bold mb-6">\${score}</div>
|
|
104
|
+
<button id="lb-btn" class="px-6 py-3 mb-4 bg-purple-600 rounded-lg font-bold">
|
|
105
|
+
VIEW LEADERBOARD
|
|
106
|
+
</button>
|
|
107
|
+
<button id="restart-btn" class="px-6 py-3 bg-gray-700 rounded-lg">
|
|
108
|
+
PLAY AGAIN
|
|
109
|
+
</button>
|
|
110
|
+
</div>
|
|
111
|
+
\`);
|
|
112
|
+
}
|
|
113
|
+
\`\`\`
|
|
114
|
+
|
|
115
|
+
### Submit Score Only (No UI)
|
|
84
116
|
|
|
85
117
|
\`\`\`javascript
|
|
86
118
|
function gameOver(finalScore) {
|
|
@@ -127,6 +159,8 @@ canvas.onclick = (e) => {
|
|
|
127
159
|
- **Don't** use \`setInterval\` for game loops - use \`ctx.loop()\`
|
|
128
160
|
- **Don't** destructure Star - use \`Star.audio\`, \`Star.leaderboard\`, etc.
|
|
129
161
|
- **Don't** invent audio preset names - only 17 exist (see audio.md)
|
|
162
|
+
- **Don't** draw buttons on canvas (fillRect + hit-test) \u2014 use \`ui.render()\` with HTML \`<button>\` elements + \`on()\` for clicks. Canvas-drawn "buttons" conflict with tap handlers and have no hover/focus states.
|
|
163
|
+
- **Don't** register multiple \`canvas.addEventListener('pointerdown', ...)\` handlers \u2014 use ONE handler with state-based logic. Use \`on()\` for UI button clicks.
|
|
130
164
|
|
|
131
165
|
## Audio Presets (Full List)
|
|
132
166
|
|
|
@@ -143,9 +177,9 @@ import Star from 'star-sdk';
|
|
|
143
177
|
Star.init({ gameId: '<gameId from .starrc>' }); // run: npx star-sdk init
|
|
144
178
|
|
|
145
179
|
Star.game(ctx => {
|
|
146
|
-
const { canvas, width, height, ctx: c } = ctx;
|
|
180
|
+
const { canvas, width, height, ctx: c, ui, on } = ctx;
|
|
147
181
|
let score = 0;
|
|
148
|
-
let
|
|
182
|
+
let state = 'playing';
|
|
149
183
|
let playerY = height / 2;
|
|
150
184
|
let obstacles = [];
|
|
151
185
|
|
|
@@ -157,13 +191,52 @@ Star.game(ctx => {
|
|
|
157
191
|
|
|
158
192
|
// Spawn obstacles
|
|
159
193
|
setInterval(() => {
|
|
160
|
-
if (
|
|
194
|
+
if (state === 'playing') {
|
|
161
195
|
obstacles.push({ x: width, y: Math.random() * height, passed: false });
|
|
162
196
|
}
|
|
163
197
|
}, 2000);
|
|
164
198
|
|
|
199
|
+
// Gameplay input \u2014 ONE handler, state-based
|
|
200
|
+
canvas.addEventListener('pointerdown', () => {
|
|
201
|
+
if (state === 'playing') {
|
|
202
|
+
playerY -= 50;
|
|
203
|
+
Star.audio.play('jump');
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// UI button clicks \u2014 use on() for DOM buttons, not canvas hit-testing
|
|
208
|
+
on('click', '#lb-btn', () => Star.leaderboard.show());
|
|
209
|
+
on('click', '#restart-btn', () => startGame());
|
|
210
|
+
|
|
211
|
+
function startGame() {
|
|
212
|
+
state = 'playing';
|
|
213
|
+
score = 0;
|
|
214
|
+
playerY = height / 2;
|
|
215
|
+
obstacles = [];
|
|
216
|
+
ui.render(''); // Clear game-over UI
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function endGame() {
|
|
220
|
+
state = 'gameover';
|
|
221
|
+
Star.audio.play('hurt');
|
|
222
|
+
Star.leaderboard.submit(score);
|
|
223
|
+
// Game-over UI with DOM buttons (not canvas-drawn)
|
|
224
|
+
ui.render(\`
|
|
225
|
+
<div class="h-full flex flex-col items-center justify-center text-white">
|
|
226
|
+
<div class="text-3xl font-bold mb-2">GAME OVER</div>
|
|
227
|
+
<div class="text-6xl font-bold mb-6">\${score}</div>
|
|
228
|
+
<button id="lb-btn" class="px-6 py-3 mb-4 bg-purple-600 rounded-lg font-bold">
|
|
229
|
+
VIEW LEADERBOARD
|
|
230
|
+
</button>
|
|
231
|
+
<button id="restart-btn" class="px-6 py-3 bg-gray-700 rounded-lg">
|
|
232
|
+
PLAY AGAIN
|
|
233
|
+
</button>
|
|
234
|
+
</div>
|
|
235
|
+
\`);
|
|
236
|
+
}
|
|
237
|
+
|
|
165
238
|
ctx.loop((dt) => {
|
|
166
|
-
if (
|
|
239
|
+
if (state !== 'playing') return;
|
|
167
240
|
|
|
168
241
|
// Clear
|
|
169
242
|
c.fillStyle = '#111827';
|
|
@@ -172,24 +245,15 @@ Star.game(ctx => {
|
|
|
172
245
|
// Update obstacles
|
|
173
246
|
obstacles.forEach(obs => {
|
|
174
247
|
obs.x -= 200 * dt;
|
|
175
|
-
|
|
176
|
-
// Score when passed
|
|
177
248
|
if (!obs.passed && obs.x < 50) {
|
|
178
249
|
obs.passed = true;
|
|
179
250
|
score += 10;
|
|
180
251
|
Star.audio.play('coin');
|
|
181
252
|
}
|
|
182
|
-
|
|
183
|
-
// Collision
|
|
184
253
|
if (Math.abs(obs.x - 50) < 20 && Math.abs(obs.y - playerY) < 30) {
|
|
185
|
-
|
|
186
|
-
Star.audio.play('hurt');
|
|
187
|
-
Star.leaderboard.submit(score);
|
|
188
|
-
Star.leaderboard.show();
|
|
254
|
+
endGame();
|
|
189
255
|
}
|
|
190
256
|
});
|
|
191
|
-
|
|
192
|
-
// Remove off-screen
|
|
193
257
|
obstacles = obstacles.filter(o => o.x > -20);
|
|
194
258
|
|
|
195
259
|
// Draw player
|
|
@@ -209,14 +273,6 @@ Star.game(ctx => {
|
|
|
209
273
|
c.font = '24px sans-serif';
|
|
210
274
|
c.fillText(\`Score: \${score}\`, 20, 40);
|
|
211
275
|
});
|
|
212
|
-
|
|
213
|
-
// Jump on click/tap
|
|
214
|
-
canvas.onclick = () => {
|
|
215
|
-
if (!gameOver) {
|
|
216
|
-
playerY -= 50;
|
|
217
|
-
Star.audio.play('jump');
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
276
|
});
|
|
221
277
|
\`\`\`
|
|
222
278
|
|
|
@@ -436,11 +492,11 @@ game(({ ctx, width, height, on, loop, ui, canvas }) => {
|
|
|
436
492
|
console.log('Button clicked!');
|
|
437
493
|
});
|
|
438
494
|
|
|
439
|
-
// 4.
|
|
440
|
-
//
|
|
441
|
-
//
|
|
495
|
+
// 4. Gameplay taps \u2014 use canvas.addEventListener for game mechanics only
|
|
496
|
+
// For buttons/menus, use ui.render() + on() above
|
|
497
|
+
// The SDK auto-suppresses this handler when a UI button is clicked
|
|
442
498
|
canvas.addEventListener('pointerdown', (e) => {
|
|
443
|
-
console.log('
|
|
499
|
+
console.log('Gameplay tap!', e);
|
|
444
500
|
});
|
|
445
501
|
});
|
|
446
502
|
\`\`\`
|
|
@@ -468,7 +524,9 @@ The 2D drawing context. Its transform is already scaled for DPR. You **always dr
|
|
|
468
524
|
|
|
469
525
|
The \`<canvas>\` element itself.
|
|
470
526
|
|
|
471
|
-
- **Use this for gameplay input
|
|
527
|
+
- **Use this for gameplay input** (e.g., \`pointerdown\` for tap-to-jump, \`pointermove\` for aiming).
|
|
528
|
+
- **For buttons and menus, use \`ui.render()\` + \`on()\` instead** \u2014 HTML buttons get hover states, touch targets, accessibility, and never conflict with gameplay handlers.
|
|
529
|
+
- Use ONE \`addEventListener\` handler with state-based logic. Multiple pointerdown handlers cause ordering bugs.
|
|
472
530
|
|
|
473
531
|
### \`width: number\` (getter)
|
|
474
532
|
|
|
@@ -710,6 +768,8 @@ game(({ ctx, width, height, loop, toStagePoint, canvas }) => {
|
|
|
710
768
|
|
|
711
769
|
### Recipe 5: Complex Game with Canvas + UI + Events (like FLOW)
|
|
712
770
|
|
|
771
|
+
**Key pattern:** Canvas for gameplay input, DOM buttons for UI. Never draw buttons on canvas.
|
|
772
|
+
|
|
713
773
|
\`\`\`ts
|
|
714
774
|
import { game } from 'star-canvas';
|
|
715
775
|
import { createLeaderboard } from 'star-leaderboard';
|
|
@@ -720,23 +780,19 @@ game(({ ctx, width, height, loop, ui, on, canvas, toStagePoint }) => {
|
|
|
720
780
|
let score = 0;
|
|
721
781
|
let state = 'menu';
|
|
722
782
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
783
|
+
// 1. ONE gameplay tap handler \u2014 state-based logic, no button hit-testing
|
|
784
|
+
canvas.addEventListener('pointerdown', () => {
|
|
785
|
+
if (state === 'menu') startGame();
|
|
786
|
+
else if (state === 'playing') {
|
|
727
787
|
// ... (player float logic) ...
|
|
728
788
|
}
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
// 1. Listen for screen taps - this makes UI click-through automatically
|
|
732
|
-
canvas.addEventListener('pointerdown', handleTap);
|
|
733
|
-
|
|
734
|
-
// 2. Listen for button clicks \u2014 on() auto-enables pointer-events for the selector
|
|
735
|
-
on('click', '#leaderboard-btn', (e) => {
|
|
736
|
-
e.stopPropagation();
|
|
737
|
-
leaderboard.show();
|
|
738
789
|
});
|
|
739
790
|
|
|
791
|
+
// 2. DOM button clicks \u2014 on() auto-enables pointer-events
|
|
792
|
+
// The SDK suppresses the canvas handler when a button is clicked
|
|
793
|
+
on('click', '#leaderboard-btn', () => leaderboard.show());
|
|
794
|
+
on('click', '#restart-btn', () => startGame());
|
|
795
|
+
|
|
740
796
|
// 3. Render UI \u2014 elements targeted by on() are automatically interactive
|
|
741
797
|
let lastState = null;
|
|
742
798
|
let lastScore = -1;
|
|
@@ -767,7 +823,9 @@ game(({ ctx, width, height, loop, ui, on, canvas, toStagePoint }) => {
|
|
|
767
823
|
<button id="leaderboard-btn" class="px-6 py-3 mb-4 bg-gradient-to-r from-blue-600 to-purple-600 rounded-xl font-bold shadow-lg shadow-blue-500/20">
|
|
768
824
|
VIEW LEADERBOARD
|
|
769
825
|
</button>
|
|
770
|
-
<
|
|
826
|
+
<button id="restart-btn" class="px-6 py-3 bg-gray-700 rounded-xl font-bold">
|
|
827
|
+
PLAY AGAIN
|
|
828
|
+
</button>
|
|
771
829
|
</div>\`);
|
|
772
830
|
}
|
|
773
831
|
}
|
|
@@ -779,6 +837,7 @@ game(({ ctx, width, height, loop, ui, on, canvas, toStagePoint }) => {
|
|
|
779
837
|
function startGame() {
|
|
780
838
|
state = 'playing';
|
|
781
839
|
score = 0;
|
|
840
|
+
ui.render(''); // Clear game-over buttons
|
|
782
841
|
updateUI();
|
|
783
842
|
}
|
|
784
843
|
|