@bitmagic/cli 0.1.65-dev.24 → 0.1.65-dev.4
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 +18 -106
- package/dist/assets/materials.d.ts +0 -9
- package/dist/assets/materials.js +2 -10
- package/dist/assets/materials.js.map +1 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/forge.js +23 -39
- package/dist/commands/forge.js.map +1 -1
- package/dist/commands/generate.d.ts +13 -31
- package/dist/commands/generate.js +56 -48
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/trailer.js +1 -1
- package/dist/commands/trailer.js.map +1 -1
- package/dist/editor/animation-preview-page.js +1 -88
- package/dist/editor/animation-preview-page.js.map +1 -1
- package/dist/editor/asset-detail-panel.d.ts +2 -2
- package/dist/editor/asset-detail-panel.js +0 -21
- package/dist/editor/asset-detail-panel.js.map +1 -1
- package/dist/editor/asset-viewer.js +8 -31
- package/dist/editor/asset-viewer.js.map +1 -1
- package/dist/editor/shell-page.js +1 -63
- package/dist/editor/shell-page.js.map +1 -1
- package/dist/mobile/asset-hosts.d.ts +9 -7
- package/dist/mobile/asset-hosts.js +16 -7
- package/dist/mobile/asset-hosts.js.map +1 -1
- package/dist/scaffold/blender-level-skill-files.js +2 -3
- package/dist/scaffold/blender-level-skill-files.js.map +1 -1
- package/dist/scaffold/claude-settings.js +0 -4
- package/dist/scaffold/claude-settings.js.map +1 -1
- package/dist/scaffold/project-files.js +12 -18
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/scaffold/project.js +1 -4
- package/dist/scaffold/project.js.map +1 -1
- package/dist/scaffold/skill-files.d.ts +5 -32
- package/dist/scaffold/skill-files.js +63 -102
- package/dist/scaffold/skill-files.js.map +1 -1
- package/dist/telemetry/creator-prompt.d.ts +2 -18
- package/dist/telemetry/creator-prompt.js +3 -24
- package/dist/telemetry/creator-prompt.js.map +1 -1
- package/dist/telemetry/report.js +16 -31
- package/dist/telemetry/report.js.map +1 -1
- package/package.json +4 -4
- package/assets/skills/trailer-craft.md +0 -180
- package/dist/commands/character.d.ts +0 -18
- package/dist/commands/character.js +0 -268
- package/dist/commands/character.js.map +0 -1
- package/dist/forge/prune-store.d.ts +0 -46
- package/dist/forge/prune-store.js +0 -86
- package/dist/forge/prune-store.js.map +0 -1
- package/dist/forge/remote-store.d.ts +0 -36
- package/dist/forge/remote-store.js +0 -113
- package/dist/forge/remote-store.js.map +0 -1
- package/dist/generate/character-body.d.ts +0 -79
- package/dist/generate/character-body.js +0 -100
- package/dist/generate/character-body.js.map +0 -1
- package/dist/project/character-search.d.ts +0 -30
- package/dist/project/character-search.js +0 -82
- package/dist/project/character-search.js.map +0 -1
- package/dist/scaffold/asset-pipeline-skill-files.d.ts +0 -42
- package/dist/scaffold/asset-pipeline-skill-files.js +0 -207
- package/dist/scaffold/asset-pipeline-skill-files.js.map +0 -1
|
@@ -40,9 +40,8 @@ export declare function creatorPromptField(override?: unknown): {
|
|
|
40
40
|
/** `body` plus the creator's own words, when the flag was given and telemetry is on. */
|
|
41
41
|
export declare function withParent(body: Record<string, unknown>): Record<string, unknown>;
|
|
42
42
|
/**
|
|
43
|
-
* The creator's ask as an opaque, stable dedupe key — what
|
|
44
|
-
*
|
|
45
|
-
* words themselves in {@link creatorAskText}, for a channel that shows them and stores nothing.)
|
|
43
|
+
* The creator's ask as an opaque, stable dedupe key — what the command beacon sends so
|
|
44
|
+
* api-server can count "prompts this game was built with" without ever being told the text.
|
|
46
45
|
*
|
|
47
46
|
* The CLI is the ONLY producer of this value. api-server stores it and compares it to other
|
|
48
47
|
* values from this same function; it never recomputes one from text, so the two sides cannot
|
|
@@ -57,18 +56,3 @@ export declare function withParent(body: Record<string, unknown>): Record<string
|
|
|
57
56
|
* `--original-prompt` produces — the count is a floor, never an overcount.
|
|
58
57
|
*/
|
|
59
58
|
export declare function creatorAskDigest(override?: unknown): string | undefined;
|
|
60
|
-
/**
|
|
61
|
-
* The creator's ask as TEXT, bounded for a wire that has a body limit.
|
|
62
|
-
*
|
|
63
|
-
* The beacon carries this so the live-prompts Slack channel can show what an
|
|
64
|
-
* agent is being asked for on the commands that send no prompt anywhere else —
|
|
65
|
-
* `build`, `verify`, `dev`, `reload`, `publish` — which are most of what
|
|
66
|
-
* building a GDK project consists of. api-server posts it and never stores it:
|
|
67
|
-
* the count still runs on {@link creatorAskDigest}, and no prompt text reaches
|
|
68
|
-
* Postgres.
|
|
69
|
-
*
|
|
70
|
-
* Capped by the same rule the digest hashes under, so the two always describe
|
|
71
|
-
* the same words. `DO_NOT_TRACK` drops it upstream in `beginCommand`, like
|
|
72
|
-
* every other field here.
|
|
73
|
-
*/
|
|
74
|
-
export declare function creatorAskText(override?: unknown): string | undefined;
|
|
@@ -52,9 +52,8 @@ export function withParent(body) {
|
|
|
52
52
|
return 'creatorPrompt' in field ? { ...body, ...field } : body;
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
|
-
* The creator's ask as an opaque, stable dedupe key — what
|
|
56
|
-
*
|
|
57
|
-
* words themselves in {@link creatorAskText}, for a channel that shows them and stores nothing.)
|
|
55
|
+
* The creator's ask as an opaque, stable dedupe key — what the command beacon sends so
|
|
56
|
+
* api-server can count "prompts this game was built with" without ever being told the text.
|
|
58
57
|
*
|
|
59
58
|
* The CLI is the ONLY producer of this value. api-server stores it and compares it to other
|
|
60
59
|
* values from this same function; it never recomputes one from text, so the two sides cannot
|
|
@@ -69,29 +68,9 @@ export function withParent(body) {
|
|
|
69
68
|
* `--original-prompt` produces — the count is a floor, never an overcount.
|
|
70
69
|
*/
|
|
71
70
|
export function creatorAskDigest(override) {
|
|
72
|
-
const text = creatorAskText(override);
|
|
73
|
-
if (text === undefined)
|
|
74
|
-
return undefined;
|
|
75
|
-
return createHash('sha256').update(text).digest('hex').slice(0, 16);
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* The creator's ask as TEXT, bounded for a wire that has a body limit.
|
|
79
|
-
*
|
|
80
|
-
* The beacon carries this so the live-prompts Slack channel can show what an
|
|
81
|
-
* agent is being asked for on the commands that send no prompt anywhere else —
|
|
82
|
-
* `build`, `verify`, `dev`, `reload`, `publish` — which are most of what
|
|
83
|
-
* building a GDK project consists of. api-server posts it and never stores it:
|
|
84
|
-
* the count still runs on {@link creatorAskDigest}, and no prompt text reaches
|
|
85
|
-
* Postgres.
|
|
86
|
-
*
|
|
87
|
-
* Capped by the same rule the digest hashes under, so the two always describe
|
|
88
|
-
* the same words. `DO_NOT_TRACK` drops it upstream in `beginCommand`, like
|
|
89
|
-
* every other field here.
|
|
90
|
-
*/
|
|
91
|
-
export function creatorAskText(override) {
|
|
92
71
|
const field = creatorPromptField(override);
|
|
93
72
|
if (!('creatorPrompt' in field))
|
|
94
73
|
return undefined;
|
|
95
|
-
return field.creatorPrompt.slice(0, MAX_ASK_CHARS);
|
|
74
|
+
return createHash('sha256').update(field.creatorPrompt.slice(0, MAX_ASK_CHARS)).digest('hex').slice(0, 16);
|
|
96
75
|
}
|
|
97
76
|
//# sourceMappingURL=creator-prompt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"creator-prompt.js","sourceRoot":"","sources":["../../src/telemetry/creator-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,sGAAsG;AACtG,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,oFAAoF;UAC7F,iEAAiE;CAC7D,CAAC;AAEX;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAkB;IACnD,MAAM,KAAK,GAAG,QAAQ,IAAI,cAAc,EAAE,EAAE,aAAa,CAAC;IAC1D,oFAAoF;IACpF,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACrG,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,UAAU,CAAC,IAA6B;IACtD,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;IACnC,gGAAgG;IAChG,8FAA8F;IAC9F,OAAO,eAAe,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACjE,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"creator-prompt.js","sourceRoot":"","sources":["../../src/telemetry/creator-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,sGAAsG;AACtG,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,oFAAoF;UAC7F,iEAAiE;CAC7D,CAAC;AAEX;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAkB;IACnD,MAAM,KAAK,GAAG,QAAQ,IAAI,cAAc,EAAE,EAAE,aAAa,CAAC;IAC1D,oFAAoF;IACpF,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACrG,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,UAAU,CAAC,IAA6B;IACtD,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;IACnC,gGAAgG;IAChG,8FAA8F;IAC9F,OAAO,eAAe,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACjE,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAkB;IACjD,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,CAAC,CAAC,eAAe,IAAI,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAClD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC7G,CAAC"}
|
package/dist/telemetry/report.js
CHANGED
|
@@ -16,19 +16,11 @@
|
|
|
16
16
|
* bounded to {@link REPORT_TIMEOUT_MS} per request so a finished command is
|
|
17
17
|
* never held open by a sulking network (the same reasoning as
|
|
18
18
|
* update/check.ts). It never prints.
|
|
19
|
-
* 2.
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* rule
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* `creatorPrompt` is the one field that carries words, and it is not
|
|
26
|
-
* analytics: api-server posts it to an internal Slack channel and stores it
|
|
27
|
-
* nowhere — not GA4, not Postgres, not BigQuery. It is here because the
|
|
28
|
-
* commands that send prompt text to a route are the generators, and an
|
|
29
|
-
* agent building a GDK project spends most of its commands on the offline
|
|
30
|
-
* ones. `DO_NOT_TRACK` drops it with everything else. See
|
|
31
|
-
* {@link CommandProvenance}.
|
|
19
|
+
* 2. It sends categories, never messages: the command name, ok/failed, the
|
|
20
|
+
* exit code, a `CliError.code`, and a duration. A message can name a path;
|
|
21
|
+
* that does not belong in an analytics event. The two later additions keep
|
|
22
|
+
* the rule: the project's game id is an id we minted, and the creator's ask
|
|
23
|
+
* travels as an opaque DIGEST, never its words. See {@link CommandProvenance}.
|
|
32
24
|
* 3. It is authenticated with the creator's own token, so the server can key
|
|
33
25
|
* the event to the account and look up their real Pro state. No token, no
|
|
34
26
|
* report — an anonymous beacon would be a spammable endpoint for nothing.
|
|
@@ -40,7 +32,7 @@ import { resolveAuth0ClientId } from '../config/environments.js';
|
|
|
40
32
|
import { getAccessToken } from '../auth/session.js';
|
|
41
33
|
import { readProjectGameIdOrNull } from '../project/context.js';
|
|
42
34
|
import { apiServerHeaders } from './request-headers.js';
|
|
43
|
-
import { creatorAskDigest
|
|
35
|
+
import { creatorAskDigest } from './creator-prompt.js';
|
|
44
36
|
import { currentCommand, isTelemetryDisabled } from './command-context.js';
|
|
45
37
|
// Re-exported because this is where the opt-out is enforced for a report that is already
|
|
46
38
|
// under way; `beginCommand` consults the same predicate to skip collecting anything at all.
|
|
@@ -93,28 +85,21 @@ function defaultDeps() {
|
|
|
93
85
|
return { fetch: globalThis.fetch, env: process.env };
|
|
94
86
|
}
|
|
95
87
|
/**
|
|
96
|
-
* The game and the ask, when this command has either.
|
|
97
|
-
* api-server validates the body strictly, and an absent key is how "nothing to say" is
|
|
98
|
-
* everywhere else on this wire.
|
|
99
|
-
*
|
|
100
|
-
* Nothing is present unless the command declared `--original-prompt` AND the agent passed it —
|
|
101
|
-
* `creatorAskDigest` reads the value `beginCommand` captured, which is also where `DO_NOT_TRACK`
|
|
102
|
-
* already dropped it.
|
|
88
|
+
* The game and the ask, when this command has either. Both keys are omitted rather than sent as
|
|
89
|
+
* null: api-server validates the body strictly, and an absent key is how "nothing to say" is
|
|
90
|
+
* spelled everywhere else on this wire.
|
|
103
91
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
92
|
+
* The digest is only ever present when the command declared `--original-prompt` AND the agent
|
|
93
|
+
* passed it — `creatorAskDigest` reads the value `beginCommand` captured, which is also where
|
|
94
|
+
* `DO_NOT_TRACK` already dropped it. The pair is only sent together: a digest with no game
|
|
95
|
+
* cannot be counted, and a game with no digest is not an ask.
|
|
108
96
|
*/
|
|
109
97
|
function provenance(deps) {
|
|
110
|
-
const
|
|
111
|
-
if (
|
|
98
|
+
const askDigest = creatorAskDigest();
|
|
99
|
+
if (askDigest === undefined)
|
|
112
100
|
return {};
|
|
113
101
|
const gameId = readProjectGameIdOrNull(deps.cwd);
|
|
114
|
-
|
|
115
|
-
if (gameId === undefined || askDigest === undefined)
|
|
116
|
-
return { creatorPrompt };
|
|
117
|
-
return { creatorPrompt, gameId, askDigest };
|
|
102
|
+
return gameId === undefined ? {} : { gameId, askDigest };
|
|
118
103
|
}
|
|
119
104
|
/**
|
|
120
105
|
* Report the running command's `report` to api-server. Resolves when the report
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/telemetry/report.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/telemetry/report.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitmagic/cli",
|
|
3
|
-
"version": "0.1.65-dev.
|
|
3
|
+
"version": "0.1.65-dev.4",
|
|
4
4
|
"description": "Build Bitmagic games from your own agent tool",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": "Bitmagic Oy",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"playwright-core": "^1.59.1",
|
|
26
26
|
"tar": "^7.4.3",
|
|
27
27
|
"ws": "^8.21.3",
|
|
28
|
-
"@bitmagic/animation-forger": "0.1.20-dev.
|
|
29
|
-
"@bitmagic/
|
|
30
|
-
"@bitmagic/
|
|
28
|
+
"@bitmagic/animation-forger": "0.1.20-dev.4",
|
|
29
|
+
"@bitmagic/world-forger": "0.1.25-dev.4",
|
|
30
|
+
"@bitmagic/asset-core": "0.2.20-dev.4"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@eslint/js": "^9.38.0",
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
# Trailer craft — structure, pacing, and on-screen text
|
|
2
|
-
|
|
3
|
-
Read this before composing a cut or putting any text on screen. Compressed
|
|
4
|
-
from Derek Lieu's trailer-editing essays (trailer editor for Half-Life: Alyx,
|
|
5
|
-
Dead Cells, Spelunky 2) plus general trailer-typography guidance; sources at
|
|
6
|
-
the bottom.
|
|
7
|
-
|
|
8
|
-
## Ordering: Genre → Hook → Anchors → Content
|
|
9
|
-
|
|
10
|
-
- **Genre** in 1–2 shots: the viewer must know what kind of game this is
|
|
11
|
-
within the first seconds. For our games one gameplay shot usually does it.
|
|
12
|
-
- **Hook** — the thing only this game has — as soon as genre is established:
|
|
13
|
-
ideally inside the first 10–15 s, never later than the first third.
|
|
14
|
-
Showing generic-but-good content before the hook makes the game read as
|
|
15
|
-
"a lesser version of an old game".
|
|
16
|
-
- **Anchors** — the features people expect from the genre (for a racer:
|
|
17
|
-
overtakes, drifts, crashes, varied tracks) fill the middle.
|
|
18
|
-
- **Content** — breadth (levels, modes, vehicles) comes last, once the viewer
|
|
19
|
-
already cares.
|
|
20
|
-
|
|
21
|
-
## Timeline layout: constraints first, footage last
|
|
22
|
-
|
|
23
|
-
Lay the timeline out as a skeleton and only then hunt for footage — not the
|
|
24
|
-
other way round:
|
|
25
|
-
|
|
26
|
-
1. Write the section skeleton: **Cold open → Introduction → Escalation →
|
|
27
|
-
Climax → Breath → Button/End card**. Sections have different lengths and
|
|
28
|
-
energy; they are not equal slices.
|
|
29
|
-
2. Reserve the **end card** first: 2–3 s, ONE card (logo + tagline + CTA),
|
|
30
|
-
never a sequence of closing screens.
|
|
31
|
-
3. Place any **body title cards** next, as placeholders with their copy
|
|
32
|
-
written — each one names what the following footage must prove.
|
|
33
|
-
4. Only now fill footage, chosen to demonstrate whatever the neighbouring
|
|
34
|
-
card or section claims.
|
|
35
|
-
|
|
36
|
-
This turns "pick 15 great clips" (squishy, drifts toward sameness) into
|
|
37
|
-
"find the one clip that proves this beat" (constrained, self-diversifying).
|
|
38
|
-
|
|
39
|
-
## Pacing
|
|
40
|
-
|
|
41
|
-
- Sections need **ebb and flow**: build to a peak, then give a deliberate
|
|
42
|
-
breathing shot (~5 s, calmer, wider) before the next build or the finale.
|
|
43
|
-
The stops emphasize the fast; the fast emphasizes the slow.
|
|
44
|
-
- **Rule of three**: never more than three similar things in a row — three
|
|
45
|
-
crashes, three drift shots, three of anything. Viewers extrapolate after
|
|
46
|
-
two; the fourth is dead time.
|
|
47
|
-
- **Do not cut on every beat.** Snap cuts to the grid, but vary clip lengths
|
|
48
|
-
(2, 3, 4, 6 beats) and sometimes let a musical moment land on an in-clip
|
|
49
|
-
action (a landing, an impact) instead of a cut. Constant per-beat cutting
|
|
50
|
-
reads as a sizzle reel, and gets nauseating.
|
|
51
|
-
- Average shot length is a genre statement: ~1.3 s reads as AAA shooter,
|
|
52
|
-
~4 s reads as contemplative indie. An arcade racer sits around 1.5–3 s
|
|
53
|
-
with a few longer holds where one continuous action is the payoff.
|
|
54
|
-
- **More editing is not better editing.** A cut must clarify or escalate;
|
|
55
|
-
cutting fast to look "like a real trailer" hides the game. If a single
|
|
56
|
-
take shows the whole stunt, keep the take.
|
|
57
|
-
- Total length: under 90 s unless there's a reason; 30–45 s is a sweet spot
|
|
58
|
-
for social/store use.
|
|
59
|
-
|
|
60
|
-
## Variety — the five axes
|
|
61
|
-
|
|
62
|
-
Audit the cut on each axis; the labels list in the shots file is the tool
|
|
63
|
-
(read the labels top to bottom — repetition is visible in text before you
|
|
64
|
-
ever re-watch):
|
|
65
|
-
|
|
66
|
-
1. **Content** — different actions, not the same action in new places.
|
|
67
|
-
Escalate variants (jump → bigger jump) rather than repeating.
|
|
68
|
-
2. **Composition** — vary camera distance and framing; chase cam all the way
|
|
69
|
-
through is monotone. Watch eye-trace: the viewer's focus point should not
|
|
70
|
-
teleport across the frame on a cut.
|
|
71
|
-
3. **Energy** — intense vs. quiet moments; contrast matters more than speed.
|
|
72
|
-
4. **Pacing** — vary section tempo, not just shot tempo.
|
|
73
|
-
5. **Shot length** — vary beats-per-clip even inside a fast section.
|
|
74
|
-
|
|
75
|
-
## Where the shots come from
|
|
76
|
-
|
|
77
|
-
Played footage is the default and the bulk of any trailer: it is the only
|
|
78
|
-
thing that shows what the game DOES, and a trailer of pure scenery reads as a
|
|
79
|
-
tech demo however pretty it is. But two of the beats above are ones a chase
|
|
80
|
-
camera almost never hands you:
|
|
81
|
-
|
|
82
|
-
- the **cold open**, which wants the level to look composed and deliberate;
|
|
83
|
-
- the **breath**, which wants one calm wide shot with nothing happening.
|
|
84
|
-
|
|
85
|
-
Both are what an authored **camera fly-through** is for — a path through the
|
|
86
|
-
level with the game running underneath, so traffic still drives and animations
|
|
87
|
-
still play while the camera moves on rails. Recording one is cheap and nobody
|
|
88
|
-
has to play it, so a fly-through is worth having even when there is plenty of
|
|
89
|
-
gameplay: cut one or two of its shots into a gameplay trailer rather than
|
|
90
|
-
building a trailer out of it.
|
|
91
|
-
|
|
92
|
-
A fly-through alone is the right whole trailer in a narrow set of cases — a
|
|
93
|
-
world reveal, an architectural or exploration piece, a level that is the
|
|
94
|
-
feature — and whenever there is no one available to play. Otherwise it is a
|
|
95
|
-
source of shots, not a substitute for showing the game.
|
|
96
|
-
|
|
97
|
-
## On-screen text
|
|
98
|
-
|
|
99
|
-
When to use text at all: only when footage cannot say it. A card that
|
|
100
|
-
narrates what the next shot visibly shows is noise. Default for our
|
|
101
|
-
gameplay trailers: **end card only**; add body cards only when they carry
|
|
102
|
-
information the footage can't (game name early, a hook that isn't visual,
|
|
103
|
-
release date).
|
|
104
|
-
|
|
105
|
-
Writing the cards:
|
|
106
|
-
- Copy must sound like this game and no other. Exercise: describe the game
|
|
107
|
-
in three words; build cards from what's left after cutting everything
|
|
108
|
-
generic.
|
|
109
|
-
- ≤ 6 words per card. Evocative verbs beat feature lists ("Forge your own
|
|
110
|
-
path", not "Over 40 unique tracks").
|
|
111
|
-
- Banned: content quantities ("5 unique levels"), genre-descriptor stacks
|
|
112
|
-
("a 2D rogue-lite action adventure"), player-verb lists ("Race! Drift!
|
|
113
|
-
Win!"), and stock phrases ("In a world…", "Get ready…", "Experience…",
|
|
114
|
-
"…like never before").
|
|
115
|
-
|
|
116
|
-
Showing the cards:
|
|
117
|
-
- **Intercut, don't overlay**: a card gets its own beat between gameplay
|
|
118
|
-
shots (dimmed still or plain background). Never ask the viewer to read
|
|
119
|
-
while action plays — they do neither. Small persistent text (logo bug,
|
|
120
|
-
date) is the exception.
|
|
121
|
-
- Reading time: ~0.5–0.7 s per word, minimum 2.5 s on screen; end card
|
|
122
|
-
2–3 s (ours may run longer over a music outro).
|
|
123
|
-
- Cards ride the same beat grid as the cuts — a card is a clip.
|
|
124
|
-
- Typography must match the game's art quality and universe: one typeface
|
|
125
|
-
family for the whole trailer, generous letter-spacing for all-caps,
|
|
126
|
-
strong contrast against footage (our title raster already adds shadow),
|
|
127
|
-
subtle animation only (fade/scale-in) — a static default-font card reads
|
|
128
|
-
as a last-minute add-on.
|
|
129
|
-
|
|
130
|
-
## Structure template (30–45 s, gameplay-only, end card close)
|
|
131
|
-
|
|
132
|
-
| Section | ~share | What goes here |
|
|
133
|
-
|---|---|---|
|
|
134
|
-
| Cold open | 5–15% | The single most striking moment; genre obvious at a glance |
|
|
135
|
-
| Introduction | 15–20% | Clean, readable driving; establish look + one anchor |
|
|
136
|
-
| Escalation | 35–45% | Varied action, rising energy, hook prominent early in it |
|
|
137
|
-
| Climax | 15–20% | Densest, loudest stretch; biggest moments back to back |
|
|
138
|
-
| Breath | ~5% | One calmer, wider shot; lets the climax land |
|
|
139
|
-
| End card | 2–3 s+ | Logo, tagline, CTA — over the music's outro |
|
|
140
|
-
|
|
141
|
-
## Selection rules already paid for
|
|
142
|
-
|
|
143
|
-
These came out of review rounds on real cuts. They cost time to learn and
|
|
144
|
-
nothing to apply, so apply them before the first render, not after.
|
|
145
|
-
|
|
146
|
-
- **Never use slow motion.** On gameplay footage it reads as the game running
|
|
147
|
-
at a low framerate, not as drama. `speed` stays 1.0.
|
|
148
|
-
- **Numbers are not interest.** Event `severity` / `impactSpeed` ranks
|
|
149
|
-
car-stops-dead-against-a-wall highest — the worst possible clip. A crash
|
|
150
|
-
earns its slot only if things visibly break (`destruction` events, debris)
|
|
151
|
-
and motion continues through the hit.
|
|
152
|
-
- **Low speed can BE the drama.** Wheelspin recoveries throwing dirt or snow,
|
|
153
|
-
plowing back onto the track, being overtaken while struggling
|
|
154
|
-
(`position-change` with `gained: false`), `off-track` +
|
|
155
|
-
`hard-acceleration` clusters — these are among the best clips and score
|
|
156
|
-
near zero on any speed metric.
|
|
157
|
-
- **"Some shots of X" means a few.** The failure mode is 0-or-100: one
|
|
158
|
-
standing start is an opener, five is a broken trailer.
|
|
159
|
-
- **Quota check before rendering.** Read the shots list labels top to bottom.
|
|
160
|
-
Max one standing start; no more than three of any shot type in the whole
|
|
161
|
-
cut and never three similar in a row; vary track or biome, camera distance
|
|
162
|
-
and action type (the five axes above).
|
|
163
|
-
- **Menu and selection clips need `"ui": false`** — the HUD replay only knows
|
|
164
|
-
the gameplay HUD and will paint it over menus.
|
|
165
|
-
- **Verify on pixels.** Contact sheets and stills for every candidate, and
|
|
166
|
-
stills from the RENDERED file for anything you changed. The timeline knows
|
|
167
|
-
about events; it cannot see framing, occlusion or beauty.
|
|
168
|
-
- **Iterate cheaply, keep history.** Editing the shots and re-rendering is
|
|
169
|
-
minutes, and revisions auto-version — so render rather than debate, and
|
|
170
|
-
compare against the previous `-rN` before delivering.
|
|
171
|
-
|
|
172
|
-
## Sources
|
|
173
|
-
|
|
174
|
-
- [How to Hook the Audience and How Quickly to Do it](https://www.derek-lieu.com/blog/2022/10/24/how-to-hook-the-audience-and-how-quickly-to-do-it)
|
|
175
|
-
- [Step Two of Starting a Game Trailer Timeline](https://www.derek-lieu.com/blog/2023/2/19/step-two-of-starting-a-game-trailer-timeline)
|
|
176
|
-
- [How to Think About Pacing in a Trailer](https://www.derek-lieu.com/blog/2020/11/16/how-to-think-about-pacing-in-a-trailer)
|
|
177
|
-
- [Variety is the Spice of Trailers](https://www.derek-lieu.com/blog/2019/3/27/variety-is-the-spice-of-trailers)
|
|
178
|
-
- [More Trailer Editing is not Better Trailer Editing](https://www.derek-lieu.com/blog/2023/4/23/more-trailer-editing-is-not-better-trailer-editing)
|
|
179
|
-
- [How to Write Trailer Title Cards With Strong Messaging](https://www.derek-lieu.com/blog/2020/4/19/how-to-write-trailer-title-cards-with-strong-messaging)
|
|
180
|
-
- [10 Common Indie Game Trailer Mistakes & How to Fix Them](https://www.derek-lieu.com/blog/2020/9/14/10-common-indie-game-trailer-mistakes-and-how-to-fix-them)
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { type CharacterSearchRecord } from '../project/character-search.js';
|
|
2
|
-
import { type CharacterArgs } from '../generate/character-body.js';
|
|
3
|
-
/**
|
|
4
|
-
* What `bitmagic character add` will ask for, from its flags and the cached search.
|
|
5
|
-
*
|
|
6
|
-
* Pure, and separated from the command so the branch that matters — which of the
|
|
7
|
-
* two forms was asked for, and whether the cache can answer it — is testable
|
|
8
|
-
* without a project, a token or a network. It returns the character args rather
|
|
9
|
-
* than a body so the shared `buildCharacterBody` still owns the wire shape.
|
|
10
|
-
*/
|
|
11
|
-
export declare function resolveAddRequest(args: {
|
|
12
|
-
name: string;
|
|
13
|
-
id?: string;
|
|
14
|
-
prompt?: string;
|
|
15
|
-
applyToPlayer?: boolean;
|
|
16
|
-
record: CharacterSearchRecord | null;
|
|
17
|
-
}): CharacterArgs;
|
|
18
|
-
export declare const characterCommand: import("citty").CommandDef<import("citty").ArgsDef>;
|