@playcraft/skills 0.0.2

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 (479) hide show
  1. package/README.md +333 -0
  2. package/dist/src/index.d.ts +3 -0
  3. package/dist/src/index.d.ts.map +1 -0
  4. package/dist/src/index.js +2 -0
  5. package/dist/src/index.js.map +1 -0
  6. package/dist/src/skills.loader.d.ts +31 -0
  7. package/dist/src/skills.loader.d.ts.map +1 -0
  8. package/dist/src/skills.loader.js +200 -0
  9. package/dist/src/skills.loader.js.map +1 -0
  10. package/dist/src/skills.types.d.ts +44 -0
  11. package/dist/src/skills.types.d.ts.map +1 -0
  12. package/dist/src/skills.types.js +9 -0
  13. package/dist/src/skills.types.js.map +1 -0
  14. package/package.json +35 -0
  15. package/skills/2048_core.aigameplay/SKILL.md +123 -0
  16. package/skills/2048_core.aigameplay/manifest.json +159 -0
  17. package/skills/2048_core.aigameplay/ref/pgs-schema.json +194 -0
  18. package/skills/2048_core.aigameplay/ref/reducer.template.ts +189 -0
  19. package/skills/SKILL.md +46 -0
  20. package/skills/ad_compliance_rules.aiconfig/SKILL.md +85 -0
  21. package/skills/ad_compliance_rules.aiconfig/manifest.json +60 -0
  22. package/skills/agent_remix_orchestrator.aicomponent/SKILL.md +257 -0
  23. package/skills/agent_remix_orchestrator.aicomponent/manifest.json +12 -0
  24. package/skills/app_metadata.aiconfig/SKILL.md +53 -0
  25. package/skills/app_metadata.aiconfig/manifest.json +32 -0
  26. package/skills/appear_sfx.aiaudio/SKILL.md +59 -0
  27. package/skills/appear_sfx.aiaudio/manifest.json +45 -0
  28. package/skills/appear_sfx.aiaudio/ref/appear.mp3 +0 -0
  29. package/skills/arrow_move_error_sfx.aiaudio/SKILL.md +59 -0
  30. package/skills/arrow_move_error_sfx.aiaudio/manifest.json +45 -0
  31. package/skills/arrow_move_error_sfx.aiaudio/ref/arrow_move_error.mp3 +0 -0
  32. package/skills/arrow_path_data_format.aicomponent/SKILL.md +152 -0
  33. package/skills/arrow_path_data_format.aicomponent/manifest.json +57 -0
  34. package/skills/arrow_path_data_format.aicomponent/ref/LevelParser.ts +188 -0
  35. package/skills/arrow_path_data_format.aicomponent/ref/LevelTypes.ts +131 -0
  36. package/skills/arrow_pick_match_rules.aigameplay/SKILL.md +117 -0
  37. package/skills/arrow_pick_match_rules.aigameplay/manifest.json +105 -0
  38. package/skills/arrow_pick_match_rules.aigameplay/ref/pgs-schema.json +180 -0
  39. package/skills/asset_pipeline_scripts.aicomponent/SKILL.md +117 -0
  40. package/skills/asset_pipeline_scripts.aicomponent/manifest.json +17 -0
  41. package/skills/asset_pipeline_scripts.aicomponent/ref/gen-sprite.sh +105 -0
  42. package/skills/ball_sort.aigameplay/SKILL.md +109 -0
  43. package/skills/ball_sort.aigameplay/manifest.json +155 -0
  44. package/skills/ball_sort.aigameplay/ref/pgs-schema.json +192 -0
  45. package/skills/ball_sort.aigameplay/ref/reducer.template.ts +156 -0
  46. package/skills/basketball_shot.aigameplay/SKILL.md +94 -0
  47. package/skills/basketball_shot.aigameplay/manifest.json +150 -0
  48. package/skills/basketball_shot.aigameplay/ref/pgs-schema.json +229 -0
  49. package/skills/basketball_shot.aigameplay/ref/reducer.template.ts +189 -0
  50. package/skills/bg_music.aiaudio/SKILL.md +61 -0
  51. package/skills/bg_music.aiaudio/manifest.json +48 -0
  52. package/skills/bg_music.aiaudio/ref/bg.mp3 +0 -0
  53. package/skills/big_watermelon.aigameplay/SKILL.md +94 -0
  54. package/skills/big_watermelon.aigameplay/manifest.json +155 -0
  55. package/skills/big_watermelon.aigameplay/ref/pgs-schema.json +191 -0
  56. package/skills/big_watermelon.aigameplay/ref/reducer.template.ts +202 -0
  57. package/skills/block_puzzle.aigameplay/SKILL.md +121 -0
  58. package/skills/block_puzzle.aigameplay/manifest.json +154 -0
  59. package/skills/block_puzzle.aigameplay/ref/pgs-schema.json +170 -0
  60. package/skills/block_puzzle.aigameplay/ref/reducer.template.ts +182 -0
  61. package/skills/board_entity_sprite.aiimage/SKILL.md +255 -0
  62. package/skills/board_entity_sprite.aiimage/manifest.json +153 -0
  63. package/skills/board_entity_sprite.aiimage/ref/ProceduralCar.ts +357 -0
  64. package/skills/board_entity_sprite.aiimage/ref/car.png +0 -0
  65. package/skills/board_entity_sprite.aiimage/ref/car.webp +0 -0
  66. package/skills/bottom_ui_bar.aicomponent/SKILL.md +50 -0
  67. package/skills/bottom_ui_bar.aicomponent/manifest.json +31 -0
  68. package/skills/bottom_ui_bar.aicomponent/ref/GameBottomUI.ts +27 -0
  69. package/skills/bubble_shooter.aigameplay/SKILL.md +129 -0
  70. package/skills/bubble_shooter.aigameplay/manifest.json +181 -0
  71. package/skills/bubble_shooter.aigameplay/ref/pgs-schema.json +226 -0
  72. package/skills/bubble_shooter.aigameplay/ref/reducer.template.ts +228 -0
  73. package/skills/button_click_sfx.aiaudio/SKILL.md +59 -0
  74. package/skills/button_click_sfx.aiaudio/manifest.json +45 -0
  75. package/skills/button_click_sfx.aiaudio/ref/button_click.mp3 +0 -0
  76. package/skills/calm_piano.aiaudio/SKILL.md +68 -0
  77. package/skills/calm_piano.aiaudio/manifest.json +50 -0
  78. package/skills/camera_controller_3d.aicomponent/SKILL.md +100 -0
  79. package/skills/camera_controller_3d.aicomponent/manifest.json +52 -0
  80. package/skills/camera_controller_3d.aicomponent/ref/CameraController.ts +199 -0
  81. package/skills/camera_controller_3d.aicomponent/ref/OrbitControlsAdapter.ts +152 -0
  82. package/skills/candy_tile.aiimage/SKILL.md +62 -0
  83. package/skills/candy_tile.aiimage/manifest.json +49 -0
  84. package/skills/car_parking.aigameplay/SKILL.md +105 -0
  85. package/skills/car_parking.aigameplay/manifest.json +164 -0
  86. package/skills/car_parking.aigameplay/ref/pgs-schema.json +163 -0
  87. package/skills/car_parking.aigameplay/ref/reducer.template.ts +166 -0
  88. package/skills/castle.aiimage/SKILL.md +64 -0
  89. package/skills/castle.aiimage/manifest.json +48 -0
  90. package/skills/cheerful_ukulele.aiaudio/SKILL.md +72 -0
  91. package/skills/cheerful_ukulele.aiaudio/manifest.json +50 -0
  92. package/skills/click_sfx.aiaudio/SKILL.md +59 -0
  93. package/skills/click_sfx.aiaudio/manifest.json +45 -0
  94. package/skills/click_sfx.aiaudio/ref/click.mp3 +0 -0
  95. package/skills/combo_display.aicomponent/SKILL.md +45 -0
  96. package/skills/combo_display.aicomponent/manifest.json +31 -0
  97. package/skills/combo_display.aicomponent/ref/ComboManager.ts +362 -0
  98. package/skills/combo_display.aicomponent/ref/ComboSmallUI.ts +232 -0
  99. package/skills/combo_praise_text.aicomponent/SKILL.md +56 -0
  100. package/skills/combo_praise_text.aicomponent/manifest.json +34 -0
  101. package/skills/combo_praise_text.aicomponent/ref/ComboManager.ts +362 -0
  102. package/skills/countdown_timer.aicomponent/SKILL.md +44 -0
  103. package/skills/countdown_timer.aicomponent/manifest.json +35 -0
  104. package/skills/countdown_timer.aicomponent/ref/CountdownDisplay.ts +213 -0
  105. package/skills/cta_platform_config.aiconfig/SKILL.md +72 -0
  106. package/skills/cta_platform_config.aiconfig/manifest.json +56 -0
  107. package/skills/debug_overlay.aicomponent/SKILL.md +75 -0
  108. package/skills/debug_overlay.aicomponent/manifest.json +17 -0
  109. package/skills/debug_overlay.aicomponent/ref/DebugOverlay.ts +144 -0
  110. package/skills/desert.aiimage/SKILL.md +63 -0
  111. package/skills/desert.aiimage/manifest.json +49 -0
  112. package/skills/difficulty_curve_designer.aiconfig/SKILL.md +68 -0
  113. package/skills/difficulty_curve_designer.aiconfig/manifest.json +36 -0
  114. package/skills/digit_renderer.aicomponent/SKILL.md +43 -0
  115. package/skills/digit_renderer.aicomponent/manifest.json +25 -0
  116. package/skills/digit_renderer.aicomponent/ref/DigitRenderer.ts +237 -0
  117. package/skills/download_button.aicomponent/SKILL.md +44 -0
  118. package/skills/download_button.aicomponent/manifest.json +25 -0
  119. package/skills/download_button.aicomponent/ref/DownloadButton.ts +137 -0
  120. package/skills/draw_line_puzzle.aigameplay/SKILL.md +84 -0
  121. package/skills/draw_line_puzzle.aigameplay/manifest.json +152 -0
  122. package/skills/draw_line_puzzle.aigameplay/ref/pgs-schema.json +189 -0
  123. package/skills/draw_line_puzzle.aigameplay/ref/reducer.template.ts +194 -0
  124. package/skills/easy_to_hard.aiconfig/SKILL.md +52 -0
  125. package/skills/easy_to_hard.aiconfig/manifest.json +33 -0
  126. package/skills/eight_ball_pool.aigameplay/SKILL.md +104 -0
  127. package/skills/eight_ball_pool.aigameplay/manifest.json +152 -0
  128. package/skills/eight_ball_pool.aigameplay/ref/pgs-schema.json +205 -0
  129. package/skills/eight_ball_pool.aigameplay/ref/reducer.template.ts +198 -0
  130. package/skills/energetic_electronic.aiaudio/SKILL.md +69 -0
  131. package/skills/energetic_electronic.aiaudio/manifest.json +51 -0
  132. package/skills/fail_sfx.aiaudio/SKILL.md +59 -0
  133. package/skills/fail_sfx.aiaudio/manifest.json +44 -0
  134. package/skills/fail_sfx.aiaudio/ref/fail.mp3 +0 -0
  135. package/skills/figer_icon.aiimage/SKILL.md +58 -0
  136. package/skills/figer_icon.aiimage/manifest.json +48 -0
  137. package/skills/figer_icon.aiimage/ref/figer.png +0 -0
  138. package/skills/figer_icon.aiimage/ref/figer.webp +0 -0
  139. package/skills/forest.aiimage/SKILL.md +74 -0
  140. package/skills/forest.aiimage/manifest.json +48 -0
  141. package/skills/fruit_tile.aiimage/SKILL.md +68 -0
  142. package/skills/fruit_tile.aiimage/manifest.json +48 -0
  143. package/skills/game_over_panel.aicomponent/SKILL.md +47 -0
  144. package/skills/game_over_panel.aicomponent/manifest.json +61 -0
  145. package/skills/game_over_panel.aicomponent/ref/GameOverPanel.ts +74 -0
  146. package/skills/game_scene.aicomponent/SKILL.md +57 -0
  147. package/skills/game_scene.aicomponent/manifest.json +36 -0
  148. package/skills/game_scene.aicomponent/ref/Game.ts +748 -0
  149. package/skills/gameplay_balance_check.aivalidator/SKILL.md +69 -0
  150. package/skills/gameplay_balance_check.aivalidator/manifest.json +21 -0
  151. package/skills/gameplay_unit_test.aivalidator/SKILL.md +89 -0
  152. package/skills/gameplay_unit_test.aivalidator/manifest.json +17 -0
  153. package/skills/gameplay_unit_test.aivalidator/ref/gameplay.test.ts +202 -0
  154. package/skills/gameplay_unit_test.aivalidator/ref/vitest.config.ts +13 -0
  155. package/skills/grid_board_layout.aicomponent/SKILL.md +85 -0
  156. package/skills/grid_board_layout.aicomponent/manifest.json +69 -0
  157. package/skills/grid_board_layout.aicomponent/ref/BoardLayout.ts +416 -0
  158. package/skills/grid_board_layout.aicomponent/ref/BoardLayout3D.ts +125 -0
  159. package/skills/grid_board_layout.aicomponent/ref/BoardLayoutMath.ts +85 -0
  160. package/skills/grid_board_layout.aicomponent/ref/BoardRenderer3D.ts +298 -0
  161. package/skills/heart_lives.aicomponent/SKILL.md +42 -0
  162. package/skills/heart_lives.aicomponent/manifest.json +25 -0
  163. package/skills/heart_lives.aicomponent/ref/HeartDisplay.ts +96 -0
  164. package/skills/idle-breathe.aicomponent/SKILL.md +27 -0
  165. package/skills/idle-breathe.aicomponent/manifest.json +20 -0
  166. package/skills/idle-breathe.aicomponent/ref/phaser.js +25 -0
  167. package/skills/idle-breathe.aicomponent/ref/playcanvas.js +29 -0
  168. package/skills/jewel_tile.aiimage/SKILL.md +63 -0
  169. package/skills/jewel_tile.aiimage/manifest.json +49 -0
  170. package/skills/knife_hit.aigameplay/SKILL.md +103 -0
  171. package/skills/knife_hit.aigameplay/manifest.json +151 -0
  172. package/skills/knife_hit.aigameplay/ref/pgs-schema.json +202 -0
  173. package/skills/knife_hit.aigameplay/ref/reducer.template.ts +196 -0
  174. package/skills/left_right_parkour.aigameplay/SKILL.md +120 -0
  175. package/skills/left_right_parkour.aigameplay/manifest.json +165 -0
  176. package/skills/left_right_parkour.aigameplay/ref/pgs-schema.json +200 -0
  177. package/skills/left_right_parkour.aigameplay/ref/reducer.template.ts +194 -0
  178. package/skills/level_data_pack.aiconfig/SKILL.md +64 -0
  179. package/skills/level_data_pack.aiconfig/manifest.json +37 -0
  180. package/skills/level_data_validator.aivalidator/SKILL.md +85 -0
  181. package/skills/level_data_validator.aivalidator/manifest.json +17 -0
  182. package/skills/level_data_validator.aivalidator/ref/LevelValidator.ts +113 -0
  183. package/skills/level_lifecycle.aicomponent/SKILL.md +81 -0
  184. package/skills/level_lifecycle.aicomponent/manifest.json +87 -0
  185. package/skills/level_lifecycle.aicomponent/ref/LevelLifecycle.ts +235 -0
  186. package/skills/level_solvability_validator.aivalidator/SKILL.md +74 -0
  187. package/skills/level_solvability_validator.aivalidator/manifest.json +23 -0
  188. package/skills/level_solvability_validator.aivalidator/ref/LevelValidator.ts +186 -0
  189. package/skills/level_state.aicomponent/SKILL.md +55 -0
  190. package/skills/level_state.aicomponent/manifest.json +34 -0
  191. package/skills/level_state.aicomponent/ref/LevelDataManager.ts +26 -0
  192. package/skills/level_state.aicomponent/ref/LevelManager.ts +26 -0
  193. package/skills/loading_screen.aicomponent/SKILL.md +57 -0
  194. package/skills/loading_screen.aicomponent/manifest.json +43 -0
  195. package/skills/loading_screen.aicomponent/ref/LoadingUI.ts +339 -0
  196. package/skills/lose_result_panel.aiimage/SKILL.md +58 -0
  197. package/skills/lose_result_panel.aiimage/manifest.json +47 -0
  198. package/skills/lose_result_panel.aiimage/ref/lose.png +0 -0
  199. package/skills/lose_result_panel.aiimage/ref/lose.webp +0 -0
  200. package/skills/lose_sfx.aiaudio/SKILL.md +59 -0
  201. package/skills/lose_sfx.aiaudio/manifest.json +45 -0
  202. package/skills/lose_sfx.aiaudio/ref/lose.mp3 +0 -0
  203. package/skills/match-pop.aicomponent/SKILL.md +29 -0
  204. package/skills/match-pop.aicomponent/manifest.json +19 -0
  205. package/skills/match-pop.aicomponent/ref/phaser.js +30 -0
  206. package/skills/match-pop.aicomponent/ref/playcanvas.js +22 -0
  207. package/skills/match3_core.aigameplay/SKILL.md +125 -0
  208. package/skills/match3_core.aigameplay/manifest.json +107 -0
  209. package/skills/match3_core.aigameplay/ref/pgs-schema.json +154 -0
  210. package/skills/match3_core.aigameplay/ref/reducer.template.ts +191 -0
  211. package/skills/match_engine.aicomponent/SKILL.md +84 -0
  212. package/skills/match_engine.aicomponent/manifest.json +23 -0
  213. package/skills/match_engine.aicomponent/ref/MatchEngine.ts +75 -0
  214. package/skills/memory_match.aigameplay/SKILL.md +111 -0
  215. package/skills/memory_match.aigameplay/manifest.json +164 -0
  216. package/skills/memory_match.aigameplay/ref/pgs-schema.json +190 -0
  217. package/skills/memory_match.aigameplay/ref/reducer.template.ts +162 -0
  218. package/skills/merge_core.aigameplay/SKILL.md +112 -0
  219. package/skills/merge_core.aigameplay/manifest.json +154 -0
  220. package/skills/merge_core.aigameplay/ref/pgs-schema.json +202 -0
  221. package/skills/merge_core.aigameplay/ref/reducer.template.ts +179 -0
  222. package/skills/mistake_sfx.aiaudio/SKILL.md +59 -0
  223. package/skills/mistake_sfx.aiaudio/manifest.json +45 -0
  224. package/skills/mistake_sfx.aiaudio/ref/mistake.mp3 +0 -0
  225. package/skills/nomove_hint_overlay.aiimage/SKILL.md +58 -0
  226. package/skills/nomove_hint_overlay.aiimage/manifest.json +48 -0
  227. package/skills/nomove_hint_overlay.aiimage/ref/nomove_hint.png +0 -0
  228. package/skills/nomove_hint_overlay.aiimage/ref/nomove_hint.webp +0 -0
  229. package/skills/normal_dot.aiimage/SKILL.md +58 -0
  230. package/skills/normal_dot.aiimage/manifest.json +46 -0
  231. package/skills/normal_dot.aiimage/ref/normal_dot.png +0 -0
  232. package/skills/normal_dot.aiimage/ref/normal_dot.webp +0 -0
  233. package/skills/ocean.aiimage/SKILL.md +71 -0
  234. package/skills/ocean.aiimage/manifest.json +49 -0
  235. package/skills/particle-confetti.aicomponent/SKILL.md +32 -0
  236. package/skills/particle-confetti.aicomponent/manifest.json +21 -0
  237. package/skills/particle-confetti.aicomponent/ref/html5-spritesheet.js +17 -0
  238. package/skills/particle-confetti.aicomponent/ref/phaser.js +38 -0
  239. package/skills/particle-explosion.aicomponent/SKILL.md +37 -0
  240. package/skills/particle-explosion.aicomponent/manifest.json +24 -0
  241. package/skills/particle-explosion.aicomponent/ref/html5-spritesheet.js +40 -0
  242. package/skills/particle-explosion.aicomponent/ref/phaser.js +46 -0
  243. package/skills/particle-explosion.aicomponent/ref/playcanvas.js +33 -0
  244. package/skills/particle-trail.aicomponent/SKILL.md +25 -0
  245. package/skills/particle-trail.aicomponent/manifest.json +22 -0
  246. package/skills/particle-trail.aicomponent/ref/phaser.js +31 -0
  247. package/skills/path_animation.aicomponent/SKILL.md +82 -0
  248. package/skills/path_animation.aicomponent/manifest.json +77 -0
  249. package/skills/path_animation.aicomponent/ref/AnimationManager.ts +694 -0
  250. package/skills/path_animation.aicomponent/ref/AnimationManager3D.ts +317 -0
  251. package/skills/path_elimination_rules.aigameplay/SKILL.md +88 -0
  252. package/skills/path_elimination_rules.aigameplay/manifest.json +119 -0
  253. package/skills/path_elimination_rules.aigameplay/ref/pgs-schema.json +227 -0
  254. package/skills/path_input_handler.aicomponent/SKILL.md +87 -0
  255. package/skills/path_input_handler.aicomponent/manifest.json +97 -0
  256. package/skills/path_input_handler.aicomponent/ref/InputHandler.ts +714 -0
  257. package/skills/path_input_handler.aicomponent/ref/InputHandler3D.ts +204 -0
  258. package/skills/path_renderer.aicomponent/SKILL.md +101 -0
  259. package/skills/path_renderer.aicomponent/manifest.json +69 -0
  260. package/skills/path_renderer.aicomponent/ref/PathRenderer.ts +509 -0
  261. package/skills/path_renderer.aicomponent/ref/PathRenderer3D.ts +176 -0
  262. package/skills/path_renderer.aicomponent/ref/PathRenderers.ts +640 -0
  263. package/skills/phaser.aicomponent/SKILL.md +315 -0
  264. package/skills/phaser.aicomponent/manifest.json +156 -0
  265. package/skills/phaser.aicomponent/ref/BootScene.ts +29 -0
  266. package/skills/phaser.aicomponent/ref/GameConfig.ts +29 -0
  267. package/skills/phaser.aicomponent/ref/GameScene.ts +88 -0
  268. package/skills/phaser.aicomponent/ref/PreloaderScene.ts +78 -0
  269. package/skills/phaser.aicomponent/ref/SceneKeys.ts +7 -0
  270. package/skills/phaser.aicomponent/ref/SoundUtils.ts +21 -0
  271. package/skills/phaser.aicomponent/ref/UiLayout.ts +74 -0
  272. package/skills/phaser.aicomponent/ref/globals.d.ts +52 -0
  273. package/skills/phaser.aicomponent/ref/index.css +40 -0
  274. package/skills/phaser.aicomponent/ref/index.html +14 -0
  275. package/skills/phaser.aicomponent/ref/index.ts +48 -0
  276. package/skills/phaser.aicomponent/ref/main.ts +16 -0
  277. package/skills/phaser.aicomponent/ref/package.json +22 -0
  278. package/skills/phaser.aicomponent/ref/tsconfig.json +25 -0
  279. package/skills/phaser.aicomponent/ref/webpack.config.js +57 -0
  280. package/skills/phaser_scene_lifecycle.aicomponent/SKILL.md +63 -0
  281. package/skills/phaser_scene_lifecycle.aicomponent/manifest.json +33 -0
  282. package/skills/pick_and_match_rules.aigameplay/SKILL.md +113 -0
  283. package/skills/pick_and_match_rules.aigameplay/manifest.json +121 -0
  284. package/skills/pick_and_match_rules.aigameplay/ref/pgs-schema.json +354 -0
  285. package/skills/pin_pull.aigameplay/SKILL.md +110 -0
  286. package/skills/pin_pull.aigameplay/manifest.json +164 -0
  287. package/skills/pin_pull.aigameplay/ref/pgs-schema.json +201 -0
  288. package/skills/pin_pull.aigameplay/ref/reducer.template.ts +216 -0
  289. package/skills/playable_app_logo.aiimage/SKILL.md +58 -0
  290. package/skills/playable_app_logo.aiimage/manifest.json +47 -0
  291. package/skills/playable_app_logo.aiimage/ref/logo.png +0 -0
  292. package/skills/playable_app_logo.aiimage/ref/logo.webp +0 -0
  293. package/skills/playable_end_screen_layout.aicomponent/SKILL.md +75 -0
  294. package/skills/playable_end_screen_layout.aicomponent/manifest.json +24 -0
  295. package/skills/playable_guidance_layer.aicomponent/SKILL.md +89 -0
  296. package/skills/playable_guidance_layer.aicomponent/manifest.json +24 -0
  297. package/skills/playable_hud_layout.aicomponent/SKILL.md +96 -0
  298. package/skills/playable_hud_layout.aicomponent/manifest.json +24 -0
  299. package/skills/playable_scripts_build.aicomponent/SKILL.md +69 -0
  300. package/skills/playable_scripts_build.aicomponent/manifest.json +53 -0
  301. package/skills/playable_scripts_build.aicomponent/ref/builds.config.js +257 -0
  302. package/skills/playcraft-3d-flip-sprite/SKILL.md +336 -0
  303. package/skills/playcraft-3d-flip-sprite/renderer/flatten_glb.mjs +62 -0
  304. package/skills/playcraft-3d-flip-sprite/renderer/render.mjs +325 -0
  305. package/skills/playcraft-3d-flip-sprite/renderer/render_single.mjs +138 -0
  306. package/skills/playcraft-asset-management/SKILL.md +73 -0
  307. package/skills/playcraft-audio-generation/SKILL.md +126 -0
  308. package/skills/playcraft-build/SKILL.md +44 -0
  309. package/skills/playcraft-code-editor/SKILL.md +71 -0
  310. package/skills/playcraft-create-remix/SKILL.md +62 -0
  311. package/skills/playcraft-deploy/SKILL.md +59 -0
  312. package/skills/playcraft-image-generation/SKILL.md +148 -0
  313. package/skills/playcraft-image-processing/SKILL.md +216 -0
  314. package/skills/playcraft-platform-intro/SKILL.md +41 -0
  315. package/skills/playcraft-prefab/SKILL.md +98 -0
  316. package/skills/playcraft-project-management/SKILL.md +57 -0
  317. package/skills/playcraft-remix-workflow/SKILL.md +119 -0
  318. package/skills/playcraft-remix-workflow/references/xplatform.schema.json5 +31 -0
  319. package/skills/playcraft-save/SKILL.md +46 -0
  320. package/skills/playcraft-skill-recommender/SKILL.md +152 -0
  321. package/skills/playcraft-sprite-generation/SKILL.md +534 -0
  322. package/skills/playcraft-sprite-remix/SKILL.md +155 -0
  323. package/skills/playcraft-sprite-sheet/SKILL.md +97 -0
  324. package/skills/playtest_report.aivalidator/SKILL.md +103 -0
  325. package/skills/playtest_report.aivalidator/manifest.json +21 -0
  326. package/skills/preloader_scene.aicomponent/SKILL.md +58 -0
  327. package/skills/preloader_scene.aicomponent/manifest.json +43 -0
  328. package/skills/preloader_scene.aicomponent/ref/Preloader.ts +339 -0
  329. package/skills/progress_bar.aicomponent/SKILL.md +38 -0
  330. package/skills/progress_bar.aicomponent/manifest.json +25 -0
  331. package/skills/progress_bar.aicomponent/ref/ProgressDisplay.ts +140 -0
  332. package/skills/references/xplatform.schema.json5 +31 -0
  333. package/skills/responsive_2d_layout.aicomponent/SKILL.md +161 -0
  334. package/skills/responsive_2d_layout.aicomponent/manifest.json +43 -0
  335. package/skills/responsive_2d_layout.aicomponent/ref/UiLayout.ts +56 -0
  336. package/skills/run_context_state.aicomponent/SKILL.md +54 -0
  337. package/skills/run_context_state.aicomponent/manifest.json +40 -0
  338. package/skills/run_context_state.aicomponent/ref/RunContextRuntime.ts +30 -0
  339. package/skills/score-fly.aicomponent/SKILL.md +31 -0
  340. package/skills/score-fly.aicomponent/manifest.json +21 -0
  341. package/skills/score-fly.aicomponent/ref/phaser.js +26 -0
  342. package/skills/score-fly.aicomponent/ref/playcanvas.js +28 -0
  343. package/skills/score_goal.aiconfig/SKILL.md +46 -0
  344. package/skills/score_goal.aiconfig/manifest.json +33 -0
  345. package/skills/screen-flash.aicomponent/SKILL.md +30 -0
  346. package/skills/screen-flash.aicomponent/manifest.json +20 -0
  347. package/skills/screen-flash.aicomponent/ref/phaser.js +48 -0
  348. package/skills/screen-shake.aicomponent/SKILL.md +30 -0
  349. package/skills/screen-shake.aicomponent/manifest.json +19 -0
  350. package/skills/screen-shake.aicomponent/ref/phaser.js +23 -0
  351. package/skills/screen-shake.aicomponent/ref/playcanvas.js +44 -0
  352. package/skills/screw_puzzle.aigameplay/SKILL.md +116 -0
  353. package/skills/screw_puzzle.aigameplay/manifest.json +164 -0
  354. package/skills/screw_puzzle.aigameplay/ref/pgs-schema.json +235 -0
  355. package/skills/screw_puzzle.aigameplay/ref/reducer.template.ts +213 -0
  356. package/skills/settings_state.aicomponent/SKILL.md +61 -0
  357. package/skills/settings_state.aicomponent/manifest.json +33 -0
  358. package/skills/settings_state.aicomponent/ref/SettingsManager.ts +130 -0
  359. package/skills/slide_out_to_tray_animation.aicomponent/SKILL.md +64 -0
  360. package/skills/slide_out_to_tray_animation.aicomponent/manifest.json +58 -0
  361. package/skills/slide_out_to_tray_animation.aicomponent/ref/AnimationManager.ts +164 -0
  362. package/skills/slide_out_to_tray_animation.aicomponent/ref/AnimationManager3D.ts +198 -0
  363. package/skills/slide_out_to_tray_animation.aicomponent/ref/BezierUtils.ts +59 -0
  364. package/skills/slot_machine.aigameplay/SKILL.md +119 -0
  365. package/skills/slot_machine.aigameplay/manifest.json +166 -0
  366. package/skills/slot_machine.aigameplay/ref/pgs-schema.json +212 -0
  367. package/skills/slot_machine.aigameplay/ref/reducer.template.ts +212 -0
  368. package/skills/sniper_shot.aigameplay/SKILL.md +84 -0
  369. package/skills/sniper_shot.aigameplay/manifest.json +153 -0
  370. package/skills/sniper_shot.aigameplay/ref/pgs-schema.json +211 -0
  371. package/skills/sniper_shot.aigameplay/ref/reducer.template.ts +211 -0
  372. package/skills/solitaire.aigameplay/SKILL.md +101 -0
  373. package/skills/solitaire.aigameplay/manifest.json +151 -0
  374. package/skills/solitaire.aigameplay/ref/pgs-schema.json +200 -0
  375. package/skills/solitaire.aigameplay/ref/reducer.template.ts +176 -0
  376. package/skills/sound-effects/SKILL.md +46 -0
  377. package/skills/sound_utils.aicomponent/SKILL.md +52 -0
  378. package/skills/sound_utils.aicomponent/manifest.json +30 -0
  379. package/skills/sound_utils.aicomponent/ref/SoundUtils.ts +29 -0
  380. package/skills/sprite-animation-2d.aicomponent/SKILL.md +45 -0
  381. package/skills/sprite-animation-2d.aicomponent/manifest.json +28 -0
  382. package/skills/sprite-animation-2d.aicomponent/ref/html5.js +83 -0
  383. package/skills/sprite-animation-2d.aicomponent/ref/phaser.js +38 -0
  384. package/skills/sprite-animation-2d.aicomponent/ref/playcanvas.js +67 -0
  385. package/skills/sprite_entity_renderer.aicomponent/SKILL.md +148 -0
  386. package/skills/sprite_entity_renderer.aicomponent/manifest.json +17 -0
  387. package/skills/sprite_entity_renderer.aicomponent/ref/SpriteEntityRenderer.ts +126 -0
  388. package/skills/success_sfx.aiaudio/SKILL.md +59 -0
  389. package/skills/success_sfx.aiaudio/manifest.json +45 -0
  390. package/skills/success_sfx.aiaudio/ref/success.mp3 +0 -0
  391. package/skills/tap_blast.aigameplay/SKILL.md +116 -0
  392. package/skills/tap_blast.aigameplay/manifest.json +153 -0
  393. package/skills/tap_blast.aigameplay/ref/pgs-schema.json +208 -0
  394. package/skills/tap_blast.aigameplay/ref/reducer.template.ts +202 -0
  395. package/skills/theme_state.aicomponent/SKILL.md +53 -0
  396. package/skills/theme_state.aicomponent/manifest.json +33 -0
  397. package/skills/theme_state.aicomponent/ref/ThemeManager.ts +174 -0
  398. package/skills/theme_switcher_build.aicomponent/SKILL.md +62 -0
  399. package/skills/theme_switcher_build.aicomponent/manifest.json +38 -0
  400. package/skills/theme_switcher_build.aicomponent/ref/theme-index.ts +8 -0
  401. package/skills/theme_variant.aiconfig/SKILL.md +78 -0
  402. package/skills/theme_variant.aiconfig/manifest.json +39 -0
  403. package/skills/theme_variant.aiconfig/ref/ExampleTheme/index.ts +16 -0
  404. package/skills/theme_variant.aiconfig/ref/ExampleTheme/levelEasy.json +141 -0
  405. package/skills/threejs.aicomponent/SKILL.md +422 -0
  406. package/skills/threejs.aicomponent/manifest.json +166 -0
  407. package/skills/threejs.aicomponent/ref/AssetLoader.ts +158 -0
  408. package/skills/threejs.aicomponent/ref/BaseScene.ts +88 -0
  409. package/skills/threejs.aicomponent/ref/GameConfig.ts +47 -0
  410. package/skills/threejs.aicomponent/ref/MainScene.ts +170 -0
  411. package/skills/threejs.aicomponent/ref/PreloaderScene.ts +93 -0
  412. package/skills/threejs.aicomponent/ref/SceneKeys.ts +6 -0
  413. package/skills/threejs.aicomponent/ref/SceneManager.ts +69 -0
  414. package/skills/threejs.aicomponent/ref/ViewportManager.ts +62 -0
  415. package/skills/threejs.aicomponent/ref/globals.d.ts +74 -0
  416. package/skills/threejs.aicomponent/ref/index.css +80 -0
  417. package/skills/threejs.aicomponent/ref/index.html +20 -0
  418. package/skills/threejs.aicomponent/ref/index.ts +117 -0
  419. package/skills/threejs.aicomponent/ref/main.ts +10 -0
  420. package/skills/threejs.aicomponent/ref/package.json +23 -0
  421. package/skills/threejs.aicomponent/ref/tsconfig.json +24 -0
  422. package/skills/threejs.aicomponent/ref/webpack.config.js +61 -0
  423. package/skills/time_clear_goal.aiconfig/SKILL.md +46 -0
  424. package/skills/time_clear_goal.aiconfig/manifest.json +32 -0
  425. package/skills/time_icon.aiimage/SKILL.md +58 -0
  426. package/skills/time_icon.aiimage/manifest.json +47 -0
  427. package/skills/time_icon.aiimage/ref/time.png +0 -0
  428. package/skills/time_icon.aiimage/ref/time.webp +0 -0
  429. package/skills/top_ui_bar.aicomponent/SKILL.md +45 -0
  430. package/skills/top_ui_bar.aicomponent/manifest.json +27 -0
  431. package/skills/top_ui_bar.aicomponent/ref/GameTopUI.ts +229 -0
  432. package/skills/tower_defence.aigameplay/SKILL.md +117 -0
  433. package/skills/tower_defence.aigameplay/manifest.json +175 -0
  434. package/skills/tower_defence.aigameplay/ref/pgs-schema.json +247 -0
  435. package/skills/tower_defence.aigameplay/ref/reducer.template.ts +239 -0
  436. package/skills/tray_container.aicomponent/SKILL.md +143 -0
  437. package/skills/tray_container.aicomponent/manifest.json +58 -0
  438. package/skills/tray_container.aicomponent/ref/TrayRenderer.ts +155 -0
  439. package/skills/tray_container.aicomponent/ref/TrayRenderer3D.ts +281 -0
  440. package/skills/tray_container.aicomponent/ref/TrayRendererHtmlOverlay.ts +228 -0
  441. package/skills/tutorial_overlay.aicomponent/SKILL.md +46 -0
  442. package/skills/tutorial_overlay.aicomponent/manifest.json +34 -0
  443. package/skills/tutorial_overlay.aicomponent/ref/TutorialManager.ts +367 -0
  444. package/skills/tween-animation-2d.aicomponent/SKILL.md +35 -0
  445. package/skills/tween-animation-2d.aicomponent/manifest.json +28 -0
  446. package/skills/tween-animation-2d.aicomponent/ref/phaser.js +49 -0
  447. package/skills/tween-animation-2d.aicomponent/ref/playcanvas.js +36 -0
  448. package/skills/typescript_module_patterns.aicomponent/SKILL.md +209 -0
  449. package/skills/typescript_module_patterns.aicomponent/manifest.json +28 -0
  450. package/skills/typescript_module_patterns.aicomponent/ref/globals.d.ts +31 -0
  451. package/skills/ui-transition.aicomponent/SKILL.md +33 -0
  452. package/skills/ui-transition.aicomponent/manifest.json +19 -0
  453. package/skills/ui-transition.aicomponent/ref/phaser.js +45 -0
  454. package/skills/ux_flow_spec.aiconfig/SKILL.md +82 -0
  455. package/skills/ux_flow_spec.aiconfig/manifest.json +39 -0
  456. package/skills/wave_difficulty.aiconfig/SKILL.md +46 -0
  457. package/skills/wave_difficulty.aiconfig/manifest.json +33 -0
  458. package/skills/webpack_build.aicomponent/SKILL.md +204 -0
  459. package/skills/webpack_build.aicomponent/manifest.json +29 -0
  460. package/skills/webpack_build.aicomponent/ref/package.json +16 -0
  461. package/skills/webpack_build.aicomponent/ref/webpack.config.js +53 -0
  462. package/skills/win_anim_sfx.aiaudio/SKILL.md +59 -0
  463. package/skills/win_anim_sfx.aiaudio/manifest.json +46 -0
  464. package/skills/win_anim_sfx.aiaudio/ref/win_anim.mp3 +0 -0
  465. package/skills/win_dot.aiimage/SKILL.md +58 -0
  466. package/skills/win_dot.aiimage/manifest.json +47 -0
  467. package/skills/win_dot.aiimage/ref/win_dot.png +0 -0
  468. package/skills/win_dot.aiimage/ref/win_dot.webp +0 -0
  469. package/skills/win_result_panel.aiimage/SKILL.md +58 -0
  470. package/skills/win_result_panel.aiimage/manifest.json +47 -0
  471. package/skills/win_result_panel.aiimage/ref/win.png +0 -0
  472. package/skills/win_result_panel.aiimage/ref/win.webp +0 -0
  473. package/skills/word_connect.aigameplay/SKILL.md +86 -0
  474. package/skills/word_connect.aigameplay/manifest.json +154 -0
  475. package/skills/word_connect.aigameplay/ref/pgs-schema.json +212 -0
  476. package/skills/word_connect.aigameplay/ref/reducer.template.ts +186 -0
  477. package/src/index.ts +10 -0
  478. package/src/skills.loader.ts +225 -0
  479. package/src/skills.types.ts +42 -0
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Arrow Puzzle 批量构建配置文件
3
+ *
4
+ * 使用 playable-scripts 的批量构建功能
5
+ *
6
+ * 使用方法:
7
+ * 1. 构建所有主题所有渠道:npm run builds
8
+ * 2. 构建指定主题:npm run builds -- -t "H-0001-Challenge Mode"
9
+ * 3. 构建指定渠道:npm run builds -- -c google,facebook
10
+ * 4. 指定并发数:npm run builds -- -p 4
11
+ */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ // 项目配置
17
+ const PROJECT_CONFIG = {
18
+ name: 'Arrow Puzzle', // 项目名称
19
+ googlePlayUrl: 'https://play.google.com/store/apps/details?id=com.ecffri.arrows&hl=en_US',
20
+ appStoreUrl: 'https://apps.apple.com/us/app/arrows-puzzle-escape/id6748397500',
21
+ };
22
+
23
+ // 文件命名配置
24
+ const NAMING_CONFIG = {
25
+ language: 'EN', // 语言
26
+ format: 'NA', // 格式
27
+ supplier: 'AIX', // 供应商
28
+ follower: 'WZN' // 跟进人
29
+ };
30
+
31
+ // 主题黑名单(这些主题不会被打包)
32
+ const THEME_BLACKLIST = [
33
+ 'dev', // 开发模式主题,仅用于本地开发
34
+ 'current.ts', // 主题配置文件,非主题目录
35
+ ];
36
+
37
+ /**
38
+ * 从 theme 目录自动读取所有主题变体
39
+ */
40
+ function getThemeVariants() {
41
+ const THEME_DIR = path.join(__dirname, 'src', 'theme');
42
+
43
+ try {
44
+ const items = fs.readdirSync(THEME_DIR, { withFileTypes: true });
45
+ const themes = items
46
+ .filter(item => item.isDirectory()) // 只保留目录
47
+ .map(item => item.name)
48
+ .filter(name => !THEME_BLACKLIST.includes(name)); // 过滤黑名单
49
+
50
+ return themes;
51
+ } catch (error) {
52
+ console.error(`读取主题目录失败: ${error.message}`);
53
+ return [];
54
+ }
55
+ }
56
+
57
+ // 获取当前月份(两位数)
58
+ function getCurrentMonth() {
59
+ return String(new Date().getMonth() + 1).padStart(2, '0');
60
+ }
61
+
62
+ // 获取当前日期(YYYYMMDD格式)
63
+ function getCurrentDate() {
64
+ const date = new Date();
65
+ const year = date.getFullYear();
66
+ const month = String(date.getMonth() + 1).padStart(2, '0');
67
+ const day = String(date.getDate()).padStart(2, '0');
68
+ return `${year}${month}${day}`;
69
+ }
70
+
71
+ /**
72
+ * 自定义文件名生成器
73
+ * 规则: {主题名}-EN-NA-{月}-{日期}-AIX-ZQL-{渠道}
74
+ *
75
+ * @param {string} themeName - 主题名称
76
+ * @param {string} channel - 渠道名称
77
+ * @param {string} date - 日期(YYYYMMDD格式)
78
+ * @param {string} month - 月份(中文格式,如:3月)
79
+ * @returns {string} 文件名(不含扩展名)
80
+ */
81
+ function generateFileName(themeName, channel, date, month) {
82
+ // 从中文月份提取数字(如:3月 → 03)
83
+ const monthNumber = getCurrentMonth();
84
+
85
+ const parts = [
86
+ themeName, // 主题名(如:H-0001-Challenge Mode)
87
+ NAMING_CONFIG.language, // EN
88
+ NAMING_CONFIG.format, // NA
89
+ monthNumber, // 03(两位数字)
90
+ date, // 20260302
91
+ NAMING_CONFIG.supplier, // AIX
92
+ NAMING_CONFIG.follower, // ZQL
93
+ channel.toLowerCase() // facebook
94
+ ];
95
+
96
+ return parts.join('-');
97
+ }
98
+
99
+ module.exports = {
100
+ // ==================== 项目配置 ====================
101
+
102
+ projectName: PROJECT_CONFIG.name,
103
+
104
+ // 商店链接配置
105
+ googlePlayUrl: PROJECT_CONFIG.googlePlayUrl,
106
+ appStoreUrl: PROJECT_CONFIG.appStoreUrl,
107
+
108
+ // ==================== 主题配置 ====================
109
+
110
+ themes: {
111
+ enabled: true,
112
+
113
+ // 主题列表(自动从 src/theme 目录读取)
114
+ list: getThemeVariants(),
115
+
116
+ // 主题切换配置
117
+ switching: {
118
+ // 入口文件路径(相对于项目根目录)
119
+ entryFile: 'src/theme/current.ts',
120
+
121
+ // 入口文件生成函数
122
+ generateEntryFile: (themeName) => {
123
+ return `/**
124
+ * 当前激活的主题变体
125
+ *
126
+ * 注意: 这个文件会在构建时被 builds 脚本自动修改
127
+ * 请不要手动修改这个值,除非你在本地开发测试
128
+ */
129
+
130
+ import type { ThemeVariantConfig } from '../client/game/config/ThemeVariants';
131
+
132
+ // 构建脚本会修改这一行
133
+ import config, { levels } from './${themeName}';
134
+
135
+ const currentConfig: ThemeVariantConfig = config;
136
+ export default currentConfig;
137
+
138
+ // 同时导出关卡数据
139
+ export { levels };
140
+ `;
141
+ },
142
+
143
+ // 关卡数据文件切换(可选)
144
+ additionalFiles: [
145
+ {
146
+ path: 'src/client/game/level-data/index.ts',
147
+ generate: (themeName) => {
148
+ return `// 构建脚本自动生成 - 当前主题: ${themeName}
149
+ import { levels } from "../../../theme/${themeName}";
150
+ export default levels;
151
+ `;
152
+ }
153
+ }
154
+ ]
155
+ },
156
+
157
+ // 默认主题(构建完成后恢复)
158
+ defaultTheme: 'dev',
159
+ },
160
+
161
+ // ==================== 渠道配置 ====================
162
+
163
+ channels: [
164
+ 'mintegral',
165
+ 'unity',
166
+ 'ironSource',
167
+ 'facebook',
168
+ 'tiktok',
169
+ 'google',
170
+ 'applovin',
171
+ 'snapchat',
172
+ 'moloco',
173
+ 'liftoff',
174
+ 'adikteev',
175
+ 'inmobi'
176
+ ],
177
+
178
+ // ==================== 文件命名配置 ====================
179
+
180
+ naming: {
181
+ // 自定义文件名生成器
182
+ generator: generateFileName,
183
+ },
184
+
185
+ // ==================== 构建配置 ====================
186
+
187
+ build: {
188
+ // 输出目录
189
+ // outputDir: 'dist-all',
190
+
191
+ // // 临时目录前缀
192
+ // tempDirPrefix: 'dist-temp',
193
+
194
+ // // 构建错误时是否继续
195
+ // continueOnError: true,
196
+
197
+ // // 构建完成后清理临时目录
198
+ // cleanTemp: true,
199
+
200
+ // // 并发数(根据 CPU 核心数自动调整:CPU核心数-1,最小2,最大8)
201
+ // // 可以通过命令行参数 -p 覆盖
202
+ // parallel: (() => {
203
+ // const cpuCount = require('os').cpus().length;
204
+ // const calculated = Math.max(2, cpuCount - 1);
205
+ // return Math.min(8, calculated);
206
+ // })(),
207
+ },
208
+
209
+ // ==================== 钩子函数(可选)====================
210
+
211
+ hooks: {
212
+ /**
213
+ * 构建前钩子
214
+ * @param {string} themeName - 主题名称
215
+ * @param {string} channel - 渠道名称
216
+ */
217
+ beforeBuild: (themeName, channel) => {
218
+ // console.log(`准备构建: ${themeName}/${channel}`);
219
+ },
220
+
221
+ /**
222
+ * 构建后钩子
223
+ * @param {string} themeName - 主题名称
224
+ * @param {string} channel - 渠道名称
225
+ * @param {boolean} success - 是否成功
226
+ */
227
+ afterBuild: (themeName, channel, success) => {
228
+ // if (success) {
229
+ // console.log(`构建成功: ${themeName}/${channel}`);
230
+ // }
231
+ },
232
+
233
+ /**
234
+ * 主题切换前钩子
235
+ * @param {string} themeName - 主题名称
236
+ */
237
+ beforeThemeSwitch: (themeName) => {
238
+ // 可以在这里清理 webpack 缓存等
239
+ const cacheDir = path.join(__dirname, 'node_modules', '.cache');
240
+ if (fs.existsSync(cacheDir)) {
241
+ try {
242
+ fs.rmSync(cacheDir, { recursive: true, force: true });
243
+ } catch (err) {
244
+ // 忽略清理失败
245
+ }
246
+ }
247
+ },
248
+
249
+ /**
250
+ * 主题切换后钩子
251
+ * @param {string} themeName - 主题名称
252
+ */
253
+ afterThemeSwitch: (themeName) => {
254
+ // console.log(`已切换主题: ${themeName}`);
255
+ },
256
+ }
257
+ };
@@ -0,0 +1,336 @@
1
+ ---
2
+ name: playcraft-3d-flip-sprite
3
+ description: 基于 3D GLB 模型生成翻牌/翻转精灵图(sprite sheet)。使用纯 JS 软件光栅化渲染器(无需 Puppeteer / headless-gl),输出可直接用于 Canvas 动画的 PNG 精灵图。适用于麻将翻牌、卡牌翻转、硬币翻转等场景。
4
+ compatibility: agent,opencode
5
+ ---
6
+
7
+ # 3D 翻转精灵图生成 Skill
8
+
9
+ ## 我做什么
10
+
11
+ 1. 调用 `playcraft tools generate-3d` 生成 GLB 3D 模型(可选,也可直接使用现有 GLB)
12
+ 2. 运行纯 JS 软件光栅化渲染器,将 GLB 模型渲染成多帧翻转动画
13
+ 3. 合并输出一张 PNG 精灵图,可直接在 Canvas / WebGL / PlayCanvas 中播放
14
+
15
+ 渲染器特性:
16
+ - 透视投影 + Z-buffer 深度测试
17
+ - 从 GLB UV atlas 双线性采样贴图
18
+ - Phong 光照(环境光 + 漫反射 + 镜面高光)
19
+ - 逐顶点法线插值(平滑着色)
20
+ - 2× 超采样抗锯齿(SSAA)
21
+ - **零原生依赖**:只用 `@gltf-transform/core` + `sharp`,无 Puppeteer、无 `headless-gl`
22
+
23
+ ---
24
+
25
+ ## 核心流程
26
+
27
+ ```
28
+ 参考图 → generate-3d(GLB) → 分析法线方向 → 计算对齐角 → 逐帧渲染 → 合并精灵图
29
+ ```
30
+
31
+ ### 步骤 1:生成 GLB(可选)
32
+
33
+ ```bash
34
+ playcraft tools generate-3d \
35
+ --input <参考图.png> \
36
+ --output <输出目录> \
37
+ --name <模型名>
38
+ ```
39
+
40
+ 生成的 GLB 保存在输出目录下,同时输出预览图。
41
+
42
+ ### 步骤 2:分析 GLB 面法线,求精确对齐角
43
+
44
+ 这是最容易踩坑的一步。HunyuanTo3D 等模型生成服务产出的 GLB **不一定** 以标准姿态朝向摄像机,必须从法线数据反推对齐旋转角。
45
+
46
+ ```javascript
47
+ // 分析主面(Y 方向三角形最多)的平均法线
48
+ const doc = await (new NodeIO()).read(glbPath);
49
+ let sumN = {x:0, y:0, z:0, n:0};
50
+ for (const mesh of doc.getRoot().listMeshes()) {
51
+ for (const prim of mesh.listPrimitives()) {
52
+ const normA = prim.getAttribute('NORMAL');
53
+ const idxA = prim.getIndices();
54
+ if (!normA) continue;
55
+ const norms = normA.getArray();
56
+ const idx = idxA?.getArray();
57
+ const count = idx ? idx.length/3 : norms.length/9;
58
+ for (let t=0; t<count; t++) {
59
+ const i0 = idx ? idx[t*3] : t*3;
60
+ const ny = norms[i0*3+1];
61
+ // 找朝向相机的那一面(Y 方向分量最显著)
62
+ if (ny < -0.5) { sumN.x+=norms[i0*3]; sumN.y+=ny; sumN.z+=norms[i0*3+2]; sumN.n++; }
63
+ }
64
+ }
65
+ }
66
+ const avgN = normalize(sumN); // 例如 (0, -0.944, 0.330)
67
+ ```
68
+
69
+ 得到主面平均法线 `avgN = (nx, ny, nz)` 后,求让该法线精确指向相机 (+Z) 的 RX 角:
70
+
71
+ ```
72
+ // 目标:rotX(avgN, RX) = (0, 0, 1)
73
+ // 方程组:ny*cos(RX) - nz*sin(RX) = 0
74
+ // ny*sin(RX) + nz*cos(RX) = 1 ← 只有一个解在正确象限
75
+ // 解:tan(RX) = ny/nz (取 z'=1 的那个解)
76
+ const RX_ALIGN = Math.atan2(-avgN.z, avgN.y); // ≈ -1.234 rad for (0,-0.944,0.330)
77
+ ```
78
+
79
+ ### 步骤 3:确定 RX_INIT 和翻转方向
80
+
81
+ GLB 模型有两个关键对齐角:
82
+
83
+ | 情况 | RX_INIT | ry 范围 | 帧 0 显示 |
84
+ |------|---------|---------|----------|
85
+ | 绿背面(或背面)先出现,翻到正面 | `RX_ALIGN + π` | `π → 0` | 背面 |
86
+ | 正面先出现(不需要翻转揭示效果) | `RX_ALIGN` | `0 → π` | 正面 |
87
+
88
+ > **关键**:用 `RX_ALIGN + π` 时,正面在 ry=0 处正对摄像机,且字符**上下方向正确**。
89
+ > 反之 `RX_ALIGN` 会让背面先出现,字符上下颠倒。
90
+
91
+ 翻转方向代码:
92
+
93
+ ```javascript
94
+ // 翻转:背面 → 正面(推荐)
95
+ const ry = Math.PI - (i / (FRAMES-1)) * Math.PI; // π → 0
96
+
97
+ // 翻转:正面 → 背面
98
+ const ry = (i / (FRAMES-1)) * Math.PI; // 0 → π
99
+ ```
100
+
101
+ ### 步骤 4:渲染循环与 UV 镜像修正
102
+
103
+ **最重要的坑**:GLB 正面在屏幕空间的三角形绕序(Winding)会变成顺时针(CW),必须在 UV 采样时做 U 轴镜像修正,否则贴图中的文字/图案会水平镜像。
104
+
105
+ ```javascript
106
+ // 屏幕空间有向面积(正=CCW,负=CW)
107
+ const area2 = (p1.sx-p0.sx)*(p0.sy-p2.sy) - (p0.sx-p2.sx)*(p1.sy-p0.sy);
108
+ const isMirrored = area2 < 0;
109
+
110
+ // U 轴镜像:翻转后的面需要翻 U,保证文字方向正确
111
+ const uSample = isMirrored ? 1 - uI : uI;
112
+ const vSample = vI; // V 不动,全局不翻转
113
+
114
+ // 重心坐标符号同步翻转
115
+ const sign = isMirrored ? -1 : 1;
116
+ const w0 = sign * (...) * invArea;
117
+ ```
118
+
119
+ **坑:不要全局翻转 V**。GLB UV 图集的 V=0 在图像顶部,与图像坐标一致,全局翻 V 会破坏图集中所有面片的纹理。
120
+
121
+ ### 步骤 5:Phong 光照参数参考
122
+
123
+ ```javascript
124
+ const LIGHT = normalize(v3(0.3, 0.8, 1.0)); // 右上前方光源
125
+ const VIEW_DIR = v3(0, 0, 1);
126
+ const AMBIENT = 0.32;
127
+ const DIFFUSE = 0.60;
128
+ const SPECULAR = 0.18;
129
+ const SHININESS = 48;
130
+
131
+ // 着色
132
+ const NdL = Math.max(0, dot(nN, LIGHT));
133
+ const half = normalize(add(LIGHT, VIEW_DIR));
134
+ const NdH = Math.max(0, dot(nN, half));
135
+ const light = AMBIENT + DIFFUSE * NdL;
136
+ const spec = SPECULAR * Math.pow(NdH, SHININESS);
137
+ ```
138
+
139
+ ---
140
+
141
+ ## 完整渲染器模板
142
+
143
+ 完整可运行的渲染器参见项目内:
144
+
145
+ ```
146
+ /tmp/mahjong-tile/ ← 示例输出
147
+ packages/cli/tmp/glb-render/render.mjs ← 完整实现(可复制复用)
148
+ ```
149
+
150
+ 主要参数一览:
151
+
152
+ ```javascript
153
+ const GLB_PATH = 'your_model.glb';
154
+ const OUT_PATH = 'tile_flip_sprite.png';
155
+ const FRAMES = 16; // 精灵图总帧数
156
+ const COLS = 8; // 每行列数(ROWS = FRAMES/COLS)
157
+ const W = 280, H = 280; // 每帧像素尺寸
158
+ const SS = 2; // 超采样倍数(2 = 4× SSAA)
159
+ const FOCAL = 2.8; // 透视焦距(越大越"长焦")
160
+ const CAMERA_Z = 3.2; // 相机 Z 距离
161
+ ```
162
+
163
+ ---
164
+
165
+ ## 常见问题与解法
166
+
167
+ ### 1. 牌/模型倾斜歪了
168
+
169
+ **原因**:直接用 RX=-π/2 是经验值,GLB 主面法线往往带有额外倾斜(如 14.9°)。
170
+ **解法**:按步骤 2 从法线数据解析精确 `RX_INIT`,不要猜。
171
+
172
+ ### 2. 字符水平镜像(字反了)
173
+
174
+ **原因**:Y 轴 180° 旋转后,三角形在屏幕空间变成 CW 绕序,UV 的 U 方向随之镜像。
175
+ **解法**:检测 `area2 < 0`,对这类三角形令 `uSample = 1 - uI`(见步骤 4)。
176
+
177
+ ### 3. 字符上下颠倒(一在下,万在上)
178
+
179
+ **原因**:错误的 `RX_INIT` 导致模型 -Z 端(字符"上"端)被映射到世界 -Y(屏幕下方)。
180
+ **解法**:使用 `RX_INIT = RX_ALIGN + π`,并将翻转方向改为 `ry: π → 0`(见步骤 3)。
181
+
182
+ ### 4. 全局翻 V 导致纹理全部乱掉
183
+
184
+ **原因**:HunyuanTo3D 输出的是 UV Atlas,不同面片共享同一纹理坐标空间,全局翻 V 会同时破坏所有面片。
185
+ **解法**:V 坐标不做任何修改,只修正 U(见步骤 4)。
186
+
187
+ ### 5. Puppeteer / headless-gl 编译失败
188
+
189
+ **原因**:`headless-gl` 依赖 C++ 原生编译,macOS / Node 22 环境下经常遭遇 C++20 或 `std::auto_ptr` 废弃错误。
190
+ **解法**:使用本 skill 的纯 JS 软件光栅化方案,不引入任何原生依赖。
191
+
192
+ ---
193
+
194
+ ## 整副麻将批量生成:白模 + 贴图分离架构
195
+
196
+ ### 问题
197
+
198
+ HunyuanTo3D 生成的 GLB **把字符形状烘焙进了 3D 几何体**(实际的浮雕顶点),不只是贴图。
199
+ 如果直接用这个 GLB 替换贴图,会看到原始 3D 字符几何体"透底"——新贴图和旧几何体叠加。
200
+
201
+ ### 正确架构
202
+
203
+ ```
204
+ 白模 GLB(平面牌面,无字符几何)
205
+ + 牌面图 × N(一萬/二萬/.../九條/中/發/白...)
206
+ = N 套精灵图
207
+ ```
208
+
209
+ ### 步骤:制作白模 GLB(两种方案)
210
+
211
+ #### 方案 A(推荐):刮平现有 GLB(快,零成本)
212
+
213
+ HunyuanTo3D 生成的 GLB 把字符形状烘焙进了正面顶点(浮雕几何)。
214
+ 只需用 `flatten_glb.mjs` 把正面顶点统一到同一 Y 值即可:
215
+
216
+ ```bash
217
+ # 把 一萬.glb 的正面浮雕刮平 → 白模
218
+ node renderer/flatten_glb.mjs \
219
+ tile_1wan.glb \
220
+ tile_base_flat.glb \
221
+ 0.7 # 法线 Y 阈值,正面顶点 normalY > 0.7 才被刮平
222
+ ```
223
+
224
+ 原理:找所有 `normalY > threshold` 的顶点(即正面),强制将 Y 坐标统一设为最大值。
225
+ 时间:< 1 秒,零 API 消耗。
226
+
227
+ #### 方案 B:用空白图重新生成 GLB
228
+
229
+ 当现有 GLB 的侧面/边缘纹理也需要替换时(如更换主题风格),
230
+ 用空白参考图重新调用 HunyuanTo3D:
231
+
232
+ ```bash
233
+ # 先生成纯白底参考图(可程序化生成,不消耗 image credits)
234
+ node -e "
235
+ require('sharp')({
236
+ create:{width:512,height:640,channels:4,background:{r:245,g:240,b:228,alpha:255}}
237
+ }).composite([{
238
+ input: Buffer.from('<svg xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"512\" height=\"640\" rx=\"40\" fill=\"none\" stroke=\"#3a6b48\" stroke-width=\"12\"/></svg>'),
239
+ }]).png().toFile('blank_tile_ref.png');
240
+ "
241
+
242
+ # 生成白模 GLB
243
+ playcraft tools generate-3d \
244
+ --input blank_tile_ref.png \
245
+ --output ./base \
246
+ --name tile_base
247
+ ```
248
+
249
+ ### 步骤:生成各牌面图
250
+
251
+ ```bash
252
+ # 一萬 到 九萬
253
+ for n in 一 二 三 四 五 六 七 八 九; do
254
+ playcraft image generate \
255
+ --prompt "${n}万 mahjong tile face, flat illustration, red characters on white" \
256
+ --output /tmp/faces/${n}wan.png
257
+ done
258
+
259
+ # 或用 SVG 程序化生成(推荐,精度高且不消耗 AI credits)
260
+ ```
261
+
262
+ ### 步骤:批量渲染
263
+
264
+ ```bash
265
+ # 同一白模 GLB + 不同牌面图 → 不同精灵图
266
+ for n in 一 二 三 四 五 六 七 八 九; do
267
+ node render.mjs \
268
+ /tmp/mahjong-base/tile_base.glb \
269
+ /tmp/sprites/${n}wan_sprite.png \
270
+ /tmp/faces/${n}wan.png
271
+ done
272
+ ```
273
+
274
+ > ⚡ 3 张牌并行约 2.5 秒,全套 34 种牌型理论 < 30 秒(8 并发时)
275
+
276
+ ### 面贴图的 UV 映射说明
277
+
278
+ render.mjs 自动从法线检测正面三角形(`normalY > 0.7`),用模型空间 XZ 坐标生成 UV:
279
+
280
+ ```
281
+ faceU = (x - faceXMin) / faceXRange → 牌面左边=0, 右边=1
282
+ faceV = (z - faceZMin) / faceZRange → 一端(顶)=0, 万端(底)=1
283
+ ```
284
+
285
+ 牌面图只需保证:**字符上边 = 图像顶部,字符下边 = 图像底部**,直接映射,无需调 UV。
286
+
287
+ ### 渲染器 CLI
288
+
289
+ ```bash
290
+ node render.mjs <glb> <output.png> [face-texture.png]
291
+ #
292
+ # 不传第三个参数 → 使用 GLB 原始 UV Atlas(调试/单张使用)
293
+ # 传入第三个参数 → 正面使用该贴图,侧面/背面仍用 GLB Atlas
294
+ ```
295
+
296
+ ---
297
+
298
+ ## 生成的精灵图如何在前端使用
299
+
300
+ ```javascript
301
+ // Canvas 播放示例
302
+ const SHEET = './tile_flip_sprite.png';
303
+ const FRAMES = 16, COLS = 8;
304
+ const FW = 280, FH = 280; // 原始每帧尺寸
305
+ const DW = 120, DH = 120; // 显示尺寸
306
+
307
+ const img = new Image();
308
+ img.src = SHEET;
309
+
310
+ function drawFrame(ctx, frame) {
311
+ const col = frame % COLS;
312
+ const row = Math.floor(frame / COLS);
313
+ ctx.drawImage(img, col*FW, row*FH, FW, FH, 0, 0, DW, DH);
314
+ }
315
+
316
+ // 翻牌动画:
317
+ let progress = 0;
318
+ function flip() {
319
+ requestAnimationFrame(() => {
320
+ drawFrame(ctx, Math.round(progress));
321
+ progress += speed;
322
+ if (progress < FRAMES - 1) flip();
323
+ });
324
+ }
325
+ ```
326
+
327
+ ---
328
+
329
+ ## 与其他 Skill 的关系
330
+
331
+ | Skill | 关系 |
332
+ |-------|------|
333
+ | `playcraft-sprite-sheet` | 多张平面图合并为精灵图;本 skill 专注 3D 模型渲染 |
334
+ | `sprite-animation-2d` | PlayCanvas 中播放精灵图动画的代码模板 |
335
+ | `sound-effects` | 翻牌时配合 `card_flip.mp3` 音效 |
336
+ | `playcraft-image-generation` | 生成牌面贴图后,再走 generate-3d → 本 skill |
@@ -0,0 +1,62 @@
1
+ /**
2
+ * GLB 正面刮平工具
3
+ *
4
+ * 作用:把 HunyuanTo3D 生成的字符 GLB 的正面几何体"刮平",
5
+ * 消除烘焙在顶点中的浮雕字符,得到适合贴图替换的白模。
6
+ *
7
+ * Usage:
8
+ * node flatten_glb.mjs <input.glb> <output_flat.glb> [threshold=0.7]
9
+ *
10
+ * 原理:
11
+ * 找所有法线 Y > threshold 的顶点(正面顶点),
12
+ * 强制将它们的 Y 坐标统一设为正面最大 Y 值(最高点 = 完全平坦)。
13
+ */
14
+ import { NodeIO } from '@gltf-transform/core';
15
+
16
+ const INPUT = process.argv[2];
17
+ const OUTPUT = process.argv[3];
18
+ const THRESHOLD = parseFloat(process.argv[4] ?? '0.7');
19
+
20
+ if (!INPUT || !OUTPUT) {
21
+ console.error('Usage: node flatten_glb.mjs <input.glb> <output.glb> [threshold=0.7]');
22
+ process.exit(1);
23
+ }
24
+
25
+ const io = new NodeIO();
26
+ const doc = await io.read(INPUT);
27
+
28
+ // Pass 1: find max Y of face vertices (highest point of the front face)
29
+ let maxFaceY = -Infinity;
30
+ for (const mesh of doc.getRoot().listMeshes()) {
31
+ for (const prim of mesh.listPrimitives()) {
32
+ const posA = prim.getAttribute('POSITION');
33
+ const normA = prim.getAttribute('NORMAL');
34
+ if (!posA || !normA) continue;
35
+ const pos = posA.getArray();
36
+ const norms = normA.getArray();
37
+ for (let i = 0; i < pos.length / 3; i++) {
38
+ if (norms[i * 3 + 1] > THRESHOLD) maxFaceY = Math.max(maxFaceY, pos[i * 3 + 1]);
39
+ }
40
+ }
41
+ }
42
+ console.log(`Face normal threshold: ${THRESHOLD} | max face Y: ${maxFaceY.toFixed(5)}`);
43
+
44
+ // Pass 2: flatten all face vertices to maxFaceY
45
+ let count = 0;
46
+ for (const mesh of doc.getRoot().listMeshes()) {
47
+ for (const prim of mesh.listPrimitives()) {
48
+ const posA = prim.getAttribute('POSITION');
49
+ const normA = prim.getAttribute('NORMAL');
50
+ if (!posA || !normA) continue;
51
+ const pos = posA.getArray();
52
+ const norms = normA.getArray();
53
+ for (let i = 0; i < pos.length / 3; i++) {
54
+ if (norms[i * 3 + 1] > THRESHOLD) { pos[i * 3 + 1] = maxFaceY; count++; }
55
+ }
56
+ posA.setArray(new Float32Array(pos));
57
+ }
58
+ }
59
+ console.log(`Flattened ${count.toLocaleString()} face vertices`);
60
+
61
+ await io.write(OUTPUT, doc);
62
+ console.log(`Saved → ${OUTPUT}`);