create-spud 0.0.1 → 0.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/example/demo.html +1 -0
- package/example/index.html +1 -0
- package/example/readme.md +6 -3
- package/example/src/assets/fonts/atari.ttf +0 -0
- package/example/src/demo.ts +0 -1
- package/example/src/gameplay.ts +9 -7
- package/example/src/index.ts +0 -1
- package/example/src/sprite.ts +3 -2
- package/index.ts +30 -4
- package/package.json +1 -1
- package/readme.md +20 -2
package/example/demo.html
CHANGED
package/example/index.html
CHANGED
package/example/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# game_name
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Getting started
|
|
4
4
|
|
|
5
5
|
Useful scripts:
|
|
6
6
|
|
|
@@ -24,14 +24,15 @@ bun run dev:demo
|
|
|
24
24
|
bun run build
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
## Resources:
|
|
28
28
|
|
|
29
29
|
- **Game assets**
|
|
30
30
|
- [itch.io game assets](https://itch.io/game-assets)
|
|
31
31
|
|
|
32
32
|
- **Audio**
|
|
33
|
-
- [sfxr.me sfx generator](https://pro.sfxr.me/)
|
|
33
|
+
- [sfxr.me sfx generator](https://pro.sfxr.me/) and [bfxr](https://www.bfxr.net/)
|
|
34
34
|
- [OpenGameArt cc0 sound effects](https://opengameart.org/art-search-advanced?keys=&title=&field_art_tags_tid_op=or&field_art_tags_tid=&name=&field_art_type_tid%5B%5D=13&field_art_licenses_tid%5B%5D=4)
|
|
35
|
+
- [Free sfx](https://www.freesfx.co.uk/)
|
|
35
36
|
|
|
36
37
|
- **Animation**
|
|
37
38
|
- [easings.net](https://easings.net/)
|
|
@@ -39,10 +40,12 @@ bun run build
|
|
|
39
40
|
- **Sprites & pixel art**
|
|
40
41
|
- [Aseprite](https://www.aseprite.org/)
|
|
41
42
|
- [Pixel planet generator (itch.io)](https://deep-fold.itch.io/pixel-planet-generator)
|
|
43
|
+
- [Paint of Persia](https://dunin.itch.io/ptop)
|
|
42
44
|
|
|
43
45
|
- **Fonts**
|
|
44
46
|
- [Google Fonts](https://fonts.google.com/)
|
|
45
47
|
- [Font Squirrel](https://www.fontsquirrel.com/)
|
|
48
|
+
- [Font Library](https://fontlibrary.org/)
|
|
46
49
|
|
|
47
50
|
- **Documentation for dev tools & APIs used in this project**
|
|
48
51
|
- [Vite docs](https://vite.dev/)
|
|
Binary file
|
package/example/src/demo.ts
CHANGED
package/example/src/gameplay.ts
CHANGED
|
@@ -21,17 +21,12 @@ export function draw(state: State, ctx: CanvasRenderingContext2D) {
|
|
|
21
21
|
y: height / 2,
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
/* pixel_art_image_smoothing */
|
|
25
|
+
|
|
24
26
|
// clear out the background
|
|
25
27
|
ctx.fillStyle = "#0b0d1a";
|
|
26
28
|
ctx.fillRect(0, 0, width, height);
|
|
27
29
|
|
|
28
|
-
// write some text
|
|
29
|
-
ctx.fillStyle = "white";
|
|
30
|
-
ctx.textAlign = "center";
|
|
31
|
-
ctx.textBaseline = "middle";
|
|
32
|
-
ctx.font = "32px sans-serif";
|
|
33
|
-
ctx.fillText("hello, gamer", center.x, center.y);
|
|
34
|
-
|
|
35
30
|
// draw an orbiting circle
|
|
36
31
|
const moon = {
|
|
37
32
|
radius: 30,
|
|
@@ -44,4 +39,11 @@ export function draw(state: State, ctx: CanvasRenderingContext2D) {
|
|
|
44
39
|
},
|
|
45
40
|
};
|
|
46
41
|
/* moon_sprite_draw */
|
|
42
|
+
|
|
43
|
+
// write some text
|
|
44
|
+
ctx.fillStyle = "white";
|
|
45
|
+
ctx.textAlign = "center";
|
|
46
|
+
ctx.textBaseline = "middle";
|
|
47
|
+
/* fonts_ctx_font */
|
|
48
|
+
ctx.fillText("hello, gamer", center.x, center.y);
|
|
47
49
|
}
|
package/example/src/index.ts
CHANGED
package/example/src/sprite.ts
CHANGED
|
@@ -30,12 +30,13 @@ export function drawSprite(
|
|
|
30
30
|
frameIndex: number,
|
|
31
31
|
x: number,
|
|
32
32
|
y: number,
|
|
33
|
+
scale = 1,
|
|
33
34
|
) {
|
|
34
35
|
const frame = frameIndex % sheet.frameCount;
|
|
35
36
|
const sourceX = frame * sheet.frameWidthPx;
|
|
36
37
|
const sourceY = 0;
|
|
37
|
-
const drawWidthPx = sheet.frameWidthPx;
|
|
38
|
-
const drawHeightPx = sheet.frameHeightPx;
|
|
38
|
+
const drawWidthPx = sheet.frameWidthPx * scale;
|
|
39
|
+
const drawHeightPx = sheet.frameHeightPx * scale;
|
|
39
40
|
|
|
40
41
|
// see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
|
|
41
42
|
ctx.drawImage(
|
package/index.ts
CHANGED
|
@@ -7,7 +7,12 @@ import kebabcase from "lodash.kebabcase";
|
|
|
7
7
|
|
|
8
8
|
intro("It's spud time.");
|
|
9
9
|
|
|
10
|
-
const Feature = {
|
|
10
|
+
const Feature = {
|
|
11
|
+
Audio: "Audio",
|
|
12
|
+
Fonts: "Fonts",
|
|
13
|
+
PixelArt: "PixelArt",
|
|
14
|
+
} as const;
|
|
15
|
+
|
|
11
16
|
type Feature = (typeof Feature)[keyof typeof Feature];
|
|
12
17
|
|
|
13
18
|
const { rawGameName, features, shouldContinue } = await group(
|
|
@@ -23,10 +28,11 @@ const { rawGameName, features, shouldContinue } = await group(
|
|
|
23
28
|
features: () =>
|
|
24
29
|
multiselect({
|
|
25
30
|
message: "Features",
|
|
26
|
-
initialValues: [
|
|
31
|
+
initialValues: [],
|
|
27
32
|
required: false,
|
|
28
33
|
options: [
|
|
29
34
|
{ label: "Audio", value: Feature.Audio },
|
|
35
|
+
{ label: "Fonts", value: Feature.Fonts },
|
|
30
36
|
{ label: "Pixel art", value: Feature.PixelArt },
|
|
31
37
|
],
|
|
32
38
|
}),
|
|
@@ -91,6 +97,8 @@ async function copyDir(
|
|
|
91
97
|
if (entry.isDirectory()) {
|
|
92
98
|
if (entry.name === "node_modules" || entry.name === "dist") continue;
|
|
93
99
|
if (entry.name === "audio" && !replacements.features.includes(Feature.Audio)) continue;
|
|
100
|
+
if (entry.name === "fonts" && !replacements.features.includes(Feature.Fonts)) continue;
|
|
101
|
+
if (entry.name === "images" && !replacements.features.includes(Feature.PixelArt)) continue;
|
|
94
102
|
await copyDir(sourcePath, destPath, replacements);
|
|
95
103
|
continue;
|
|
96
104
|
}
|
|
@@ -123,9 +131,9 @@ async function copyDir(
|
|
|
123
131
|
replacements.features.includes(Feature.PixelArt) ? "image-rendering: pixelated;\n" : "",
|
|
124
132
|
)
|
|
125
133
|
.replaceAll(
|
|
126
|
-
"/* pixel_art_image_smoothing */\n",
|
|
134
|
+
" /* pixel_art_image_smoothing */\n",
|
|
127
135
|
replacements.features.includes(Feature.PixelArt)
|
|
128
|
-
? "ctx.imageSmoothingEnabled = false;\n"
|
|
136
|
+
? " // ensure sharp edges for scaled pixel art\n ctx.imageSmoothingEnabled = false;\n"
|
|
129
137
|
: "",
|
|
130
138
|
)
|
|
131
139
|
.replaceAll(
|
|
@@ -138,6 +146,18 @@ async function copyDir(
|
|
|
138
146
|
"/* audio_import */\n",
|
|
139
147
|
replacements.features.includes(Feature.Audio) ? 'import { sfx } from "./audio";\n' : "",
|
|
140
148
|
)
|
|
149
|
+
.replaceAll(
|
|
150
|
+
" /* fonts_face_css */\n",
|
|
151
|
+
replacements.features.includes(Feature.Fonts)
|
|
152
|
+
? ` @font-face {
|
|
153
|
+
font-family: 'Atari';
|
|
154
|
+
font-display: block;
|
|
155
|
+
src: url('/src/assets/fonts/atari.ttf') format('truetype');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
`
|
|
159
|
+
: "",
|
|
160
|
+
)
|
|
141
161
|
.replaceAll(
|
|
142
162
|
" /* moon_sprite_draw */\n",
|
|
143
163
|
replacements.features.includes(Feature.PixelArt)
|
|
@@ -158,6 +178,12 @@ async function copyDir(
|
|
|
158
178
|
});
|
|
159
179
|
`
|
|
160
180
|
: " /* handle inputs and update state */\n",
|
|
181
|
+
)
|
|
182
|
+
.replaceAll(
|
|
183
|
+
"/* fonts_ctx_font */",
|
|
184
|
+
replacements.features.includes(Feature.Fonts)
|
|
185
|
+
? 'ctx.font = "48px Atari";'
|
|
186
|
+
: 'ctx.font = "48px sans-serif";',
|
|
161
187
|
);
|
|
162
188
|
await Bun.write(destPath, replaced);
|
|
163
189
|
}
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -11,14 +11,27 @@ bun create spud
|
|
|
11
11
|
## What gets created
|
|
12
12
|
|
|
13
13
|
- A new folder named after your game
|
|
14
|
-
- A ready-to-run spud game project
|
|
14
|
+
- A ready-to-run minimal spud game project
|
|
15
|
+
|
|
16
|
+
## Automatically set up for you
|
|
17
|
+
|
|
18
|
+
- A working [Vite](https://vite.dev) + [TypeScript](https://www.typescriptlang.org) project with [hot module replacement](https://vite.dev/guide/features#hot-module-replacement) (no config needed)
|
|
19
|
+
- Canvas setup with [DPI-aware scaling](https://web.dev/articles/canvas-hidipi)
|
|
20
|
+
- Fixed‑timestep update loop + variable-rate render loop ready to edit
|
|
21
|
+
- Static asset import handling through [Vite](https://vite.dev/guide/assets#importing-asset-as-url) and [image optimization](https://github.com/FatehAK/vite-plugin-image-optimizer)
|
|
22
|
+
- `index.html` and `demo.html` entry points with scripts ready to run
|
|
23
|
+
- _(Demo mode is a non-interactive gameplay example, providing your potential players a preview into your actual game. Spud favors realistic self-playing demos over pre-recorded trailers.)_
|
|
24
|
+
- Gamepad integration with the [spud API (npm)](https://www.npmjs.com/package/@spud.gg/api)
|
|
25
|
+
- Optional:
|
|
26
|
+
- Audio examples (low-latency [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API) sfx and [streaming background music](https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement)) with [autoplay best practices](https://developer.chrome.com/blog/web-audio-autoplay) for web games.
|
|
27
|
+
- Pixel‑art mode (example sprite sheet, image smoothing disabled)
|
|
15
28
|
|
|
16
29
|
## Requirements
|
|
17
30
|
|
|
18
31
|
- [Bun](https://bun.com/) (runtime + package manager)
|
|
19
32
|
|
|
20
33
|
<details>
|
|
21
|
-
<summary><strong>
|
|
34
|
+
<summary><strong>Installation guide</strong></summary>
|
|
22
35
|
|
|
23
36
|
1. Install Bun ([docs](https://bun.com/docs/installation)):
|
|
24
37
|
|
|
@@ -33,3 +46,8 @@ curl -fsSL https://bun.sh/install | bash
|
|
|
33
46
|
## Notes
|
|
34
47
|
|
|
35
48
|
- This CLI is Bun-only. If you try to run it with Node (`npx create spud`, for example), that won't work.
|
|
49
|
+
|
|
50
|
+
<!--
|
|
51
|
+
TODO
|
|
52
|
+
- add hmr hints for Vite to cleanup / reinit the raf loops and keep game state reloadable.
|
|
53
|
+
-->
|