@coze-arch/cli 0.0.35 → 0.0.36-alpha.1dcb49

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.
Files changed (54) hide show
  1. package/lib/__templates__/phaser/.coze +13 -0
  2. package/lib/__templates__/phaser/README.md +37 -0
  3. package/lib/__templates__/phaser/_gitignore +22 -0
  4. package/lib/__templates__/phaser/_npmrc +14 -0
  5. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0001.png +0 -0
  6. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0002.png +0 -0
  7. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0003.png +0 -0
  8. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0004.png +0 -0
  9. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0005.png +0 -0
  10. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0006.png +0 -0
  11. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0007.png +0 -0
  12. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0008.png +0 -0
  13. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0009.png +0 -0
  14. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0010.png +0 -0
  15. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0011.png +0 -0
  16. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0012.png +0 -0
  17. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0013.png +0 -0
  18. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0014.png +0 -0
  19. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0015.png +0 -0
  20. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0016.png +0 -0
  21. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0017.png +0 -0
  22. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0018.png +0 -0
  23. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0019.png +0 -0
  24. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0020.png +0 -0
  25. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0021.png +0 -0
  26. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0022.png +0 -0
  27. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0023.png +0 -0
  28. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0024.png +0 -0
  29. package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/manifest.json +11 -0
  30. package/lib/__templates__/phaser/debug-runtime/flow-graph.json +20 -0
  31. package/lib/__templates__/phaser/index.html +12 -0
  32. package/lib/__templates__/phaser/package.json +31 -0
  33. package/lib/__templates__/phaser/pnpm-lock.yaml +661 -0
  34. package/lib/__templates__/phaser/scripts/build.sh +8 -0
  35. package/lib/__templates__/phaser/scripts/dev.sh +11 -0
  36. package/lib/__templates__/phaser/scripts/prepare.sh +7 -0
  37. package/lib/__templates__/phaser/scripts/start.sh +11 -0
  38. package/lib/__templates__/phaser/scripts/validate.sh +7 -0
  39. package/lib/__templates__/phaser/src/assets.ts +15 -0
  40. package/lib/__templates__/phaser/src/global.d.ts +36 -0
  41. package/lib/__templates__/phaser/src/main.ts +28 -0
  42. package/lib/__templates__/phaser/src/scene/BootScene.ts +209 -0
  43. package/lib/__templates__/phaser/src/scene/DevLoadingScene.ts +66 -0
  44. package/lib/__templates__/phaser/src/style.css +28 -0
  45. package/lib/__templates__/phaser/src/types.ts +32 -0
  46. package/lib/__templates__/phaser/src/utils/asset-loader.ts +319 -0
  47. package/lib/__templates__/phaser/src/utils/index.ts +2 -0
  48. package/lib/__templates__/phaser/src/utils/mark-editable.ts +6 -0
  49. package/lib/__templates__/phaser/template.config.js +43 -0
  50. package/lib/__templates__/phaser/tsconfig.json +23 -0
  51. package/lib/__templates__/phaser/vite.config.ts +120 -0
  52. package/lib/__templates__/templates.json +25 -0
  53. package/lib/cli.js +1 -1
  54. package/package.json +1 -1
@@ -0,0 +1,15 @@
1
+ export const IMAGES: Record<string, string> = {};
2
+
3
+ export const IMAGE_SEQUENCES: Record<string, string> = {
4
+ 'coze-game-loading': 'assets/image_sequence/coze-game-loading',
5
+ };
6
+
7
+ export const VIDEOS: Record<string, string> = {};
8
+
9
+ export const AUDIOS: Record<string, string> = {};
10
+
11
+ // 注册素材到 PhaserBridge,可以在扣子网页游戏开发中定位
12
+ window.PhaserBridge?.markImageAssetMap?.(IMAGES);
13
+ window.PhaserBridge?.markImageSequenceAssetMap?.(IMAGE_SEQUENCES);
14
+ window.PhaserBridge?.markVideoAssetMap?.(VIDEOS);
15
+ window.PhaserBridge?.markAudioAssetMap?.(AUDIOS);
@@ -0,0 +1,36 @@
1
+ import type Phaser from 'phaser';
2
+
3
+ declare global {
4
+ interface LaunchSpec {
5
+ protocol_version: 1;
6
+ target: {
7
+ graph_id: string;
8
+ node_id: string;
9
+ };
10
+ input: Record<string, any>;
11
+ }
12
+
13
+ interface PhaserBridgeBinding {
14
+ launchGame: (launchSpec: LaunchSpec | undefined) => Phaser.Game;
15
+ }
16
+
17
+ interface PhaserBridgeApi {
18
+ bind: (binding: PhaserBridgeBinding) => Phaser.Game;
19
+ markEditable?: <T extends Phaser.GameObjects.GameObject>(
20
+ stableId: string,
21
+ gameObject: T,
22
+ ) => T;
23
+ markImageAssetMap?: (images: Record<string, string>) => void;
24
+ markImageSequenceAssetMap?: (
25
+ imageSequences: Record<string, string>,
26
+ ) => void;
27
+ markVideoAssetMap?: (videos: Record<string, string>) => void;
28
+ markAudioAssetMap?: (audios: Record<string, string>) => void;
29
+ }
30
+
31
+ interface Window {
32
+ PhaserBridge?: PhaserBridgeApi;
33
+ }
34
+ }
35
+
36
+ export {};
@@ -0,0 +1,28 @@
1
+ import Phaser from 'phaser';
2
+
3
+ import { BootScene } from './scene/BootScene';
4
+ import { DevLoadingScene } from './scene/DevLoadingScene';
5
+ import './style.css';
6
+
7
+ const createGame = (_launchSpec?: LaunchSpec): Phaser.Game =>
8
+ new Phaser.Game({
9
+ type: Phaser.AUTO,
10
+ pixelArt: true,
11
+ parent: 'game',
12
+ // TODO 根据游戏的要求进行尺寸
13
+ width: 540,
14
+ height: 960,
15
+ backgroundColor: '#ffffff',
16
+ scale: {
17
+ mode: Phaser.Scale.FIT,
18
+ autoCenter: Phaser.Scale.CENTER_BOTH,
19
+ },
20
+ scene: [BootScene, DevLoadingScene],
21
+ });
22
+
23
+ // 注册游戏到 PhaserBridge,可以在扣子网页游戏开发中编辑评论
24
+ if (window.PhaserBridge) {
25
+ window.PhaserBridge.bind({ launchGame: createGame });
26
+ } else {
27
+ createGame();
28
+ }
@@ -0,0 +1,209 @@
1
+ import Phaser from 'phaser';
2
+
3
+ import {
4
+ getAssetLabelFromFileKey,
5
+ getImageSequenceRuntimeKeys,
6
+ loadAssets,
7
+ markEditable,
8
+ registerImageSequenceAnimations,
9
+ } from '@/utils';
10
+
11
+ import { AUDIOS, IMAGES, IMAGE_SEQUENCES, VIDEOS } from '../assets';
12
+
13
+ interface LoaderFileLike {
14
+ key: string;
15
+ }
16
+
17
+ const LOADING_TEXT_STYLE: Phaser.Types.GameObjects.Text.TextStyle = {
18
+ color: '#71717a',
19
+ fontFamily: '"PingFang SC", "Microsoft YaHei", sans-serif',
20
+ fontSize: '18px',
21
+ };
22
+
23
+ const DEV_LOADING_ASSET_KEY = 'coze-game-loading';
24
+ const devLoadingSource = IMAGE_SEQUENCES[DEV_LOADING_ASSET_KEY];
25
+
26
+ if (!devLoadingSource) {
27
+ throw new Error(`Image sequence asset not found: ${DEV_LOADING_ASSET_KEY}`);
28
+ }
29
+
30
+ const BOOT_IMAGE_SEQUENCES = {
31
+ [DEV_LOADING_ASSET_KEY]: devLoadingSource,
32
+ };
33
+
34
+ const BOOT_ASSETS = {
35
+ imageSequences: BOOT_IMAGE_SEQUENCES,
36
+ };
37
+
38
+ const GAME_IMAGE_SEQUENCES = Object.fromEntries(
39
+ Object.entries(IMAGE_SEQUENCES).filter(
40
+ ([assetKey]) => assetKey !== DEV_LOADING_ASSET_KEY,
41
+ ),
42
+ );
43
+
44
+ const GAME_ASSETS = {
45
+ images: IMAGES,
46
+ imageSequences: GAME_IMAGE_SEQUENCES,
47
+ videos: VIDEOS,
48
+ audios: AUDIOS,
49
+ };
50
+
51
+ export const DEV_LOADING_RUNTIME_KEYS =
52
+ getImageSequenceRuntimeKeys(DEV_LOADING_ASSET_KEY);
53
+
54
+ export class BootScene extends Phaser.Scene {
55
+ private loadingText?: Phaser.GameObjects.Text;
56
+ private progressBar?: Phaser.GameObjects.Graphics;
57
+ private progressText?: Phaser.GameObjects.Text;
58
+ private loadFailed = false;
59
+
60
+ constructor() {
61
+ super('boot');
62
+ }
63
+
64
+ preload(): void {
65
+ loadAssets(this, BOOT_ASSETS);
66
+ }
67
+
68
+ create(): void {
69
+ registerImageSequenceAnimations(this, BOOT_IMAGE_SEQUENCES);
70
+ this.createLoadingView();
71
+
72
+ const queuedFileCount = loadAssets(this, GAME_ASSETS);
73
+ if (queuedFileCount === 0) {
74
+ this.finishLoading();
75
+ return;
76
+ }
77
+
78
+ this.load.on(Phaser.Loader.Events.FILE_PROGRESS, this.onGameFile, this);
79
+ this.load.on(Phaser.Loader.Events.PROGRESS, this.onProgress, this);
80
+ this.load.once(
81
+ Phaser.Loader.Events.FILE_LOAD_ERROR,
82
+ this.onLoadError,
83
+ this,
84
+ );
85
+ this.load.once(Phaser.Loader.Events.COMPLETE, this.finishLoading, this);
86
+ this.load.start();
87
+ }
88
+
89
+ private createLoadingView(): void {
90
+ const { centerX, centerY } = this.cameras.main;
91
+
92
+ markEditable(
93
+ 'boot.loading-mascot',
94
+ this.add
95
+ .sprite(centerX, centerY - 58, DEV_LOADING_RUNTIME_KEYS.texture)
96
+ .setScale(0.56)
97
+ .play(DEV_LOADING_RUNTIME_KEYS.animation),
98
+ );
99
+
100
+ this.loadingText = markEditable(
101
+ 'boot.loading-text',
102
+ this.add
103
+ .text(
104
+ centerX,
105
+ centerY + 120,
106
+ '正在加载游戏资源…',
107
+ LOADING_TEXT_STYLE,
108
+ )
109
+ .setOrigin(0.5),
110
+ );
111
+
112
+ this.progressBar = markEditable(
113
+ 'boot.loading-progress-bar',
114
+ this.add.graphics(),
115
+ );
116
+ this.renderProgressBar(0);
117
+
118
+ this.progressText = markEditable(
119
+ 'boot.loading-progress',
120
+ this.add
121
+ .text(centerX, centerY + 190, '0%', LOADING_TEXT_STYLE)
122
+ .setOrigin(0.5),
123
+ );
124
+ }
125
+
126
+ private readonly onGameFile = (file: LoaderFileLike): void => {
127
+ const assetLabel = getAssetLabelFromFileKey(file.key, GAME_ASSETS);
128
+ this.loadingText?.setText(`正在加载${assetLabel}`);
129
+ };
130
+
131
+ private readonly onProgress = (progress: number): void => {
132
+ this.renderProgressBar(progress);
133
+ this.progressText?.setText(`${Math.round(progress * 100)}%`);
134
+ };
135
+
136
+ private readonly onLoadError = (file: LoaderFileLike): void => {
137
+ this.loadFailed = true;
138
+ const assetLabel = getAssetLabelFromFileKey(file.key, GAME_ASSETS);
139
+ this.loadingText?.setText(`${assetLabel}加载失败`);
140
+ };
141
+
142
+ private renderProgressBar(progress: number): void {
143
+ if (!this.progressBar) {
144
+ return;
145
+ }
146
+
147
+ const { centerX, centerY } = this.cameras.main;
148
+ const width = 300;
149
+ const height = 14;
150
+ const x = centerX - width / 2;
151
+ const y = centerY + 154;
152
+ const padding = 3;
153
+ const innerWidth = width - padding * 2;
154
+ const filledWidth = Phaser.Math.Clamp(progress, 0, 1) * innerWidth;
155
+
156
+ this.progressBar.clear();
157
+ this.progressBar.fillStyle(0x102536, 0.12);
158
+ this.progressBar.fillRoundedRect(x, y, width, height, height / 2);
159
+ this.progressBar.lineStyle(2, 0x102536, 0.28);
160
+ this.progressBar.strokeRoundedRect(x, y, width, height, height / 2);
161
+
162
+ if (filledWidth <= 0) {
163
+ return;
164
+ }
165
+
166
+ this.progressBar.fillStyle(0xef648d, 1);
167
+ this.progressBar.fillRoundedRect(
168
+ x + padding,
169
+ y + padding,
170
+ filledWidth,
171
+ height - padding * 2,
172
+ (height - padding * 2) / 2,
173
+ );
174
+
175
+ this.progressBar.fillStyle(0x165ca8, 1);
176
+ this.progressBar.fillCircle(
177
+ Phaser.Math.Clamp(
178
+ x + padding + filledWidth,
179
+ x + padding + 5,
180
+ x + width - padding - 5,
181
+ ),
182
+ y + height / 2,
183
+ 5,
184
+ );
185
+ }
186
+
187
+ private readonly finishLoading = (): void => {
188
+ this.removeLoaderListeners();
189
+
190
+ if (this.loadFailed) {
191
+ this.progressText?.setText('请刷新页面重试');
192
+ return;
193
+ }
194
+
195
+ registerImageSequenceAnimations(this, GAME_IMAGE_SEQUENCES);
196
+ this.scene.start('dev-loading');
197
+ };
198
+
199
+ private removeLoaderListeners(): void {
200
+ this.load.off(Phaser.Loader.Events.FILE_PROGRESS, this.onGameFile, this);
201
+ this.load.off(Phaser.Loader.Events.PROGRESS, this.onProgress, this);
202
+ this.load.off(
203
+ Phaser.Loader.Events.FILE_LOAD_ERROR,
204
+ this.onLoadError,
205
+ this,
206
+ );
207
+ this.load.off(Phaser.Loader.Events.COMPLETE, this.finishLoading, this);
208
+ }
209
+ }
@@ -0,0 +1,66 @@
1
+ import Phaser from 'phaser';
2
+
3
+ import { markEditable } from '@/utils';
4
+
5
+ import { DEV_LOADING_RUNTIME_KEYS } from './BootScene';
6
+
7
+ /**
8
+ * 本场景用于为兜底场景展示
9
+ */
10
+ export class DevLoadingScene extends Phaser.Scene {
11
+ constructor() {
12
+ super('dev-loading');
13
+ }
14
+
15
+ create(): void {
16
+ const { centerX, centerY } = this.cameras.main;
17
+
18
+ markEditable(
19
+ 'dev-loading.mascot',
20
+ this.add
21
+ .sprite(centerX, centerY - 82, DEV_LOADING_RUNTIME_KEYS.texture)
22
+ .setScale(0.64)
23
+ .play(DEV_LOADING_RUNTIME_KEYS.animation),
24
+ );
25
+
26
+ markEditable(
27
+ 'dev-loading.title',
28
+ this.add
29
+ .text(centerX, centerY + 92, '游戏开发中', {
30
+ color: '#18181b',
31
+ fontFamily: '"PingFang SC", "Microsoft YaHei", sans-serif',
32
+ fontSize: '32px',
33
+ fontStyle: 'bold',
34
+ })
35
+ .setOrigin(0.5),
36
+ );
37
+
38
+ markEditable(
39
+ 'dev-loading.description',
40
+ this.add
41
+ .text(centerX, centerY + 138, '请稍后,游戏世界即将呈现', {
42
+ color: '#71717a',
43
+ fontFamily: '"PingFang SC", "Microsoft YaHei", sans-serif',
44
+ fontSize: '18px',
45
+ })
46
+ .setOrigin(0.5),
47
+ );
48
+
49
+ const loadingDots = Array.from({ length: 3 }, (_value, index) =>
50
+ this.add.circle(centerX + (index - 1) * 18, centerY + 178, 4, 0xf43f75),
51
+ );
52
+
53
+ loadingDots.forEach((dot, index) => {
54
+ this.tweens.add({
55
+ targets: dot,
56
+ alpha: 0.25,
57
+ scale: 0.7,
58
+ duration: 520,
59
+ delay: index * 160,
60
+ ease: 'Sine.inOut',
61
+ repeat: -1,
62
+ yoyo: true,
63
+ });
64
+ });
65
+ }
66
+ }
@@ -0,0 +1,28 @@
1
+ :root {
2
+ color-scheme: dark;
3
+ font-family: Arial, sans-serif;
4
+ background: #000;
5
+ }
6
+
7
+ * {
8
+ box-sizing: border-box;
9
+ }
10
+
11
+ html,
12
+ body,
13
+ #game {
14
+ width: 100%;
15
+ height: 100%;
16
+ margin: 0;
17
+ overflow: hidden;
18
+ background: #000;
19
+ }
20
+
21
+ #game {
22
+ min-width: 0;
23
+ min-height: 0;
24
+ }
25
+
26
+ #game canvas {
27
+ display: block;
28
+ }
@@ -0,0 +1,32 @@
1
+ export type AssetSources = Readonly<Record<string, string>>;
2
+
3
+ export interface AssetCollections {
4
+ images?: AssetSources;
5
+ imageSequences?: AssetSources;
6
+ videos?: AssetSources;
7
+ audios?: AssetSources;
8
+ }
9
+
10
+ export interface ImageSequenceManifest {
11
+ schema_version: string;
12
+ id: string;
13
+ type: 'image_sequence';
14
+ name: string;
15
+ properties: {
16
+ fps: number;
17
+ frame_count: number;
18
+ };
19
+ sources: readonly string[];
20
+ }
21
+
22
+ export type LoadedAsset =
23
+ | {
24
+ kind: 'image' | 'video' | 'audio';
25
+ key: string;
26
+ }
27
+ | {
28
+ kind: 'image-sequence';
29
+ key: string;
30
+ texture: string;
31
+ animation: string;
32
+ };