summer-engine 1.1.0 → 1.2.1
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 +58 -24
- package/dist/bin/summer.js +4 -0
- package/dist/commands/doctor.d.ts +26 -0
- package/dist/commands/doctor.js +248 -0
- package/dist/commands/doctor.test.d.ts +1 -0
- package/dist/commands/doctor.test.js +29 -0
- package/dist/commands/mcp.js +49 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +128 -0
- package/dist/commands/skills.js +246 -107
- package/dist/lib/agent-config.d.ts +39 -0
- package/dist/lib/agent-config.js +284 -0
- package/dist/lib/agent-config.test.d.ts +1 -0
- package/dist/lib/agent-config.test.js +80 -0
- package/dist/lib/api-client.d.ts +19 -2
- package/dist/lib/api-client.js +129 -14
- package/dist/lib/skills-registry.d.ts +69 -0
- package/dist/lib/skills-registry.js +118 -0
- package/dist/mcp/server.js +2 -0
- package/dist/mcp/tools/asset-tools.js +14 -5
- package/dist/mcp/tools/generate-tools.d.ts +2 -0
- package/dist/mcp/tools/generate-tools.js +422 -0
- package/dist/mcp/tools/project-tools.js +100 -23
- package/dist/mcp/tools/with-engine.js +3 -53
- package/package.json +5 -2
- package/skills/asset-strategy/SKILL.md +235 -0
- package/skills/gdscript-patterns/SKILL.md +2 -2
- package/skills/make-game/SKILL.md +441 -0
- package/skills/scene-composition/SKILL.md +1 -1
|
@@ -1,18 +1,83 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { withEngine } from "./with-engine.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
function asRecord(value) {
|
|
4
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
5
|
+
? value
|
|
6
|
+
: null;
|
|
7
|
+
}
|
|
8
|
+
function stringFrom(value) {
|
|
9
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
10
|
+
}
|
|
11
|
+
function pickString(record, keys) {
|
|
12
|
+
if (!record)
|
|
7
13
|
return null;
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
for (const key of keys) {
|
|
15
|
+
const value = stringFrom(record[key]);
|
|
16
|
+
if (value)
|
|
17
|
+
return value;
|
|
12
18
|
}
|
|
13
|
-
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
function getProjectData(projectState) {
|
|
22
|
+
return asRecord(asRecord(projectState)?.data);
|
|
23
|
+
}
|
|
24
|
+
function getProjectSetting(projectState, keys) {
|
|
25
|
+
const data = getProjectData(projectState);
|
|
26
|
+
const entries = data?.entries;
|
|
27
|
+
if (!Array.isArray(entries))
|
|
14
28
|
return null;
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
const item = asRecord(entry);
|
|
31
|
+
const key = stringFrom(item?.key);
|
|
32
|
+
if (key && keys.includes(key)) {
|
|
33
|
+
return stringFrom(item?.value);
|
|
34
|
+
}
|
|
15
35
|
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function getProjectPath(projectState) {
|
|
39
|
+
const root = asRecord(projectState);
|
|
40
|
+
const data = getProjectData(projectState);
|
|
41
|
+
return (pickString(data, [
|
|
42
|
+
"projectPath",
|
|
43
|
+
"project_path",
|
|
44
|
+
"projectRoot",
|
|
45
|
+
"project_root",
|
|
46
|
+
"rootPath",
|
|
47
|
+
"root_path",
|
|
48
|
+
]) ??
|
|
49
|
+
pickString(root, ["projectPath", "project_path", "projectRoot", "project_root"]) ??
|
|
50
|
+
null);
|
|
51
|
+
}
|
|
52
|
+
function getProjectName(projectState) {
|
|
53
|
+
const root = asRecord(projectState);
|
|
54
|
+
const data = getProjectData(projectState);
|
|
55
|
+
return (pickString(data, ["projectName", "project_name", "name"]) ??
|
|
56
|
+
pickString(root, ["projectName", "project_name", "name"]) ??
|
|
57
|
+
getProjectSetting(projectState, ["application/config/name", "config/name"]));
|
|
58
|
+
}
|
|
59
|
+
function getMainScene(projectState) {
|
|
60
|
+
const root = asRecord(projectState);
|
|
61
|
+
const data = getProjectData(projectState);
|
|
62
|
+
return (pickString(data, ["mainScene", "main_scene", "mainScenePath", "main_scene_path"]) ??
|
|
63
|
+
pickString(root, ["mainScene", "main_scene", "mainScenePath", "main_scene_path"]) ??
|
|
64
|
+
getProjectSetting(projectState, [
|
|
65
|
+
"application/run/main_scene",
|
|
66
|
+
"run/main_scene",
|
|
67
|
+
]));
|
|
68
|
+
}
|
|
69
|
+
function getCurrentScene(projectState, sceneState) {
|
|
70
|
+
const sceneRoot = asRecord(sceneState);
|
|
71
|
+
const sceneData = asRecord(sceneRoot?.data);
|
|
72
|
+
const sceneProvenance = asRecord(sceneRoot?.provenance);
|
|
73
|
+
const projectRoot = asRecord(projectState);
|
|
74
|
+
const projectData = getProjectData(projectState);
|
|
75
|
+
const projectProvenance = asRecord(projectRoot?.provenance);
|
|
76
|
+
return (pickString(sceneProvenance, ["scenePath", "scene_path"]) ??
|
|
77
|
+
pickString(sceneData, ["scenePath", "scene_path", "currentScene", "current_scene"]) ??
|
|
78
|
+
pickString(projectData, ["currentScene", "current_scene", "scenePath", "scene_path"]) ??
|
|
79
|
+
pickString(projectProvenance, ["scenePath", "scene_path"]) ??
|
|
80
|
+
null);
|
|
16
81
|
}
|
|
17
82
|
export function registerProjectTools(server) {
|
|
18
83
|
server.tool("summer_get_agent_playbook", `AI-first operating guide for Summer Engine MCP.
|
|
@@ -25,7 +90,8 @@ It returns the safe workflow, anti-patterns, and recovery steps.`, {}, async ()
|
|
|
25
90
|
text: JSON.stringify({
|
|
26
91
|
startupChecklist: [
|
|
27
92
|
"Decide first: file edit or engine operation.",
|
|
28
|
-
"
|
|
93
|
+
"Write .gd/.cs/.json/docs/simple config with host file-edit tools directly.",
|
|
94
|
+
"Use Summer MCP for live scene state, inspector/node edits, imports, play/stop, diagnostics, screenshots/verification.",
|
|
29
95
|
"Call summer_get_project_context first.",
|
|
30
96
|
"If no scene is open, call summer_open_main_scene.",
|
|
31
97
|
"Call summer_get_scene_tree before structural edits.",
|
|
@@ -33,15 +99,16 @@ It returns the safe workflow, anti-patterns, and recovery steps.`, {}, async ()
|
|
|
33
99
|
],
|
|
34
100
|
safeDefaults: [
|
|
35
101
|
"Never guess scene filenames (main.tscn/Main.tscn).",
|
|
36
|
-
"Do not route ordinary
|
|
102
|
+
"Do not route ordinary script, JSON, docs, or simple config edits through MCP when direct file tools are available.",
|
|
37
103
|
"Never remove multiple top-level nodes unless user explicitly requests destructive edits.",
|
|
38
|
-
"
|
|
104
|
+
"Use MCP for scene-tree mutations and inspector-visible values instead of hand-editing live scene state.",
|
|
39
105
|
],
|
|
40
106
|
preferredFlow: [
|
|
41
107
|
"summer_get_project_context",
|
|
42
108
|
"summer_open_main_scene (if needed)",
|
|
43
109
|
"summer_get_scene_tree",
|
|
44
110
|
"edit via summer_add_node / summer_set_prop / summer_set_resource_property",
|
|
111
|
+
"write scripts/data/docs/simple config directly with host file-edit tools",
|
|
45
112
|
"summer_save_scene",
|
|
46
113
|
"summer_get_diagnostics",
|
|
47
114
|
],
|
|
@@ -56,35 +123,45 @@ It returns the safe workflow, anti-patterns, and recovery steps.`, {}, async ()
|
|
|
56
123
|
}));
|
|
57
124
|
server.tool("summer_get_project_context", `Get essential project context before editing. Returns:
|
|
58
125
|
- engine health/status
|
|
59
|
-
- project name and path
|
|
126
|
+
- project name and project path when exposed by project state
|
|
60
127
|
- current scene path (if available)
|
|
61
|
-
- main scene path from project
|
|
128
|
+
- main scene path from project settings
|
|
62
129
|
|
|
63
130
|
Use this first in every fresh chat to avoid guessing scene filenames or editing the wrong scene.`, {}, async () => withEngine(async (client) => {
|
|
64
|
-
const [health, projectState] = await Promise.all([
|
|
131
|
+
const [health, projectState, sceneState] = await Promise.all([
|
|
65
132
|
client.health(),
|
|
66
133
|
client.getProjectState(),
|
|
134
|
+
client.getSceneState().catch((err) => ({
|
|
135
|
+
ok: false,
|
|
136
|
+
error: err instanceof Error ? err.message : String(err),
|
|
137
|
+
})),
|
|
67
138
|
]);
|
|
68
|
-
const
|
|
69
|
-
const
|
|
139
|
+
const projectPath = getProjectPath(projectState);
|
|
140
|
+
const projectName = getProjectName(projectState);
|
|
141
|
+
const mainScene = getMainScene(projectState);
|
|
142
|
+
const currentScene = getCurrentScene(projectState, sceneState);
|
|
70
143
|
return {
|
|
71
144
|
health,
|
|
72
145
|
project: projectState,
|
|
146
|
+
scene: sceneState,
|
|
147
|
+
projectName,
|
|
148
|
+
projectPath,
|
|
149
|
+
currentScene,
|
|
73
150
|
mainScene,
|
|
74
151
|
guidance: mainScene
|
|
75
152
|
? "Use `summer_open_scene` with `mainScene` if no scene is open."
|
|
76
|
-
: "Main scene not found in project.
|
|
153
|
+
: "Main scene not found in project state. Open a known scene path explicitly.",
|
|
154
|
+
fileEditingGuidance: "Write .gd/.cs/.json/docs/simple config directly with host file tools. Use MCP for live scene state, inspector/node edits, imports, play/stop, diagnostics, and visual verification.",
|
|
77
155
|
};
|
|
78
156
|
}));
|
|
79
|
-
server.tool("summer_open_main_scene", `Open the project's configured main scene from project.
|
|
157
|
+
server.tool("summer_open_main_scene", `Open the project's configured main scene from project settings.
|
|
80
158
|
|
|
81
159
|
Safer than guessing scene names like main.tscn/Main.tscn.
|
|
82
160
|
Call this when you get "no scene open".`, {}, async () => withEngine(async (client) => {
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
const mainScene = await readMainSceneFromProject(projectPath);
|
|
161
|
+
const projectState = await client.getProjectState();
|
|
162
|
+
const mainScene = getMainScene(projectState);
|
|
86
163
|
if (!mainScene) {
|
|
87
|
-
throw new Error("Could not resolve application/run/main_scene from project.
|
|
164
|
+
throw new Error("Could not resolve application/run/main_scene from project state. Call `summer_get_project_context` and open a scene explicitly.");
|
|
88
165
|
}
|
|
89
166
|
return client.executeOps([{ op: "OpenScene", path: mainScene }]);
|
|
90
167
|
}));
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { getClient, resetClient } from "../server.js";
|
|
2
|
-
import { getAuthToken } from "../../lib/auth.js";
|
|
3
|
-
const GATEWAY_URL = process.env.SUMMER_GATEWAY_URL || "https://www.summerengine.com";
|
|
4
2
|
function extractOpError(result) {
|
|
5
3
|
if (!result || typeof result !== "object")
|
|
6
4
|
return null;
|
|
7
5
|
const op = result;
|
|
6
|
+
if (op.ok === false && op.error)
|
|
7
|
+
return op.error;
|
|
8
8
|
if (op.status === "error" && op.error)
|
|
9
9
|
return op.error;
|
|
10
10
|
const firstFailed = op.results?.find((r) => r.ok === false && r.error);
|
|
@@ -19,62 +19,12 @@ function buildActionHint(message) {
|
|
|
19
19
|
return "Scene path could not be opened. Call `summer_get_project_context` to get `mainScene`, then open that exact path. Avoid guessing scene filenames.";
|
|
20
20
|
}
|
|
21
21
|
if (normalized.includes("writefile cannot edit .tscn/.scn")) {
|
|
22
|
-
return "
|
|
22
|
+
return "Write .gd/.cs/.json/docs/simple config files with normal file-edit tools. Use Summer MCP for live scene state, inspector/node edits, imports, play/stop, diagnostics, and visual verification.";
|
|
23
23
|
}
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
|
-
async function checkMcpQuota() {
|
|
27
|
-
const token = await getAuthToken();
|
|
28
|
-
if (!token) {
|
|
29
|
-
return {
|
|
30
|
-
allowed: false,
|
|
31
|
-
message: "Not signed in. The user needs to run this in their terminal:\n npx summer-engine login\nOr open: https://www.summerengine.com/login\nMCP tools require a Summer Engine account (free to create).",
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
try {
|
|
35
|
-
const res = await fetch(`${GATEWAY_URL}/api/mcp/log-local-call`, {
|
|
36
|
-
method: "POST",
|
|
37
|
-
headers: {
|
|
38
|
-
Authorization: `Bearer ${token}`,
|
|
39
|
-
"Content-Type": "application/json",
|
|
40
|
-
},
|
|
41
|
-
body: JSON.stringify({}),
|
|
42
|
-
signal: AbortSignal.timeout(10000),
|
|
43
|
-
});
|
|
44
|
-
const data = (await res.json().catch(() => ({})));
|
|
45
|
-
// Hard enforcement errors should block tool execution.
|
|
46
|
-
if ([401, 402, 403, 429].includes(res.status)) {
|
|
47
|
-
return {
|
|
48
|
-
allowed: false,
|
|
49
|
-
message: data.message || "Quota check failed. Try again.",
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
// Soft-fail on service/gateway issues so local tools keep working.
|
|
53
|
-
if (!res.ok) {
|
|
54
|
-
return { allowed: true };
|
|
55
|
-
}
|
|
56
|
-
if (data.allowed === false) {
|
|
57
|
-
return {
|
|
58
|
-
allowed: false,
|
|
59
|
-
message: data.message || "MCP call limit reached. Upgrade at https://summerengine.com/pricing",
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
return { allowed: true };
|
|
63
|
-
}
|
|
64
|
-
catch (err) {
|
|
65
|
-
// Network/transient errors should not brick local MCP usage.
|
|
66
|
-
return { allowed: true };
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
26
|
export async function withEngine(fn) {
|
|
70
27
|
try {
|
|
71
|
-
const quota = await checkMcpQuota();
|
|
72
|
-
if (!quota.allowed) {
|
|
73
|
-
return {
|
|
74
|
-
content: [{ type: "text", text: quota.message || "Quota exceeded" }],
|
|
75
|
-
isError: true,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
28
|
const client = await getClient();
|
|
79
29
|
const result = await fn(client);
|
|
80
30
|
const opError = extractOpError(result);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "summer-engine",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "CLI and MCP tools for Summer Engine — the AI-native game engine",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"game-engine",
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
"build": "tsc",
|
|
32
32
|
"dev": "tsc --watch",
|
|
33
33
|
"summer": "node dist/bin/summer.js",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"test:watch": "vitest",
|
|
34
36
|
"prepublishOnly": "npm run build"
|
|
35
37
|
},
|
|
36
38
|
"engines": {
|
|
@@ -48,6 +50,7 @@
|
|
|
48
50
|
"devDependencies": {
|
|
49
51
|
"@types/diff": "^7.0.0",
|
|
50
52
|
"@types/node": "^20.0.0",
|
|
51
|
-
"typescript": "^5.5.0"
|
|
53
|
+
"typescript": "^5.5.0",
|
|
54
|
+
"vitest": "^3.2.4"
|
|
52
55
|
}
|
|
53
56
|
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: asset-strategy
|
|
3
|
+
description: Decision guide for how to create different game asset types. Use when deciding whether to generate 3D models, textures, or use primitives. Includes prompt templates for AI image-to-3D pipeline.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility:
|
|
6
|
+
- Cursor
|
|
7
|
+
- Claude Code
|
|
8
|
+
- Windsurf
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Asset Strategy for Summer Engine
|
|
12
|
+
|
|
13
|
+
How to create different types of game assets. Pick the right pipeline for each asset type.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Decision Tree
|
|
18
|
+
|
|
19
|
+
| Asset type | Pipeline | Why |
|
|
20
|
+
|-----------|----------|-----|
|
|
21
|
+
| Props (sword, barrel, chair, crate) | Image-to-3D | Best quality for isolated objects |
|
|
22
|
+
| Characters (player, NPC, enemy) | Image-to-3D (mannequin ref) | T-pose reference preserves proportions |
|
|
23
|
+
| Organic (trees, rocks, mushrooms) | Image-to-3D | Handles complex shapes well |
|
|
24
|
+
| Vehicles (car, spaceship, boat) | Image-to-3D | Good for hard-surface objects |
|
|
25
|
+
| Walls / floors / ceilings | Texture + BoxMesh/CSGBox | Tiled textures on simple geometry |
|
|
26
|
+
| Terrain / ground | Texture + PlaneMesh | Tileable ground textures |
|
|
27
|
+
| Buildings / structures | Texture + CSG or modular kit | Combine primitive shapes with textures |
|
|
28
|
+
| UI elements / icons / HUD | 2D image only | No 3D conversion needed |
|
|
29
|
+
| Sprites (2D games) | 2D image only | Direct use as Sprite2D texture |
|
|
30
|
+
| Skyboxes / backgrounds | Panoramic image gen | Environment, not mesh |
|
|
31
|
+
| Particles / VFX | Built in engine | GPUParticles3D, no generation |
|
|
32
|
+
| Audio / music / SFX | summer_generate_audio | Separate pipeline |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Pipeline 1: Image-to-3D (Props, Characters, Organic)
|
|
37
|
+
|
|
38
|
+
### How it works
|
|
39
|
+
|
|
40
|
+
1. A reference template image (mannequin on white bg) is stored server-side
|
|
41
|
+
2. nano-banana-2 img2img replaces the mannequin with the desired asset
|
|
42
|
+
3. The result is a clean, 3D-ready reference image
|
|
43
|
+
4. That image feeds into Hunyuan 3D (default) or Trellis 2 for 3D conversion
|
|
44
|
+
5. You get a 3D model back
|
|
45
|
+
|
|
46
|
+
Available 3D models:
|
|
47
|
+
- **hunyuan** (default) — Hunyuan 3D v3.1 Pro, highest quality
|
|
48
|
+
- **trellis** — Trellis 2, fast and detailed
|
|
49
|
+
- **meshy** — Legacy option
|
|
50
|
+
|
|
51
|
+
From the CLI, the reference image step is invisible. Just call:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
summer_generate_3d(prompt="a treasure chest with gold coins")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The server generates the reference image and feeds it to 3D automatically.
|
|
58
|
+
|
|
59
|
+
To pick a specific model:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
summer_generate_3d(prompt="a treasure chest", model="trellis")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
For manual control (you want to see/approve the reference image first):
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
summer_generate_image(prompt="a treasure chest with gold coins, game asset, white background, stylized, not ultra realistic")
|
|
69
|
+
# Review the image with Read tool
|
|
70
|
+
summer_generate_3d(kind="image-to-3d", imageUrl="<fileUrl from above>")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Prompt template for 3D reference images
|
|
74
|
+
|
|
75
|
+
When generating images intended for 3D conversion, use this suffix:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
[your asset description]. Game asset, centered in frame, 3D model render style,
|
|
79
|
+
stylized game art, not ultra realistic, clean white background, soft studio
|
|
80
|
+
lighting. Isolated object ready for 3D mesh generation.
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Examples:
|
|
84
|
+
|
|
85
|
+
**Weapon:**
|
|
86
|
+
```
|
|
87
|
+
A medieval broadsword with ornate crossguard and leather-wrapped grip. Game asset,
|
|
88
|
+
centered in frame, 3D model render style, stylized game art, not ultra realistic,
|
|
89
|
+
clean white background, soft studio lighting. Isolated object ready for 3D mesh
|
|
90
|
+
generation.
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Character:**
|
|
94
|
+
```
|
|
95
|
+
A fantasy knight in full plate armor, red cape, detailed pauldrons. T-pose,
|
|
96
|
+
front-facing. Game character asset, 3D model render style, stylized game art,
|
|
97
|
+
not ultra realistic, clean white background, soft studio lighting. Ready for
|
|
98
|
+
3D mesh generation.
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Organic prop:**
|
|
102
|
+
```
|
|
103
|
+
Cluster of bioluminescent mushrooms growing from a small mossy rock base. Pale
|
|
104
|
+
green glow, translucent caps, alien organic shapes. Game asset, centered in frame,
|
|
105
|
+
3D model render style, stylized game art, not ultra realistic, clean white
|
|
106
|
+
background, soft studio lighting. Isolated object ready for 3D mesh generation.
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Vehicle:**
|
|
110
|
+
```
|
|
111
|
+
A rusted sci-fi hover bike with exposed engine parts and worn paint. Game asset,
|
|
112
|
+
centered in frame, 3D model render style, stylized game art, not ultra realistic,
|
|
113
|
+
clean white background, soft studio lighting. Isolated vehicle ready for 3D mesh
|
|
114
|
+
generation.
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### What makes a BAD 3D reference
|
|
118
|
+
|
|
119
|
+
Avoid these in prompts:
|
|
120
|
+
- Scene context ("sword lying on a table") -- the table becomes part of the mesh
|
|
121
|
+
- Strong directional shadows -- baked into the mesh as geometry
|
|
122
|
+
- Ultra-realistic rendering -- too much detail for game-ready meshes
|
|
123
|
+
- Flat 2D angles (pure front/side view) -- 3D gen needs depth cues
|
|
124
|
+
- Multiple objects ("a sword and a shield") -- generates as one fused mesh
|
|
125
|
+
- Busy backgrounds -- bleeds into the model
|
|
126
|
+
|
|
127
|
+
### Polycount guidance
|
|
128
|
+
|
|
129
|
+
Pass in options when quality matters:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
summer_generate_3d(prompt="...", options={ target_polycount: 5000 })
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Rough targets:
|
|
136
|
+
- Mobile games: 1k-3k tris
|
|
137
|
+
- Indie/stylized PC: 3k-10k tris
|
|
138
|
+
- Detailed hero assets: 10k-30k tris
|
|
139
|
+
- Cinematics/showcase: 30k-100k tris
|
|
140
|
+
|
|
141
|
+
Default if not specified: provider decides (usually 10k-30k range).
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Pipeline 2: Textures + Geometry (Walls, Floors, Structures)
|
|
146
|
+
|
|
147
|
+
For flat surfaces and repeating structures, don't generate 3D models. Generate textures and apply them to simple geometry.
|
|
148
|
+
|
|
149
|
+
### Walls
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
summer_generate_image(prompt="seamless brick wall texture, tileable, game texture, diffuse map")
|
|
153
|
+
summer_import_from_url(url="<fileUrl>", path="res://assets/textures/brick_wall.png")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Then in the scene:
|
|
157
|
+
```
|
|
158
|
+
summer_add_node(parent="./Level", type="CSGBox3D", name="Wall")
|
|
159
|
+
summer_set_prop(path="./Level/Wall", key="size", value="Vector3(10, 3, 0.3)")
|
|
160
|
+
summer_set_prop(path="./Level/Wall", key="position", value="Vector3(0, 1.5, -5)")
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Attach material with texture via script or manual setup.
|
|
164
|
+
|
|
165
|
+
### Floors / ground
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
summer_generate_image(prompt="seamless grass ground texture, top-down, tileable, game texture")
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Apply to PlaneMesh or CSGBox3D scaled flat.
|
|
172
|
+
|
|
173
|
+
### Modular building kit
|
|
174
|
+
|
|
175
|
+
For buildings, combine CSG shapes:
|
|
176
|
+
- CSGBox3D for walls
|
|
177
|
+
- CSGBox3D for floors/ceilings
|
|
178
|
+
- CSGCylinder3D for pillars
|
|
179
|
+
- CSGPolygon3D for custom shapes
|
|
180
|
+
|
|
181
|
+
Generate different textures for each material:
|
|
182
|
+
- Wall texture (brick, stone, wood)
|
|
183
|
+
- Floor texture (tile, wood planks, concrete)
|
|
184
|
+
- Roof texture (shingles, metal)
|
|
185
|
+
|
|
186
|
+
### Texture prompt tips
|
|
187
|
+
|
|
188
|
+
For tileable textures, always include:
|
|
189
|
+
```
|
|
190
|
+
[material description], seamless, tileable, game texture, diffuse map,
|
|
191
|
+
top-down view, uniform lighting, no perspective
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Pipeline 3: 2D Only (UI, Sprites, Icons)
|
|
197
|
+
|
|
198
|
+
For 2D game assets and UI elements:
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
summer_generate_image(prompt="pixel art heart icon, 64x64, red, game UI element",
|
|
202
|
+
style="pixel")
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
For sprite sheets, consider the sprite animation tools (separate from this pipeline).
|
|
206
|
+
|
|
207
|
+
### UI prompt tips
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
[element description], flat design, clean edges, game UI style,
|
|
211
|
+
transparent background preferred, [size]px
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Pipeline 4: Audio
|
|
217
|
+
|
|
218
|
+
Separate from visual assets. See summer_generate_audio tool:
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
summer_generate_audio(capability="sound_effects", text="metal sword clash impact")
|
|
222
|
+
summer_generate_audio(capability="music", prompt="ambient fantasy forest theme", durationSeconds=60)
|
|
223
|
+
summer_generate_audio(capability="text_to_speech", text="You shall not pass!", voiceId="<id>")
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Edge Cases (add to this list as they come up)
|
|
229
|
+
|
|
230
|
+
- **Transparent/glass objects**: Add "translucent material, see-through" to prompt. 3D gen may struggle -- consider using engine materials instead.
|
|
231
|
+
- **Animated objects**: Generate static mesh first, animate in engine with AnimationPlayer.
|
|
232
|
+
- **Multi-part objects**: Generate each part separately, assemble in scene tree.
|
|
233
|
+
- **Extremely thin objects** (paper, cloth, flags): 3D gen produces thick meshes. Better to use MeshInstance3D with a plane + texture + shader.
|
|
234
|
+
- **Emissive/glowing objects**: Generate the mesh, add emissive material in engine. Don't rely on the glow being in the mesh.
|
|
235
|
+
- **LOD (Level of Detail)**: Generate one version. If perf needs LOD, use Godot's LOD system or generate a second low-poly version.
|
|
@@ -89,8 +89,8 @@ var siblings = get_parent().get_children()
|
|
|
89
89
|
|
|
90
90
|
For health systems, input handling, and state machines, see [reference.md](reference.md).
|
|
91
91
|
|
|
92
|
-
##
|
|
92
|
+
## Scene Integration
|
|
93
93
|
|
|
94
|
-
When the AI adds a
|
|
94
|
+
When the AI adds a node with Summer MCP tools and attaches a script, the script path is set via `summer_set_prop(path, "script", "res://path/to/script.gd")`. The script file must exist first. Create or edit `.gd` files with the host agent's normal file-editing tools, then use Summer MCP only to attach the script and connect scene signals.
|
|
95
95
|
|
|
96
96
|
For signal connections, the receiver must have the handler method. Create the script with the method stub before calling `summer_connect_signal`.
|