@origingame/create-game 0.1.0 → 0.2.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/README.md +15 -7
- package/archetypes/README.md +4 -2
- package/archetypes/idle/gitignore +8 -0
- package/archetypes/idle/package.json +1 -1
- package/archetypes/idle/project.ogt +1 -1
- package/archetypes/match3/gitignore +8 -0
- package/archetypes/match3/package.json +1 -1
- package/archetypes/match3/project.ogt +1 -1
- package/archetypes/rhythm/gitignore +8 -0
- package/archetypes/rhythm/package.json +1 -1
- package/archetypes/rhythm/project.ogt +1 -1
- package/archetypes/roguelike/gitignore +8 -0
- package/archetypes/roguelike/package.json +1 -1
- package/archetypes/roguelike/project.ogt +1 -1
- package/archetypes/runner/gitignore +8 -0
- package/archetypes/runner/package.json +1 -1
- package/archetypes/runner/project.ogt +1 -1
- package/archetypes/third-person/gitignore +8 -0
- package/archetypes/third-person/package.json +1 -1
- package/archetypes/third-person/project.ogt +1 -1
- package/archetypes/topdown/gitignore +8 -0
- package/archetypes/topdown/package.json +1 -1
- package/archetypes/topdown/project.ogt +1 -1
- package/archetypes/tower-defense/gitignore +8 -0
- package/archetypes/tower-defense/package.json +1 -1
- package/archetypes/tower-defense/project.ogt +1 -1
- package/bin/create.ts +80 -16
- package/package.json +5 -4
- package/template/gitignore +8 -0
- package/template/package.json +1 -1
- package/template/project.ogt +1 -1
- package/template-2d/.origin/setup +14 -0
- package/template-2d/LICENSES.md +14 -0
- package/template-2d/README.md +39 -0
- package/template-2d/assets/audio/coin.wav +0 -0
- package/template-2d/assets/audio/jump.wav +0 -0
- package/template-2d/assets/fonts/OFL.txt +93 -0
- package/template-2d/assets/fonts/ui.ttf +0 -0
- package/template-2d/assets/fonts/ui.ttf.ogt +11 -0
- package/template-2d/assets/looks/origin-game-2d.ogt +12 -0
- package/template-2d/assets/maps/level.ogt +112 -0
- package/template-2d/assets/sprites/coin.ogt +11 -0
- package/template-2d/assets/sprites/coin.png +0 -0
- package/template-2d/assets/sprites/coin.png.ogt +7 -0
- package/template-2d/assets/sprites/courier.ogt +34 -0
- package/template-2d/assets/sprites/courier.png +0 -0
- package/template-2d/assets/sprites/courier.png.ogt +7 -0
- package/template-2d/assets/strings/en.ogt +11 -0
- package/template-2d/assets/tiles/city.ogt +17 -0
- package/template-2d/assets/tiles/city.png +0 -0
- package/template-2d/assets/tiles/city.png.ogt +7 -0
- package/template-2d/gitattributes +22 -0
- package/template-2d/gitignore +8 -0
- package/template-2d/package.json +18 -0
- package/template-2d/project.ogt +21 -0
- package/template-2d/src/main.ts +305 -0
- package/template-2d/tests/smoke.og.ts +60 -0
- package/template-2d/tsconfig.json +17 -0
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# @origingame/create-game
|
|
2
2
|
|
|
3
|
-
Scaffolds an Origin Engine game from
|
|
4
|
-
|
|
3
|
+
Scaffolds an Origin Engine game from a project template of the engine
|
|
4
|
+
repository, the 3D template `templates/game` or the 2D starter
|
|
5
|
+
`templates/game-2d`: `project.ogt`, `src/`, `assets/`, `tests/`,
|
|
5
6
|
`package.json` and `tsconfig.json`, with the game's name and id filled in.
|
|
6
7
|
|
|
7
8
|
```sh
|
|
@@ -9,13 +10,20 @@ bun packages/create/bin/create.ts my-game --name "My Game" # in the engine rep
|
|
|
9
10
|
cd my-game && bun install && og cook && og run .
|
|
10
11
|
```
|
|
11
12
|
|
|
12
|
-
`create [dir] [--name "Name"] [--id slug] [--archetype name] [--premise "one line"] [--look slug] [--force]`: the
|
|
13
|
+
`create [dir] [--name "Name"] [--id slug] [--profile 2d|3d] [--archetype name] [--premise "one line"] [--look slug] [--force]`: the
|
|
13
14
|
id defaults to the name in lowercase-hyphen form (or the directory's name);
|
|
14
15
|
a directory that is not empty is refused unless `--force` is given.
|
|
16
|
+
`--profile 2d` writes the 2D starter (a courier's platformer: sprites,
|
|
17
|
+
flipbooks, a tilemap with one-way ledges, 2D physics, a HUD and a pause
|
|
18
|
+
menu, one playtest), `--profile 3d` the 3D template; without the flag, a
|
|
19
|
+
premise that says 2D, platformer, side-scroller, metroidvania, pixel art,
|
|
20
|
+
sprites or tiles without saying 3D gets the 2D starter, anything else the
|
|
21
|
+
3D template. The output's `Profile:` line says which and why.
|
|
15
22
|
`--archetype` starts from a complete game of `templates/archetypes/`
|
|
16
|
-
instead of
|
|
23
|
+
instead of either template: `runner`, `match3`, `topdown`, `tower-defense`,
|
|
17
24
|
`roguelike`, `idle`, `rhythm` or `third-person` (the published package carries them under
|
|
18
|
-
`archetypes/`)
|
|
25
|
+
`archetypes/`); an archetype has its own profile, so `--profile` beside it
|
|
26
|
+
is refused.
|
|
19
27
|
|
|
20
28
|
Every game gets its look: `assets/looks/<id>.ogt`, an `.ogt` of `type look`
|
|
21
29
|
(og-taste §12 of the knowledge package) that `GameDesc.look` in the entry
|
|
@@ -42,8 +50,8 @@ is, what it extends and how to change it. For an archetype, a preset moves
|
|
|
42
50
|
the frames its `references/` hold; create says so and how to record them
|
|
43
51
|
again.
|
|
44
52
|
|
|
45
|
-
The published package carries the template under `template
|
|
46
|
-
catalogue under `looks/`, copied by the `prepack` script
|
|
53
|
+
The published package carries the 3D template under `template/`, the 2D
|
|
54
|
+
starter under `template-2d/` and the style catalogue under `looks/`, copied by the `prepack` script
|
|
47
55
|
(`scripts/pack-template.ts`); build output (`cooked/`,
|
|
48
56
|
`dist/`, `node_modules/`) is never copied. The template's `gitattributes`
|
|
49
57
|
(media through Git LFS) is written as the project's `.gitattributes`: under
|
package/archetypes/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# Archetypes
|
|
2
2
|
|
|
3
3
|
Complete games a Maker starts from, each a project like `templates/game`
|
|
4
|
-
(`project.ogt`, `src/`, `assets/`,
|
|
5
|
-
`tsconfig.json`) that `og run`, `og test` and
|
|
4
|
+
and the 2D starter `templates/game-2d` (`project.ogt`, `src/`, `assets/`,
|
|
5
|
+
`tests/`, `package.json`, `tsconfig.json`) that `og run`, `og test` and
|
|
6
|
+
`og build` take as is; `npm create @origingame/game --archetype <name>`
|
|
7
|
+
writes one, with its own profile. They
|
|
6
8
|
rebuild the platform's six Godot Discover prototypes on Origin Engine,
|
|
7
9
|
keeping each prototype's premise and lessons and none of its code or art,
|
|
8
10
|
and add a top-down room built for the engine's 2D and game-feel modules
|
package/bin/create.ts
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
// `npm create @origingame/game` (and `bun create`): scaffolds the Origin
|
|
3
3
|
// Engine project template (templates/game) into a directory, naming the game.
|
|
4
4
|
//
|
|
5
|
-
// create [dir] [--name "Name"] [--id slug] [--archetype runner|match3|...]
|
|
5
|
+
// create [dir] [--name "Name"] [--id slug] [--profile 2d|3d] [--archetype runner|match3|...]
|
|
6
6
|
// [--premise "one line"] [--look <slug>] [--force]
|
|
7
7
|
//
|
|
8
|
-
// The template comes from ./template/
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
8
|
+
// The template comes from ./template/ (the 3D template, templates/game) or
|
|
9
|
+
// ./template-2d/ (the 2D starter, templates/game-2d) in the published
|
|
10
|
+
// package (prepack: scripts/pack-template.ts), or from ../../templates/ in
|
|
11
|
+
// the engine repository; an archetype (a complete small game to start from)
|
|
12
|
+
// from ./archetypes/<name>/ or ../../templates/archetypes/<name>/. The 2D
|
|
13
|
+
// starter is written for --profile 2d, or for a premise that says 2D
|
|
14
|
+
// (platformer, side-scroller, metroidvania, pixel art) without saying 3D.
|
|
12
15
|
//
|
|
13
16
|
// Every game gets its look, `assets/looks/<id>.ogt`, named by GameDesc.look
|
|
14
17
|
// in the entry: it extends the style catalogue's preset given by --look, or
|
|
@@ -28,9 +31,16 @@ export const SKIPPED = new Set(["cooked", "dist", "node_modules", "og-out", ".og
|
|
|
28
31
|
* Template files stored under another name and renamed in the scaffold: the
|
|
29
32
|
* template's Git attributes route media through LFS in a game's repository,
|
|
30
33
|
* but as `.gitattributes` they would do the same in the engine repository,
|
|
31
|
-
* which stores the template's media as plain blobs
|
|
34
|
+
* which stores the template's media as plain blobs; and `.gitignore`, which
|
|
35
|
+
* npm drops from every tarball, so the published package carries it as
|
|
36
|
+
* `gitignore` (scripts/pack-template.ts renames it at pack; the engine
|
|
37
|
+
* repository keeps the dotted name, and a copy taken from there scaffolds
|
|
38
|
+
* the same).
|
|
32
39
|
*/
|
|
33
|
-
export const RENAMED: ReadonlyMap<string, string> = new Map([
|
|
40
|
+
export const RENAMED: ReadonlyMap<string, string> = new Map([
|
|
41
|
+
["gitattributes", ".gitattributes"],
|
|
42
|
+
["gitignore", ".gitignore"],
|
|
43
|
+
]);
|
|
34
44
|
|
|
35
45
|
export interface CreateOptions {
|
|
36
46
|
readonly dir: string;
|
|
@@ -39,6 +49,8 @@ export interface CreateOptions {
|
|
|
39
49
|
readonly force?: boolean;
|
|
40
50
|
/** Start from an archetype (`runner`, `match3`, `topdown`, `tower-defense`, `roguelike`, `idle`, `rhythm`, `third-person`: templates/archetypes/README.md) instead of the project template. */
|
|
41
51
|
readonly archetype?: string;
|
|
52
|
+
/** The template's profile: `2d` writes the 2D starter (templates/game-2d), `3d` the 3D template (templates/game). Unset, a premise that says 2D picks `2d`; else `3d`. Not for an archetype, which has its own. */
|
|
53
|
+
readonly profile?: Profile;
|
|
42
54
|
/** The template directory; found next to this file when omitted. */
|
|
43
55
|
readonly template?: string;
|
|
44
56
|
/** A style catalogue slug the game's look extends (og-taste §12). */
|
|
@@ -61,11 +73,22 @@ export interface CreatedLook {
|
|
|
61
73
|
readonly chosen: "flag" | "premise" | "template" | "none";
|
|
62
74
|
}
|
|
63
75
|
|
|
76
|
+
/** A project's profile (`project.ogt`'s `profile` line): 2D or 3D. */
|
|
77
|
+
export type Profile = "2d" | "3d";
|
|
78
|
+
|
|
79
|
+
/** The profile the game was written in, and how it was chosen. */
|
|
80
|
+
export interface CreatedProfile {
|
|
81
|
+
readonly value: Profile;
|
|
82
|
+
/** `flag` for --profile, `premise` for a premise that says 2D, `archetype` for an archetype's own, `template` for the 3D template by default. */
|
|
83
|
+
readonly chosen: "flag" | "premise" | "archetype" | "template";
|
|
84
|
+
}
|
|
85
|
+
|
|
64
86
|
export interface Created {
|
|
65
87
|
readonly dir: string;
|
|
66
88
|
readonly name: string;
|
|
67
89
|
readonly id: string;
|
|
68
90
|
readonly files: readonly string[];
|
|
91
|
+
readonly profile: CreatedProfile;
|
|
69
92
|
readonly look: CreatedLook;
|
|
70
93
|
/** What the game's author should know, such as reference frames a look will move. */
|
|
71
94
|
readonly notes: readonly string[];
|
|
@@ -157,8 +180,30 @@ function title(id: string): string {
|
|
|
157
180
|
.join(" ");
|
|
158
181
|
}
|
|
159
182
|
|
|
160
|
-
/**
|
|
161
|
-
|
|
183
|
+
/** Words of a premise that say the game is 2D (platformer, side-scroller, metroidvania, pixel art, sprites, tiles). */
|
|
184
|
+
const TWO_D = new Set(["platformer", "platforming", "side-scroller", "side-scrolling", "sidescroller", "sidescrolling", "scroller", "metroidvania", "pixel", "pixel-art", "pixelart", "sprite", "tilemap", "tile", "8bit", "16bit", "1bit"]);
|
|
185
|
+
const THREE_D = new Set(["third-person", "first-person", "fps", "voxel", "lowpoly", "low-poly"]);
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The profile a premise asks for: `2d` when it uses a 2D word (TWO_D, or
|
|
189
|
+
* `2D` itself) and no 3D word (THREE_D, or `3D`), else null: the template's
|
|
190
|
+
* own, 3D, applies.
|
|
191
|
+
*/
|
|
192
|
+
export function premiseProfile(premise: string): Profile | null {
|
|
193
|
+
const asked = new Set(words(premise));
|
|
194
|
+
const says = (w: string) => new RegExp(`(^|[^a-z0-9])${w}($|[^a-z0-9])`, "i").test(premise);
|
|
195
|
+
if (says("3d") || [...THREE_D].some((w) => asked.has(w))) return null;
|
|
196
|
+
return says("2d") || [...TWO_D].some((w) => asked.has(w)) ? "2d" : null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** The `profile "2d"` line of a project manifest; `3d` when it has none. */
|
|
200
|
+
function profileOf(dir: string): Profile {
|
|
201
|
+
const m = /^profile[ \t]+"(2d|3d)"/m.exec(readFileSync(join(dir, "project.ogt"), "utf8"));
|
|
202
|
+
return m?.[1] === "2d" ? "2d" : "3d";
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** The template this copy of the package scaffolds: the archetype's, or the project template of a profile (3D unless `2d`). */
|
|
206
|
+
export function templateDir(archetype?: string, profile: Profile = "3d"): string {
|
|
162
207
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
163
208
|
if (archetype !== undefined) {
|
|
164
209
|
const roots = [join(here, "..", "archetypes"), join(here, "..", "..", "..", "templates", "archetypes")];
|
|
@@ -168,10 +213,12 @@ export function templateDir(archetype?: string): string {
|
|
|
168
213
|
const known = roots.filter(existsSync).flatMap((r) => readdirSync(r).filter((d) => existsSync(join(r, d, "project.ogt"))));
|
|
169
214
|
throw new Error(`no archetype '${archetype}': choose one of ${[...new Set(known)].sort().join(", ") || "(none shipped)"}`);
|
|
170
215
|
}
|
|
171
|
-
|
|
216
|
+
const packed = profile === "2d" ? "template-2d" : "template";
|
|
217
|
+
const source = profile === "2d" ? "game-2d" : "game";
|
|
218
|
+
for (const candidate of [join(here, "..", packed), join(here, "..", "..", "..", "templates", source)]) {
|
|
172
219
|
if (existsSync(join(candidate, "project.ogt"))) return candidate;
|
|
173
220
|
}
|
|
174
|
-
throw new Error(`no project template next to ${here}: expected
|
|
221
|
+
throw new Error(`no ${profile} project template next to ${here}: expected ../${packed} (published package) or ../../../templates/${source} (engine repository)`);
|
|
175
222
|
}
|
|
176
223
|
|
|
177
224
|
function ogtString(value: string): string {
|
|
@@ -315,8 +362,16 @@ export function create(options: CreateOptions): Created {
|
|
|
315
362
|
throw new Error(`${dir} is not empty; pick an empty directory, or pass --force to write the template over it`);
|
|
316
363
|
}
|
|
317
364
|
}
|
|
365
|
+
if (options.profile !== undefined && options.archetype !== undefined) {
|
|
366
|
+
throw new Error(`--profile and --archetype together: an archetype has its own profile (${options.archetype}); drop one`);
|
|
367
|
+
}
|
|
318
368
|
const choice = chooseStyle(options);
|
|
319
|
-
const
|
|
369
|
+
const asked = options.profile ?? (options.premise !== undefined ? premiseProfile(options.premise) : null);
|
|
370
|
+
const template = options.template ?? templateDir(options.archetype, asked ?? "3d");
|
|
371
|
+
const profile: CreatedProfile = {
|
|
372
|
+
value: profileOf(template),
|
|
373
|
+
chosen: options.archetype !== undefined ? "archetype" : options.profile !== undefined ? "flag" : asked !== null ? "premise" : "template",
|
|
374
|
+
};
|
|
320
375
|
const list = files(template);
|
|
321
376
|
const written: string[] = [];
|
|
322
377
|
mkdirSync(dir, { recursive: true });
|
|
@@ -335,7 +390,7 @@ export function create(options: CreateOptions): Created {
|
|
|
335
390
|
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
336
391
|
}
|
|
337
392
|
const { look, notes } = writeLook(dir, name, id, choice, written);
|
|
338
|
-
return { dir, name, id, files: written, look, notes };
|
|
393
|
+
return { dir, name, id, files: written, profile, look, notes };
|
|
339
394
|
}
|
|
340
395
|
|
|
341
396
|
/**
|
|
@@ -420,12 +475,13 @@ function writeLook(dir: string, name: string, id: string, choice: ReturnType<typ
|
|
|
420
475
|
return { look, notes };
|
|
421
476
|
}
|
|
422
477
|
|
|
423
|
-
const USAGE = `usage: create [dir] [--name "Name"] [--id slug] [--archetype name] [--premise "one line"] [--look slug] [--force]
|
|
478
|
+
const USAGE = `usage: create [dir] [--name "Name"] [--id slug] [--profile 2d|3d] [--archetype name] [--premise "one line"] [--look slug] [--force]
|
|
424
479
|
|
|
425
480
|
Scaffolds an Origin Engine game into dir (default: ./origin-game).
|
|
426
481
|
--name the game's display name (default: from the id or the directory)
|
|
427
482
|
--id the game's id, lowercase-hyphen (default: from the name)
|
|
428
|
-
--
|
|
483
|
+
--profile 2d writes the 2D starter (a platformer: sprites, tilemap, 2D physics), 3d the 3D template (default: 2d for a premise that says 2D, platformer, side-scroller, metroidvania or pixel art without saying 3D; else 3d)
|
|
484
|
+
--archetype start from a complete archetype game (runner, match3, topdown, tower-defense, roguelike, idle, rhythm, third-person) instead of the template; it has its own profile
|
|
429
485
|
--premise the game in one line (genre, mood, setting, audience): its look extends the style catalogue's best match (asked for on a terminal when neither this nor --look is given)
|
|
430
486
|
--look the style catalogue slug the look extends (og-taste §12), such as anime-cel-3d or 16bit-pixel
|
|
431
487
|
--no-prompt never ask for the premise
|
|
@@ -437,6 +493,7 @@ function main(argv: string[]): number {
|
|
|
437
493
|
let id: string | undefined;
|
|
438
494
|
let force = false;
|
|
439
495
|
let archetype: string | undefined;
|
|
496
|
+
let profile: Profile | undefined;
|
|
440
497
|
let premise: string | undefined;
|
|
441
498
|
let look: string | undefined;
|
|
442
499
|
let ask = true;
|
|
@@ -451,7 +508,11 @@ function main(argv: string[]): number {
|
|
|
451
508
|
else if (a === "--id") id = value();
|
|
452
509
|
else if (a === "--force") force = true;
|
|
453
510
|
else if (a === "--archetype") archetype = value();
|
|
454
|
-
else if (a === "--
|
|
511
|
+
else if (a === "--profile") {
|
|
512
|
+
const v = value();
|
|
513
|
+
if (v !== "2d" && v !== "3d") throw new Error(`--profile ${v}: use 2d or 3d. ${USAGE}`);
|
|
514
|
+
profile = v;
|
|
515
|
+
} else if (a === "--premise") premise = value();
|
|
455
516
|
else if (a === "--look") look = value();
|
|
456
517
|
else if (a === "--no-prompt") ask = false;
|
|
457
518
|
else if (a === "-h" || a === "--help") {
|
|
@@ -469,13 +530,16 @@ function main(argv: string[]): number {
|
|
|
469
530
|
...(name ? { name } : {}),
|
|
470
531
|
...(id ? { id } : {}),
|
|
471
532
|
...(archetype ? { archetype } : {}),
|
|
533
|
+
...(profile ? { profile } : {}),
|
|
472
534
|
...(premise ? { premise } : {}),
|
|
473
535
|
...(look ? { look } : {}),
|
|
474
536
|
force,
|
|
475
537
|
});
|
|
476
538
|
const shown = relative(process.cwd(), made.dir) || ".";
|
|
477
539
|
const style = made.look.extends ? `extends ${made.look.extends}${made.look.chosen === "premise" ? " (from the premise)" : made.look.chosen === "flag" ? " (--look)" : ""}` : "extends no preset";
|
|
540
|
+
const how = { flag: " (--profile)", premise: " (from the premise)", archetype: ` (the ${archetype} archetype's)`, template: "" }[made.profile.chosen];
|
|
478
541
|
console.log(`Created ${made.name} (${made.id}) in ${shown}: ${made.files.length} files.
|
|
542
|
+
Profile: ${made.profile.value}${how}.
|
|
479
543
|
Look: ${made.look.file}, ${style}; README.md says how to change it.${made.notes.map((n) => `\nNote: ${n}`).join("")}
|
|
480
544
|
|
|
481
545
|
Next:
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@origingame/create-game",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Scaffolds an Origin Engine game from the project template: npm create @origingame/game.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/
|
|
8
|
+
"url": "git+https://github.com/originai-game/origin-engine.git",
|
|
9
9
|
"directory": "packages/create"
|
|
10
10
|
},
|
|
11
|
-
"homepage": "https://github.com/
|
|
11
|
+
"homepage": "https://github.com/originai-game/origin-engine#readme",
|
|
12
12
|
"bugs": {
|
|
13
|
-
"url": "https://github.com/
|
|
13
|
+
"url": "https://github.com/originai-game/origin-engine/issues"
|
|
14
14
|
},
|
|
15
15
|
"publishConfig": {
|
|
16
16
|
"access": "public"
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"files": [
|
|
23
23
|
"bin",
|
|
24
24
|
"template",
|
|
25
|
+
"template-2d",
|
|
25
26
|
"archetypes",
|
|
26
27
|
"looks",
|
|
27
28
|
"README.md"
|
package/template/package.json
CHANGED
package/template/project.ogt
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Prepares this Game's cloud sandbox snapshot: Origin runs it once per
|
|
3
|
+
# snapshot build, in the checkout, without a terminal, for at most 20 minutes,
|
|
4
|
+
# and saves the result for new sandboxes of the Game. Keep it idempotent; it
|
|
5
|
+
# never receives credentials. Only ignored output is kept: node_modules/ and
|
|
6
|
+
# cooked/ (og cook is incremental, so a new sandbox starts cooked).
|
|
7
|
+
set -eu
|
|
8
|
+
if [ -f bun.lock ]; then bun install --frozen-lockfile; else bun install; fi
|
|
9
|
+
og="${ORIGIN_OG_BIN:-og}"
|
|
10
|
+
if ! command -v "$og" >/dev/null 2>&1; then
|
|
11
|
+
echo "no og on this executor class; the first og run cooks instead"
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
"$og" cook .
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Asset licences
|
|
2
|
+
|
|
3
|
+
Nothing in this project is downloaded: the art and sounds are the engine's
|
|
4
|
+
runner archetype's (`templates/archetypes/runner`, drawn and synthesised by
|
|
5
|
+
its `tools/make-assets.py`), and the rest is written by hand.
|
|
6
|
+
|
|
7
|
+
| File | Licence | Origin |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `assets/sprites/courier.png`, `assets/sprites/coin.png` and their `.ogt` | CC0 1.0 | self-made: the runner archetype's courier and coin |
|
|
10
|
+
| `assets/tiles/city.png` | CC0 1.0 | self-made: the runner archetype's city tileset |
|
|
11
|
+
| `assets/tiles/city.ogt`, `assets/maps/level.ogt` | CC0 1.0 | self-made, written by hand |
|
|
12
|
+
| `assets/audio/jump.wav`, `assets/audio/coin.wav` | CC0 1.0 | self-made: the runner archetype's synthesised tones |
|
|
13
|
+
| `assets/fonts/ui.ttf` | SIL Open Font License 1.1 (`assets/fonts/OFL.txt`) | Noto Sans SC Regular, subset to Latin-1 by the runner archetype from the project template's `assets/fonts/NotoSansSC-Regular-subset.ttf` |
|
|
14
|
+
| `assets/looks/*.ogt`, `assets/strings/*.ogt`, every other `.ogt` | CC0 1.0 | self-made, written by hand |
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# The 2D starter
|
|
2
|
+
|
|
3
|
+
What `npm create @origingame/game` writes for a 2D game (`--profile 2d`, or
|
|
4
|
+
a premise that says 2D, platformer, side-scroller, metroidvania or pixel
|
|
5
|
+
art): the smallest whole 2D Origin Engine project. A courier runs and jumps
|
|
6
|
+
the rooftops of one level, takes coins, jumps up through one-way ledges and
|
|
7
|
+
lands on them, loses a life in the pit and clears the level at the far
|
|
8
|
+
wall; Escape (or the HUD's Pause) opens the pause menu.
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
og run . # headless with the probe; og dev . reloads on save
|
|
12
|
+
og test . # tests/smoke.og.ts
|
|
13
|
+
og build . # the play page
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## What it demonstrates, and the skills it exercises
|
|
17
|
+
|
|
18
|
+
| Part | Where | Skill |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| The project in the 2D profile: an orthographic camera, sprites, 2D physics | `project.ogt`, `start` in `src/main.ts` | og-game §2, §8 |
|
|
21
|
+
| A hand-written tilemap over a tileset: a parallax skyline, roofs that collide, ledges on their own collider layer (`colliderLayers 2`) that the controller makes one-way | `assets/maps/level.ogt`, `assets/tiles/city.ogt` | og-game §8 (the grammar) |
|
|
22
|
+
| A flipbook sheet, its clips numbered in file order and checked against the code | `assets/sprites/courier.ogt`, `CLIP`, `checkClips` | og-game §8 |
|
|
23
|
+
| og.PlatformerController: run, jump (held for the full height), coyote time, the jump buffer, one-way ledges, Down to drop | `Control`, the hero in `start` | og-game §13; og-feel §12 |
|
|
24
|
+
| The map's objects: the spawn, the coins, the goal | `start`, `Pickups` | og-game §8 |
|
|
25
|
+
| Input on the press's tick: keys, a pad, the HUD's touch controls on a phone | `Control`, `Hud` | og-feel §1; og-ui §5 |
|
|
26
|
+
| The UI kit's platformer HUD and pause menu, every word from the string table | `Hud`, `assets/strings/en.ogt` | og-ui §1, §6; og-i18n §1 |
|
|
27
|
+
| A camera that follows with a dead zone and look-ahead | og.Follow2D on the hero | og-feel §8 |
|
|
28
|
+
| The frame's art direction as data: a look extending the 16bit-pixel preset | `assets/looks/origin-game-2d.ogt` (a created game's is `<id>.ogt`) | og-taste §1, §12 |
|
|
29
|
+
| A playtest that asserts on dumps, never on the code | `tests/smoke.og.ts` | og-build §3, §4 |
|
|
30
|
+
|
|
31
|
+
What a game made from it changes first: the art (the courier and the city
|
|
32
|
+
are the runner archetype's, `templates/archetypes/runner/tools/make-assets.py`
|
|
33
|
+
draws them), the level (`assets/maps/level.ogt`, or a Tiled `.tmj` beside
|
|
34
|
+
it, og-game §8), the controller's tunables (og-feel §12) and what the map's
|
|
35
|
+
objects mean. The 2D archetypes (`templates/archetypes/`) are whole games
|
|
36
|
+
to start from instead: a runner, a top-down room, match-3, a roguelike,
|
|
37
|
+
tower defence, an idle game and a rhythm game.
|
|
38
|
+
|
|
39
|
+
Asset licences: `LICENSES.md`.
|
|
Binary file
|
|
Binary file
|