arcade-ai 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,30 +1,154 @@
1
1
  # arcade-ai
2
2
 
3
- AI-driven scaffold and live studio for Microsoft MakeCode Arcade games.
4
-
5
- ## 安装与集成方式 (Installation & Integration)
6
-
7
- ### 1. Claude Code 插件市场 (Claude Code plugin marketplace)
8
- 你可以在 Claude Code plugin marketplace 中搜索并安装 `arcade-ai`。安装后即可在与 AI 对话时使用对应的技能,帮助生成和实时验证 MakeCode Arcade 游戏代码。
9
-
10
- ### 2. Trae 智能辅助工具 (.trae)
11
- 如果你使用 Trae,请将技能配置添加到 `.trae` 相关的规则路径中。初始化脚手架时通过指定 `--tool trae` 会在项目根目录下自动创建 `.trae/project_rules.md`。
12
-
13
- ### 3. AI 代理常规配置 (AGENTS.md)
14
- 对于通用的 AI 代理(如 Cline Cursor 等),会在初始化时生成 `AGENTS.md` 文件。该文件定义了只修改 `game/main.ts`、严守 MakeCode Arcade 4 个约束、不可臆造 API 等核心规则,请确保将此规则导入你的 AI Agent 配置中。
15
-
16
- ## 使用方法 (Usage)
17
-
18
- 1. **项目初始化**:
19
- ```bash
20
- npx aca init <your-game-dir> [--tool claude|trae|agents]
21
- ```
22
- 2. **启动本地 Studio 双向实时预览服务**:
23
- ```bash
24
- cd <your-game-dir>
25
- npx aca dev
26
- ```
27
- 3. **自检 postMessage 协议状态**:
28
- ```bash
29
- npx aca check
30
- ```
3
+ AI-driven scaffold and live studio for Microsoft MakeCode Arcade games. It ships as:
4
+
5
+ - a reusable skill (`SKILL.md` + `reference/`) that tells an AI agent how to build Arcade games safely;
6
+ - an npm CLI (`aca`) that creates projects, runs a local live studio, and checks the MakeCode editor protocol.
7
+
8
+ ## What It Does
9
+
10
+ - Generates a pure TypeScript MakeCode Arcade project.
11
+ - Adds the right AI rule file for your tool: `CLAUDE.md`, `.trae/project_rules.md`, or `AGENTS.md`.
12
+ - Starts a local studio that embeds the official MakeCode Arcade editor.
13
+ - Keeps `game/main.ts` and Arcade resource files synchronized with the editor.
14
+ - Provides an offline reference for Arcade APIs, limits, project format, and known pitfalls.
15
+
16
+ ## Install The CLI
17
+
18
+ Use it directly with `npx`:
19
+
20
+ ```bash
21
+ npx arcade-ai init my-game --tool agents
22
+ cd my-game
23
+ npx aca dev
24
+ ```
25
+
26
+ If the npm package has not been published yet, run it from GitHub:
27
+
28
+ ```bash
29
+ npx --yes github:guolin/arcade-ai init my-game --tool agents
30
+ cd my-game
31
+ npx --yes github:guolin/arcade-ai dev
32
+ ```
33
+
34
+ Or install it in a project:
35
+
36
+ ```bash
37
+ npm install -D arcade-ai
38
+ npx aca init my-game --tool claude
39
+ ```
40
+
41
+ ## Commands
42
+
43
+ ```bash
44
+ aca init [dir] [--tool claude|trae|agents]
45
+ aca dev [--port 8080]
46
+ aca check [--url <makecode-url>]
47
+ ```
48
+
49
+ - `aca init` creates the Arcade project, copies `reference/`, and writes the tool-specific rule file.
50
+ - `aca dev` runs the local studio with live preview and editor-to-disk synchronization.
51
+ - `aca check` verifies the MakeCode editor handshake and rendering path. This needs network access and Puppeteer.
52
+
53
+ ## Install The Skill
54
+
55
+ The skill is the repo root: `SKILL.md` plus the `reference/` directory. Keep those together when installing.
56
+
57
+ ### Workbudy
58
+
59
+ Workbudy should start from the dedicated guide, not only the repository homepage:
60
+
61
+ ```text
62
+ https://github.com/guolin/arcade-ai/blob/main/WORKBUDDY.md
63
+ ```
64
+
65
+ That page points Workbudy to the raw `SKILL.md` and every file under `reference/`. It also gives a prompt you can paste directly into Workbudy.
66
+
67
+ Until the npm package is published, create projects from GitHub:
68
+
69
+ ```bash
70
+ npx --yes github:guolin/arcade-ai init my-game --tool agents
71
+ ```
72
+
73
+ After npm publishing, this shorter command is equivalent:
74
+
75
+ ```bash
76
+ npx arcade-ai init my-game --tool agents
77
+ ```
78
+
79
+ Then make sure Workbudy reads:
80
+
81
+ - `my-game/AGENTS.md`
82
+ - `my-game/reference/`
83
+
84
+ In Workbudy prompts, ask it to follow `AGENTS.md` and use `reference/` before writing Arcade APIs it is unsure about. Run the studio with:
85
+
86
+ ```bash
87
+ cd my-game
88
+ npx aca dev
89
+ ```
90
+
91
+ ### Codex
92
+
93
+ For Codex, install the skill into your Codex skills directory:
94
+
95
+ ```bash
96
+ mkdir -p ~/.codex/skills
97
+ git clone <this-repo-url> ~/.codex/skills/arcade-ai
98
+ ```
99
+
100
+ Restart Codex after installation so it can discover the new skill. To create a project with Codex-friendly rules:
101
+
102
+ ```bash
103
+ npx arcade-ai init my-game --tool agents
104
+ ```
105
+
106
+ Codex should then read `AGENTS.md` in the generated project and use the installed `arcade-ai` skill whenever you ask it to build or modify a MakeCode Arcade game.
107
+
108
+ ### Claude Code
109
+
110
+ Use the Claude Code plugin marketplace when this repo is published as a plugin:
111
+
112
+ ```text
113
+ /plugin marketplace
114
+ ```
115
+
116
+ Search for `arcade-ai`, install it, then restart or open a new Claude Code session if prompted.
117
+
118
+ For a manual local install, copy or clone this repo as a skill:
119
+
120
+ ```bash
121
+ mkdir -p ~/.claude/skills
122
+ git clone <this-repo-url> ~/.claude/skills/arcade-ai
123
+ ```
124
+
125
+ Create Claude-specific project rules with:
126
+
127
+ ```bash
128
+ npx arcade-ai init my-game --tool claude
129
+ ```
130
+
131
+ Claude Code will use `CLAUDE.md` in the generated project plus the installed `arcade-ai` skill.
132
+
133
+ ## Typical Workflow
134
+
135
+ ```bash
136
+ npx arcade-ai init space-runner --tool claude
137
+ cd space-runner
138
+ npx aca dev
139
+ ```
140
+
141
+ Then ask your AI agent to edit `game/main.ts`. Keep the studio open while the agent works; preview refreshes automatically.
142
+
143
+ ## Rules For AI Agents
144
+
145
+ - Only write game code in `game/main.ts`.
146
+ - Store resources in `game/assets.json`; do not inline large art assets into JavaScript.
147
+ - Keep the project TypeScript-only: do not add `main.blocks`.
148
+ - Use named tilemaps or built-in tiles for maps.
149
+ - Read `reference/arcade-api.md`, `reference/limits.md`, and `reference/pitfalls.md` when unsure.
150
+ - Do not invent MakeCode Arcade APIs.
151
+
152
+ ## License
153
+
154
+ MIT
package/SKILL.md CHANGED
@@ -12,12 +12,27 @@ description: Use when building or modifying a Microsoft MakeCode Arcade game —
12
12
  - 用户想做 / 改 arcade 游戏、像素小游戏、makecode 游戏时。
13
13
 
14
14
  ## 怎么用(三个命令)
15
- 1. 起项目:`npx arcade-ai init <dir> [--tool claude|trae|agents]`
16
- —— 生成纯 TS 脚手架,并把规则文件与 `reference/` 一并拷进项目。
15
+ 1. 起项目:`npx arcade-ai init <dir> [--template blank|platformer|flappy] [--tool claude|trae|agents]`
16
+
17
+ **执行 init 前,先问用户想做哪种游戏,根据回答选模板:**
18
+ - `--template blank`(**默认**):空白项目,只有最简单的精灵和地图引用,适合从零开始。
19
+ - `--template platformer`:横版平台跳跃,含地图、敌人、重力跳跃,推荐有明确玩法时选。
20
+ - `--template flappy`:Flappy Bird 风格,管道障碍 + 重力下坠,推荐单一机制练习。
21
+
22
+ 生成纯 TS 脚手架,并把规则文件与 `reference/` 一并拷进项目。
17
23
  2. 起 studio:在项目目录 `npx aca dev` —— 浏览器实时预览,AI 改 `game/main.ts` 自动刷新;
18
24
  在编辑器里改代码/画精灵也会回写磁盘(双向)。
19
25
  3. 协议自检:`npx aca check` —— 验证官方编辑器握手 + 代码真渲染(联网,需 puppeteer)。
20
26
 
27
+ ## 接手项目时必须先读文档
28
+ 拿到一个 arcade 项目(或准备写代码)时,**第一步读以下文件,不要跳过**:
29
+ - `reference/arcade-api.md` —— 有哪些 API、怎么用
30
+ - `reference/pitfalls.md` —— 哪些写法会翻车(踩坑成本极高)
31
+ - `reference/limits.md` —— 内存/尺寸/功能硬限制
32
+ - `reference/project-format.md` —— 文件格式约定(pxt.json / assets.json)
33
+
34
+ 读完再动手,遇到不确认的 API 回来查,不要靠记忆臆造。
35
+
21
36
  ## 写代码硬约束(违反会翻车)
22
37
  - 代码只写 `game/main.ts`;资源(精灵/地图,4-bit 16 色)走 `game/assets.json`,不内联大图到 JS。
23
38
  - **纯 TS 项目**:`game/` 里不要 `main.blocks`,`pxt.json` 的 `files` 也不列它,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcade-ai",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "AI-driven scaffold and live studio for Microsoft MakeCode Arcade games",
5
5
  "keywords": [
6
6
  "makecode",
@@ -85,6 +85,8 @@
85
85
  ## 6. 游戏信息 (Info)
86
86
  处理分数、生命值和计时器。
87
87
 
88
+ > **重要**:以下 API 调用后,Arcade HUD 会**自动在屏幕角落渲染分数、心形血条、倒计时**,无需写任何显示代码。不要用 `game.showLongText` 或自定义 sprite 手搓 UI——那是重复造轮子。
89
+
88
90
  - **设置/修改分数**:
89
91
  `info.setScore(0)`
90
92
  `info.changeScoreBy(10)`
@@ -95,6 +97,23 @@
95
97
  `info.startCountdown(30)` (30秒)
96
98
  `info.onCountdownEnd(function() { game.over(false) })`
97
99
 
100
+ ## 6b. 持久化存储与历史最高分 (Settings)
101
+ MakeCode Arcade 提供 `settings` 命名空间,数据持久化到设备 flash(浏览器模拟器对应 localStorage)。
102
+
103
+ ```typescript
104
+ // 历史最高分示例
105
+ const best = settings.readNumber("highScore") || 0;
106
+ if (info.score() > best) {
107
+ settings.writeNumber("highScore", info.score());
108
+ }
109
+ ```
110
+
111
+ - `settings.writeNumber(key, value)` / `settings.readNumber(key)`
112
+ - `settings.writeString(key, value)` / `settings.readString(key)`
113
+ - `settings.exists(key)` / `settings.remove(key)`
114
+
115
+ > **没有内置排行榜 UI**:`settings` 只是 key-value 存储,多名玩家排行榜需要自己用 sprite/text 实现显示逻辑。
116
+
98
117
  ## 7. 地图 Tilemap(两条可靠路线,外加一个会崩的禁区)
99
118
 
100
119
  > ⚠️ **禁区**:`tiles.createTilemap(hex`...`, img`...`, [内联 img tile], ...)` —— **把内联 `img` 当图块会让官方编辑器崩溃**(Oops 重载循环)。编辑器的 tilemap 解析器只接受“命名图块资源”,不接受随手写的内联 img。已实测确认。详见 pitfalls 坑5。
package/src/cli.js CHANGED
@@ -9,6 +9,7 @@ export function parseArgs(argv) {
9
9
  strict: false,
10
10
  options: {
11
11
  tool: { type: 'string' },
12
+ template: { type: 'string' },
12
13
  port: { type: 'string' },
13
14
  url: { type: 'string' },
14
15
  },
@@ -19,9 +20,9 @@ export function parseArgs(argv) {
19
20
 
20
21
  const HELP = `arcade-ai (aca)
21
22
  用法:
22
- aca init [dir] [--tool claude|trae|agents] 起脚手架
23
- aca dev [--port 8080] 起本地 studio
24
- aca check [--url <makecode-url>] 自检 postMessage 协议`;
23
+ aca init [dir] [--template blank|platformer|flappy] [--tool claude|trae|agents] 起脚手架(默认: blank)
24
+ aca dev [--port 8080] 起本地 studio
25
+ aca check [--url <makecode-url>] 自检 postMessage 协议`;
25
26
 
26
27
  export async function run(argv) {
27
28
  const { command, positionals, options } = parseArgs(argv);
@@ -21,14 +21,25 @@ function writeRules(dest, tool) {
21
21
  writeFileSync(target, content, 'utf8');
22
22
  }
23
23
 
24
+ const TEMPLATES = ['platformer', 'flappy', 'blank'];
25
+ const DEFAULT_TEMPLATE = 'blank';
26
+
24
27
  export default async function init(ctx) {
25
28
  const dest = ctx.positionals[0] || 'arcade-game';
29
+ const tpl = ctx.options.template || DEFAULT_TEMPLATE;
30
+
31
+ if (!TEMPLATES.includes(tpl)) {
32
+ console.error(`未知模板: ${tpl},可选: ${TEMPLATES.join(', ')}`);
33
+ return 1;
34
+ }
35
+
26
36
  mkdirSync(dest, { recursive: true });
27
- cpSync(join(templateDir, 'game'), join(dest, 'game'), { recursive: true });
37
+ // 每个模板是完整的 game/ 目录,直接拷贝
38
+ cpSync(join(templateDir, tpl, 'game'), join(dest, 'game'), { recursive: true });
28
39
  copyFileSync(join(templateDir, 'package.json'), join(dest, 'package.json'));
29
40
  // 把知识库拷进项目,使其自包含、跨工具可查(Trae/其它 AI 裸读项目也能用)
30
41
  cpSync(referenceDir, join(dest, 'reference'), { recursive: true });
31
42
  writeRules(dest, ctx.options.tool);
32
- console.log(`✅ 已创建 ${dest}\n cd ${dest} && npx aca dev`);
43
+ console.log(`✅ 已创建 ${dest}(模板: ${tpl})\n cd ${dest} && npx aca dev`);
33
44
  return 0;
34
45
  }
@@ -1,11 +1,22 @@
1
1
  import { createServer } from 'node:http';
2
+ import { createHash } from 'node:crypto';
2
3
  import { readFileSync } from 'node:fs';
3
4
  import { readProject, writeProject } from '../project-io.js';
4
5
  import { createWatcher } from './watcher.js';
5
6
 
7
+ function hashFiles(files) {
8
+ const h = createHash('sha1');
9
+ for (const k of Object.keys(files).sort()) h.update(k + '\0' + (files[k] ?? ''));
10
+ return h.digest('hex');
11
+ }
12
+
6
13
  export function startStudio({ gameDir, port = 0, hostHtmlPath }) {
7
14
  const clients = new Set();
15
+ let lastAiWriteAt = 0; // AI 写盘时刻
16
+ let lastPushedHash = ''; // 最后一次推给编辑器的内容 hash
17
+
8
18
  const watcher = createWatcher(gameDir, () => {
19
+ lastAiWriteAt = Date.now();
9
20
  for (const res of clients) res.write('data: changed\n\n');
10
21
  });
11
22
 
@@ -15,16 +26,38 @@ export function startStudio({ gameDir, port = 0, hostHtmlPath }) {
15
26
  res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
16
27
  res.end(readFileSync(hostHtmlPath));
17
28
  } else if (req.method === 'GET' && url === '/api/project') {
29
+ const files = readProject(gameDir);
30
+ lastPushedHash = hashFiles(files);
18
31
  res.writeHead(200, { 'content-type': 'application/json' });
19
- res.end(JSON.stringify({ files: readProject(gameDir) }));
32
+ res.end(JSON.stringify({ files }));
20
33
  } else if (req.method === 'POST' && url === '/api/save') {
21
34
  let body = '';
22
35
  req.on('data', (c) => (body += c));
23
36
  req.on('end', () => {
24
37
  try {
25
38
  const { files } = JSON.parse(body || '{}');
39
+
40
+ // 时间层:AI 写盘后 2 秒内的 save 是旧 iframe 临死前的遗留,丢弃
41
+ const msSinceAiWrite = Date.now() - lastAiWriteAt;
42
+ if (lastAiWriteAt > 0 && msSinceAiWrite < 2000) {
43
+ console.log(`[save] 忽略(AI 写盘后 ${msSinceAiWrite}ms,旧 iframe 遗留)`);
44
+ res.writeHead(200, { 'content-type': 'application/json' });
45
+ res.end(JSON.stringify({ success: true }));
46
+ return;
47
+ }
48
+
49
+ // 内容层:编辑器把我们推出去的内容原样还回来(echo),跳过落盘
50
+ const incomingHash = hashFiles(files || {});
51
+ if (incomingHash === lastPushedHash) {
52
+ console.log('[save] 忽略(内容与上次推送一致,echo)');
53
+ res.writeHead(200, { 'content-type': 'application/json' });
54
+ res.end(JSON.stringify({ success: true }));
55
+ return;
56
+ }
57
+
26
58
  watcher.pause(600);
27
59
  const written = writeProject(gameDir, files);
60
+ lastPushedHash = incomingHash;
28
61
  const incoming = Object.keys(files || {});
29
62
  const dropped = incoming.filter((f) => !written.includes(f));
30
63
  console.log(`[save] 收到 ${incoming.length} 文件,落盘 ${written.length}`);
@@ -0,0 +1 @@
1
+ # Blank Game
@@ -0,0 +1 @@
1
+ # Flappy Bird Game
File without changes
@@ -0,0 +1,18 @@
1
+ {
2
+ "myImages.image1": {
3
+ "id": "myImages.image1",
4
+ "data": "hwQQABAAAAAAAAAAu8sMAAAAAAC93csAAAAAAN3c3QwAAAAAXcXdywAAsLtb1dzNAAC7VVtV3M0AsFtVvVXbzQCw1dFVvdXNALAV/1VVVc2wu/W/RVVVxVu8VdVEVVXFtbBV3URVVbULANtERFVVuwAA8ExEW9ULAAAAALSwuwAAAAAACwAAAA==",
5
+ "dataEncoding": "base64",
6
+ "namespace": "myImages.",
7
+ "mimeType": "image/x-mkcd-f4",
8
+ "displayName": "playerImage"
9
+ },
10
+ "anim1": {
11
+ "id": "anim1",
12
+ "data": "YzgwMDEwMDAxMDAwMDYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBiMDU1MGIwMDAwMDAwMGJiYmJiYjAwMDAwMDAwYjA1YjU1NTUwYjAwYjBiYmJiNTU1NTU1YjUwMGIwNWQ1YjU1NTU1NWI1MDAwMDViYjVkNWYxZDVmNDAwMDBkYjU1MWJmZjQ1YzQwMGJiYmQ1NWQ1YmY0NDQ0MGJkYmNkNWRiNTQ1NDQ0NGI0ZGNkZGNjNWI1NTU1NTUwYmJjZGRkZDVkNTU1NTU1MGJjMGRkZGRkZDU1NTVkNTBiMDBiY2RkZGQ1ZDU1YmIwMDAwYzBjY2NjY2NiYzBiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDViMGIwMDAwMDAwMDAwYjBiNTAwMDAwMDAwMDBiYmJiYmIwMDAwMDAwMGIwNWI1NTU1MGIwMGIwYmJiYjU1NTU1NWI1MDBiMDVkNWI1NTU1NTViNTAwMDA1YmI1ZDVmMWQ1ZjQwMDAwZGI1NTFiZmY0NWM0MDBiYmJkNTVkNWJmNDQ0NGI0ZGJjZDVkYjU0NTQ0NDQwYmRjZGRjYzViNTU1NTU1MGJiY2RkZGQ1ZDU1NTU1NTBiYzBkZGRkZGQ1NTU1ZDUwYjAwYmNkZGRkNWQ1NWJiMDAwMGMwY2NjY2NjYmMwYjAwMDAwMDAwMDAwMDViMGIwMDAwMDAwMDAwYjBiNTAwMDAwMDAwMDAwMGIwMGMwMDAwMDAwMDAwYmJiYmJiMDAwMDAwMDBiMDViNTU1NTBiMDAwMDAwYmJkNWYxNTVmZDAwMDAwMDViMTVmZmQ1YzQwMDAwMDA1YmQ1YmZkZDQ0MDBkYmRkYmI1ZDU1NDQ0NGI0YmI1ZDU1NWI0NTQ0NDQwYmRiNWM1NWQ1NTU1NWI1MDBkY2NkNWRiNTU1NTU1NTBiYmNkZGNjNWI1NTU1NTUwYmMwZGRkZGRkNTU1NWQ1MGIwMGJjZGRkZDVkNTViYjAwMDBjMGNjY2NjY2JjMGIwMDAwMDAwMDAwMDA1YjBiMDAwMDAwMDAwMGIwYjUwMDAwMDAwMDAwYmJiYmJiMDAwMDAwMDBiMDViNTU1NTBiMDAwMDAwYmJkNWYxZDVjNDAwMDAwMDViMTVmZmRkNDRiNDAwMDA1YmQ1YmY0NDQ0MGIwMGIwNWQ1NTQ1NDRiNDAwMDBkYjVkNTU1NTU1YjUwMGIwZGRkZDU1NTU1NTU1MGJkYmRkYmI1YjU1NTU1NTBiZGNiZDU1Y2Q1NTU1NTUwYmJjZGJkNWRjNTU1NTU1MGJiMDU1Y2JkZDU1NTVkNTBiYmJjY2RjZGQ1ZDU1YmIwMDAwYzBjY2NjY2NiYzBiMDAwMDAwMDAwMDAwNWIwYjAwMDAwMDAwMDBiMGI1MDAwMDAwMDAwMGJiYmJiYjAwMDAwMDAwYjA1YjU1NTUwYjAwMDAwMGJiZDVmMWQ1YzQwMDAwMDA1YjE1ZmZkZDQ0YjQwMDAwNWJkNWJmNDQ0NDBiMDBiMDVkNTU0NTQ0YjQwMGIwZGJkZDU1NTU1NWI1MDBkYmRkYmI1YjU1NTU1NTBiZGNiZDU1Y2Q1NTU1NTUwYmJjZGJkNWRjNTU1NTU1MGJiYzU1Y2JkZDU1NTU1NTBiYmJjY2RjZGQ1NTU1ZDUwYjAwMDBjY2RkNWQ1NWJiMDAwMDAwMDBjY2NjYmMwYjAwMDAwMDAwMDAwMDViMGIwMDAwMDAwMDAwYjBiNTAwMDAwMDAwMDBiYmJiYmIwMDAwMDAwMGIwNWI1NTU1MGIwMDAwMDBiYmQ1ZjE1NWZkMDAwMDAwNWIxNWZmZDVjNDAwMDAwMDViZDViZmRkNDQwMGIwYmI1ZDU1NTU0NDQ0YjRkYmRkYmI1ZDQ1NDQ0NDBiYmI1ZDU1NWI1NTU1YjUwMGRjNWM1NWQ1NTU1NTU1MGJiY2NkNWRiNTU1NTU1NTBiYzBkZGNjZGI1NTU1ZDUwYjAwYmNkZGRkNWQ1NWJiMDAwMGMwY2NjY2NjYmMwYjAwMDAwMDAwMDAwMDAwMDAwMA==",
13
+ "dataEncoding": "base64",
14
+ "namespace": "myAnimations.",
15
+ "mimeType": "application/mkcd-animation",
16
+ "displayName": "playerAnimation"
17
+ }
18
+ }
@@ -0,0 +1,160 @@
1
+ // Auto-generated code. Do not edit.
2
+ namespace myImages {
3
+
4
+ helpers._registerFactory("image", function(name: string) {
5
+ switch(helpers.stringTrim(name)) {
6
+ case "image1":
7
+ case "playerImage":return img`
8
+ . . . . . . . . . . b 5 b . . .
9
+ . . . . . . . . . b 5 b . . . .
10
+ . . . . . . . . . b c . . . . .
11
+ . . . . . . b b b b b b . . . .
12
+ . . . . . b b 5 5 5 5 5 b . . .
13
+ . . . . b b 5 d 1 f 5 5 d f . .
14
+ . . . . b 5 5 1 f f 5 d 4 c . .
15
+ . . . . b 5 5 d f b d d 4 4 . .
16
+ b d d d b b d 5 5 5 4 4 4 4 4 b
17
+ b b d 5 5 5 b 5 5 4 4 4 4 4 b .
18
+ b d c 5 5 5 5 d 5 5 5 5 5 b . .
19
+ c d d c d 5 5 b 5 5 5 5 5 5 b .
20
+ c b d d c c b 5 5 5 5 5 5 5 b .
21
+ . c d d d d d d 5 5 5 5 5 d b .
22
+ . . c b d d d d d 5 5 5 b b . .
23
+ . . . c c c c c c c c b b . . .
24
+ `;
25
+ }
26
+ return null;
27
+ })
28
+
29
+ helpers._registerFactory("animation", function(name: string) {
30
+ switch(helpers.stringTrim(name)) {
31
+ case "playerAnimation":
32
+ case "anim1":return [img`
33
+ . . . . . . . . . . . . . . . .
34
+ . . . . . . . . . . . . . . . .
35
+ . . . . . . . . . b 5 5 b . . .
36
+ . . . . . . b b b b b b . . . .
37
+ . . . . . b b 5 5 5 5 5 b . . .
38
+ . b b b b b 5 5 5 5 5 5 5 b . .
39
+ . b d 5 b 5 5 5 5 5 5 5 5 b . .
40
+ . . b 5 5 b 5 d 1 f 5 d 4 f . .
41
+ . . b d 5 5 b 1 f f 5 4 4 c . .
42
+ b b d b 5 5 5 d f b 4 4 4 4 b .
43
+ b d d c d 5 5 b 5 4 4 4 4 4 4 b
44
+ c d d d c c b 5 5 5 5 5 5 5 b .
45
+ c b d d d d d 5 5 5 5 5 5 5 b .
46
+ . c d d d d d d 5 5 5 5 5 d b .
47
+ . . c b d d d d d 5 5 5 b b . .
48
+ . . . c c c c c c c c b b . . .
49
+ `, img`
50
+ . . . . . . . . . . . . . . . .
51
+ . . . . . . . . . . b 5 b . . .
52
+ . . . . . . . . . b 5 b . . . .
53
+ . . . . . . b b b b b b . . . .
54
+ . . . . . b b 5 5 5 5 5 b . . .
55
+ . b b b b b 5 5 5 5 5 5 5 b . .
56
+ . b d 5 b 5 5 5 5 5 5 5 5 b . .
57
+ . . b 5 5 b 5 d 1 f 5 d 4 f . .
58
+ . . b d 5 5 b 1 f f 5 4 4 c . .
59
+ b b d b 5 5 5 d f b 4 4 4 4 4 b
60
+ b d d c d 5 5 b 5 4 4 4 4 4 b .
61
+ c d d d c c b 5 5 5 5 5 5 5 b .
62
+ c b d d d d d 5 5 5 5 5 5 5 b .
63
+ . c d d d d d d 5 5 5 5 5 d b .
64
+ . . c b d d d d d 5 5 5 b b . .
65
+ . . . c c c c c c c c b b . . .
66
+ `, img`
67
+ . . . . . . . . . . b 5 b . . .
68
+ . . . . . . . . . b 5 b . . . .
69
+ . . . . . . . . . b c . . . . .
70
+ . . . . . . b b b b b b . . . .
71
+ . . . . . b b 5 5 5 5 5 b . . .
72
+ . . . . b b 5 d 1 f 5 5 d f . .
73
+ . . . . b 5 5 1 f f 5 d 4 c . .
74
+ . . . . b 5 5 d f b d d 4 4 . .
75
+ b d d d b b d 5 5 5 4 4 4 4 4 b
76
+ b b d 5 5 5 b 5 5 4 4 4 4 4 b .
77
+ b d c 5 5 5 5 d 5 5 5 5 5 b . .
78
+ c d d c d 5 5 b 5 5 5 5 5 5 b .
79
+ c b d d c c b 5 5 5 5 5 5 5 b .
80
+ . c d d d d d d 5 5 5 5 5 d b .
81
+ . . c b d d d d d 5 5 5 b b . .
82
+ . . . c c c c c c c c b b . . .
83
+ `, img`
84
+ . . . . . . . . . . b 5 b . . .
85
+ . . . . . . . . . b 5 b . . . .
86
+ . . . . . . b b b b b b . . . .
87
+ . . . . . b b 5 5 5 5 5 b . . .
88
+ . . . . b b 5 d 1 f 5 d 4 c . .
89
+ . . . . b 5 5 1 f f d d 4 4 4 b
90
+ . . . . b 5 5 d f b 4 4 4 4 b .
91
+ . . . b d 5 5 5 5 4 4 4 4 b . .
92
+ . . b d d 5 5 5 5 5 5 5 5 b . .
93
+ . b d d d d 5 5 5 5 5 5 5 5 b .
94
+ b d d d b b b 5 5 5 5 5 5 5 b .
95
+ c d d b 5 5 d c 5 5 5 5 5 5 b .
96
+ c b b d 5 d c d 5 5 5 5 5 5 b .
97
+ . b 5 5 b c d d 5 5 5 5 5 d b .
98
+ b b c c c d d d d 5 5 5 b b . .
99
+ . . . c c c c c c c c b b . . .
100
+ `, img`
101
+ . . . . . . . . . . b 5 b . . .
102
+ . . . . . . . . . b 5 b . . . .
103
+ . . . . . . b b b b b b . . . .
104
+ . . . . . b b 5 5 5 5 5 b . . .
105
+ . . . . b b 5 d 1 f 5 d 4 c . .
106
+ . . . . b 5 5 1 f f d d 4 4 4 b
107
+ . . . . b 5 5 d f b 4 4 4 4 b .
108
+ . . . b d 5 5 5 5 4 4 4 4 b . .
109
+ . b b d d d 5 5 5 5 5 5 5 b . .
110
+ b d d d b b b 5 5 5 5 5 5 5 b .
111
+ c d d b 5 5 d c 5 5 5 5 5 5 b .
112
+ c b b d 5 d c d 5 5 5 5 5 5 b .
113
+ c b 5 5 b c d d 5 5 5 5 5 5 b .
114
+ b b c c c d d d 5 5 5 5 5 d b .
115
+ . . . . c c d d d 5 5 5 b b . .
116
+ . . . . . . c c c c c b b . . .
117
+ `, img`
118
+ . . . . . . . . . . b 5 b . . .
119
+ . . . . . . . . . b 5 b . . . .
120
+ . . . . . . b b b b b b . . . .
121
+ . . . . . b b 5 5 5 5 5 b . . .
122
+ . . . . b b 5 d 1 f 5 5 d f . .
123
+ . . . . b 5 5 1 f f 5 d 4 c . .
124
+ . . . . b 5 5 d f b d d 4 4 . .
125
+ . b b b d 5 5 5 5 5 4 4 4 4 4 b
126
+ b d d d b b d 5 5 4 4 4 4 4 b .
127
+ b b d 5 5 5 b 5 5 5 5 5 5 b . .
128
+ c d c 5 5 5 5 d 5 5 5 5 5 5 b .
129
+ c b d c d 5 5 b 5 5 5 5 5 5 b .
130
+ . c d d c c b d 5 5 5 5 5 d b .
131
+ . . c b d d d d d 5 5 5 b b . .
132
+ . . . c c c c c c c c b b . . .
133
+ . . . . . . . . . . . . . . . .
134
+ `];
135
+ }
136
+ return null;
137
+ })
138
+
139
+ helpers._registerFactory("song", function(name: string) {
140
+ switch(helpers.stringTrim(name)) {
141
+
142
+ }
143
+ return null;
144
+ })
145
+
146
+ helpers._registerFactory("json", function(name: string) {
147
+ switch(helpers.stringTrim(name)) {
148
+
149
+ }
150
+ return null;
151
+ })
152
+
153
+ }
154
+ // Auto-generated code. Do not edit.
155
+
156
+ // Auto-generated code. Do not edit.
157
+ namespace myTiles {
158
+
159
+ }
160
+ // Auto-generated code. Do not edit.
@@ -0,0 +1,61 @@
1
+ // Use SpriteKind.create() to create a unique kind for each Sprite
2
+ const GateKind = SpriteKind.create();
3
+ const PlayerKind = SpriteKind.create();
4
+
5
+ scene.setBackgroundColor(9);
6
+ showControls();
7
+ startGame();
8
+
9
+ function showControls() {
10
+ // showLongText automatically pauses until A is pressed
11
+ game.showLongText("Press A to jump\n \nTry to avoid the gates!", DialogLayout.Center);
12
+ }
13
+
14
+ function startGame() {
15
+ // Create the player sprite using the "playerImage" asset
16
+ const mySprite = sprites.create(assets.image`playerImage`, PlayerKind);
17
+ mySprite.left = 4;
18
+ mySprite.setFlag(SpriteFlag.StayInScreen, true);
19
+ mySprite.ay = 500;
20
+
21
+ // When A is pressed, make the player sprite jump and play a flapping animation
22
+ controller.A.onEvent(ControllerButtonEvent.Pressed, () => {
23
+ mySprite.vy = -100;
24
+ animation.runImageAnimation(mySprite, assets.animation`playerAnimation`, 50)
25
+ });
26
+
27
+ // Spawn a gate every 2 seconds
28
+ game.onUpdateInterval(2000, () => {
29
+ // Create a gate with an empty image that spans the height of the screen
30
+ const gate = sprites.create(image.create(12, screen.height), GateKind);
31
+
32
+ // Select a y value for our gate opening to start
33
+ const gapHeight = 50;
34
+ const gapStart = randint(0, screen.height - gapHeight);
35
+
36
+ // Draw the gate on the empty image
37
+ gate.image.fillRect(2, 0, 8, screen.height, 6);
38
+ gate.image.fillRect(2, gapStart, 8, 50, 0);
39
+ gate.image.fillRect(0, gapStart - 5, 12, 5, 6);
40
+ gate.image.fillRect(0, gapStart + gapHeight, 12, 5, 6);
41
+
42
+ // Move the gate to the right side of the screen and give it velocity
43
+ gate.left = screen.width;
44
+ gate.vx = -50;
45
+
46
+ // Turn on the AutoDestroy flag so that gates are automatically destroyed
47
+ // when they leave the screen
48
+ gate.setFlag(SpriteFlag.AutoDestroy, true);
49
+ });
50
+
51
+ // End the game when the player overlaps a gate
52
+ sprites.onOverlap(PlayerKind, GateKind, (sprite, otherSprite) => {
53
+ game.over();
54
+ });
55
+
56
+ // When a gate is auto destroyed, increase our score by 1
57
+ sprites.onDestroyed(GateKind, () => {
58
+ info.changeScoreBy(1);
59
+ });
60
+ }
61
+
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "flappy-game",
3
+ "description": "Flappy Bird style game template",
4
+ "dependencies": {
5
+ "device": "*"
6
+ },
7
+ "files": [
8
+ "main.ts",
9
+ "README.md",
10
+ "assets.json",
11
+ "images.g.jres",
12
+ "images.g.ts"
13
+ ],
14
+ "supportedTargets": [
15
+ "arcade"
16
+ ],
17
+ "preferredEditor": "tsprj"
18
+ }
@@ -0,0 +1 @@
1
+ # Platformer Game
@@ -0,0 +1,18 @@
1
+ {
2
+ "myImages.image1": {
3
+ "id": "myImages.image1",
4
+ "data": "hwQOAA4AAAAAAAD7/w8AAAAAAN/d+wAAAAAA+//9AAAAAAAA8Pv/AM7M7/6/2/0A3jvb3bLdDwDevdPfLb0PAMDd3f0t3f0AAL/d3Svd/wAA393dK90PAMDd3f0t3Q8A3r3T3y3b/QDeO9vd+///AM7M7/4PAAAA",
5
+ "dataEncoding": "base64",
6
+ "namespace": "myImages.",
7
+ "mimeType": "image/x-mkcd-f4",
8
+ "displayName": "playerImage"
9
+ },
10
+ "myImages.image2": {
11
+ "id": "myImages.image2",
12
+ "data": "hwQQABAAAAAAAAAAAPD/AAAAAAAAwLEAAAD//w8f+wAA/929/B+xAPAbEd29H/sA8BERvRzM8Q8fERHxH73/Dx8REdER+///HxER0RGx//8fERHxH/v///AREb0czbEP8BsR3b0f+wAA/929/B+xAAAA//8PH/sAAAAAAADPsQAAAAAAAPD/AA==",
13
+ "dataEncoding": "base64",
14
+ "namespace": "myImages.",
15
+ "mimeType": "image/x-mkcd-f4",
16
+ "displayName": "enemyImage"
17
+ }
18
+ }
@@ -0,0 +1,74 @@
1
+ // Auto-generated code. Do not edit.
2
+ namespace myImages {
3
+
4
+ helpers._registerFactory("image", function(name: string) {
5
+ switch(helpers.stringTrim(name)) {
6
+ case "image1":
7
+ case "playerImage":return img`
8
+ . . . . e e e . . . . e e e
9
+ . . . . c d d c . . c d d c
10
+ . . . . c b d d f f d d b c
11
+ . . . . c 3 b d b d d b 3 c
12
+ . . . . f b 3 d d d d 3 b f
13
+ . . . . e d d d d d d d d e
14
+ b f b . e d f d d d d f d e
15
+ f d f . f d d f d d f d d f
16
+ f d f . f 2 d d b b d d b f
17
+ f d f f b b 2 2 2 2 2 2 f .
18
+ f b d b b d d d d d d b f .
19
+ . f f f d d b d d d d d f .
20
+ . . . f d f f d f f f d f .
21
+ . . . f f . . f f . . f f .
22
+ `;
23
+ case "image2":
24
+ case "enemyImage":return img`
25
+ . . . . . . f f f f . . . . . .
26
+ . . . . f f 1 1 1 1 f f . . . .
27
+ . . . f b 1 1 1 1 1 1 b f . . .
28
+ . . . f 1 1 1 1 1 1 1 1 f . . .
29
+ . . f d 1 1 1 1 1 1 1 1 d f . .
30
+ . . f d 1 1 1 1 1 1 1 1 d f . .
31
+ . . f d d d 1 1 1 1 d d d f . .
32
+ . . f b d b f d d f b d b f . .
33
+ . . f c d c f 1 1 f c d c f . .
34
+ . . . f b 1 1 1 1 1 1 b f . . .
35
+ . . f f f c d b 1 b d f f f f .
36
+ f c 1 1 1 c b f b f c 1 1 1 c f
37
+ f 1 b 1 b 1 f f f f 1 b 1 b 1 f
38
+ f b f b f f f f f f b f b f b f
39
+ . . . . . f f f f f f . . . . .
40
+ . . . . . . . f f f . . . . . .
41
+ `;
42
+ }
43
+ return null;
44
+ })
45
+
46
+ helpers._registerFactory("animation", function(name: string) {
47
+ switch(helpers.stringTrim(name)) {
48
+
49
+ }
50
+ return null;
51
+ })
52
+
53
+ helpers._registerFactory("song", function(name: string) {
54
+ switch(helpers.stringTrim(name)) {
55
+
56
+ }
57
+ return null;
58
+ })
59
+
60
+ helpers._registerFactory("json", function(name: string) {
61
+ switch(helpers.stringTrim(name)) {
62
+
63
+ }
64
+ return null;
65
+ })
66
+
67
+ }
68
+ // Auto-generated code. Do not edit.
69
+
70
+ // Auto-generated code. Do not edit.
71
+ namespace myTiles {
72
+
73
+ }
74
+ // Auto-generated code. Do not edit.
@@ -0,0 +1,141 @@
1
+ // Use SpriteKind.create() to create a unique kind for each sprite
2
+ const PlayerKind = SpriteKind.create();
3
+ const EnemyKind = SpriteKind.create();
4
+
5
+ // The Y acceleration of the player and enemies in pixel/second^2
6
+ const GRAVITY = 1000;
7
+
8
+ // The move speed of the player in pixels/second
9
+ const MOVE_SPEED = 100;
10
+
11
+ // The jump height in pixels
12
+ const JUMP_HEIGHT = 32;
13
+
14
+ // The height the player bounces when they jump on an enemy
15
+ const BOUNCE_HEIGHT = 16;
16
+
17
+ // The move speed of enemies in pixels/second
18
+ const ENEMY_SPEED = 50;
19
+
20
+ let player: Sprite;
21
+ startGame();
22
+
23
+ function startGame() {
24
+ // Set the background color to cyan
25
+ scene.setBackgroundColor(9);
26
+
27
+ loadTilemap();
28
+ createPlayer();
29
+ registerEvents();
30
+ }
31
+
32
+ function loadTilemap () {
33
+ // Load the tilemap asset
34
+ scene.setTileMapLevel(assets.tilemap`level`);
35
+
36
+ // Loop over all enemySpawn tiles and create enemy sprites at
37
+ // those locations
38
+ for (let location of tiles.getTilesByType(assets.tile`enemySpawn`)) {
39
+ const enemy = sprites.create(assets.image`enemyImage`, EnemyKind);
40
+ enemy.x = location.x;
41
+ enemy.y = location.y;
42
+ enemy.ay = GRAVITY;
43
+
44
+ // Hide the enemy spawn tile
45
+ tiles.setTileAt(location, assets.tile`transparency16`);
46
+
47
+ // Randomly make the enemy move left or right
48
+ if (Math.percentChance(50)) {
49
+ enemy.vx = ENEMY_SPEED;
50
+ } else {
51
+ enemy.vx = 0 - ENEMY_SPEED;
52
+ }
53
+ }
54
+ }
55
+
56
+ function createPlayer() {
57
+ // Create the player sprite using the playerImage asset
58
+ player = sprites.create(assets.image`playerImage`, PlayerKind);
59
+
60
+ // Set the acceleration in the Y direction equal to our gravity
61
+ player.ay = GRAVITY;
62
+
63
+ // Move the sprite with buttons, but only in the X direction
64
+ controller.moveSprite(player, MOVE_SPEED, 0);
65
+
66
+ // Follow the player with the camera. The camera is automatically
67
+ // boxed within the tilemap
68
+ scene.cameraFollowSprite(player);
69
+
70
+ // Place the player on top of the playerSpawn tile
71
+ tiles.placeOnRandomTile(player, assets.tile`playerSpawn`);
72
+
73
+ // Hide the player spawn tile
74
+ tiles.setTileAt(player.tilemapLocation(), assets.tile`transparency16`);
75
+ }
76
+
77
+ function registerEvents () {
78
+ // Jump on A button press
79
+ controller.A.onEvent(ControllerButtonEvent.Pressed, () => {
80
+ // First make sure the player is on the ground
81
+ if (player.isHittingTile(CollisionDirection.Bottom)) {
82
+ doJump(player, JUMP_HEIGHT);
83
+ }
84
+ });
85
+
86
+ // When the player overlaps lava, end the game
87
+ scene.onOverlapTile(PlayerKind, assets.tile`lava`, (sprite, location) => {
88
+ onPlayerDied();
89
+ });
90
+
91
+ // When the player overlaps the treasure chest, win the game
92
+ scene.onOverlapTile(PlayerKind, assets.tile`treasureChest`, (sprite, location) => {
93
+ game.over(true);
94
+ });
95
+
96
+ // Handle player overlapping with an enemy
97
+ sprites.onOverlap(PlayerKind, EnemyKind, (sprite, otherSprite) => {
98
+ // Check to see if the player is jumping on the enemy
99
+ if (sprite.vy > 0 && sprite.bottom < otherSprite.top + 8) {
100
+ // Destroy the enemy and bounce the player
101
+ otherSprite.destroy();
102
+ doJump(sprite, BOUNCE_HEIGHT);
103
+ }
104
+ else {
105
+ // If the player isn't jumping on top of the enemy, take damage
106
+ onPlayerDied();
107
+ }
108
+ });
109
+
110
+ // Bounce enemies off of each other when they overlap
111
+ sprites.onOverlap(EnemyKind, EnemyKind, (sprite, otherSprite) => {
112
+ if (sprite.x < otherSprite.x) {
113
+ sprite.right = otherSprite.left;
114
+ sprite.vx = -ENEMY_SPEED;
115
+ otherSprite.vx = ENEMY_SPEED;
116
+ }
117
+ else {
118
+ otherSprite.right = sprite.left;
119
+ otherSprite.vx = -ENEMY_SPEED;
120
+ sprite.vx = ENEMY_SPEED;
121
+ }
122
+ });
123
+
124
+ // Also bounce enemies off of walls
125
+ scene.onHitWall(EnemyKind, (sprite, location) => {
126
+ if (sprite.isHittingTile(CollisionDirection.Left)) {
127
+ sprite.vx = ENEMY_SPEED;
128
+ }
129
+ else if (sprite.isHittingTile(CollisionDirection.Right)) {
130
+ sprite.vx = -ENEMY_SPEED;
131
+ }
132
+ });
133
+ }
134
+
135
+ function doJump (sprite: Sprite, height: number) {
136
+ sprite.vy = -Math.sqrt(2 * height * GRAVITY);
137
+ }
138
+
139
+ function onPlayerDied () {
140
+ game.reset();
141
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "platformer-game",
3
+ "description": "Platform game template with tilemap, enemies, and player",
4
+ "dependencies": {
5
+ "device": "*",
6
+ "color-coded-tilemap": "*"
7
+ },
8
+ "files": [
9
+ "main.ts",
10
+ "README.md",
11
+ "assets.json",
12
+ "images.g.jres",
13
+ "images.g.ts",
14
+ "tilemap.g.jres",
15
+ "tilemap.g.ts"
16
+ ],
17
+ "supportedTargets": [
18
+ "arcade"
19
+ ],
20
+ "preferredEditor": "tsprj"
21
+ }
@@ -0,0 +1,71 @@
1
+ {
2
+ "myTiles.transparency16": {
3
+ "id": "myTiles.transparency16",
4
+ "data": "hwQQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
5
+ "dataEncoding": "base64",
6
+ "namespace": "myTiles.",
7
+ "mimeType": "image/x-mkcd-f4",
8
+ "tilemapTile": true
9
+ },
10
+ "myTiles.tile1": {
11
+ "id": "myTiles.tile1",
12
+ "data": "hwQQABAAAADdHd0d3R3dHRER3R0REd0d3R3dHd0d3R3dHd0d3R3dHd0d3R3dHd0d3R0REd0dERHdHd0d3R3dHd0d3R3dHd0d3R3dHd0d3R0REd0dERHdHd0d3R3dHd0d3R3dHd0d3R3dHd0d3R3dHd0dERHdHRER3R3dHd0d3R3dHd0d3R3dHQ==",
13
+ "dataEncoding": "base64",
14
+ "namespace": "myTiles.",
15
+ "mimeType": "image/x-mkcd-f4",
16
+ "tilemapTile": true,
17
+ "displayName": "brick"
18
+ },
19
+ "myTiles.tile2": {
20
+ "id": "myTiles.tile2",
21
+ "data": "hwQQABAAAAAAu7u7y7y7C7Du7u677M6760Tk7rvs7rtLRETuu+zuC0tERO677O4LS0RE7rvs7gtLRETuu8vuC0tERO7NvO4LS0RE7s287gtLRETuu8vuC0tERO677O4LS0RE7rvs7gtLRETuu+zuC+tE5O677O67sO7u7rvszrsAu7u7y7y7Cw==",
22
+ "dataEncoding": "base64",
23
+ "namespace": "myTiles.",
24
+ "mimeType": "image/x-mkcd-f4",
25
+ "tilemapTile": true,
26
+ "displayName": "treasureChest"
27
+ },
28
+ "myTiles.tile3": {
29
+ "id": "myTiles.tile3",
30
+ "data": "hwQQABAAAABVJERCRVREREUiREJVVVVVJCIiVEVFVVUiJERVRCJURCJCVEQiQiJFIkRFJCIkJEUiVEUiJEIiRUJUJCIiIkJFREUkIiIiQkVCRSJEIiRCRUJFIkQiIlJFQlVEIiJCVCRCRVVFRFRVJERVRFVVVUQiVEREVEVVJEJVRCJCRVQkQg==",
31
+ "dataEncoding": "base64",
32
+ "namespace": "myTiles.",
33
+ "mimeType": "image/x-mkcd-f4",
34
+ "tilemapTile": true,
35
+ "displayName": "lava"
36
+ },
37
+ "myTiles.tile4": {
38
+ "id": "myTiles.tile4",
39
+ "data": "hwQQABAAAAAAAAAAAAAAAAAAAAD7/w8AAAAAAN/d+wAAAAAA+//9AAAAAAAA8Pv/AM7M7/6/2/0A3jvb3bLdDwDevdPfLb0PAMDd3f0t3f0AAL/d3Svd/wAA393dK90PAMDd3f0t3Q8A3r3T3y3b/QDeO9vd+///AM7M7/4PAAAAAAAAAAAAAA==",
40
+ "dataEncoding": "base64",
41
+ "namespace": "myTiles.",
42
+ "mimeType": "image/x-mkcd-f4",
43
+ "tilemapTile": true,
44
+ "displayName": "playerSpawn"
45
+ },
46
+ "myTiles.tile5": {
47
+ "id": "myTiles.tile5",
48
+ "data": "hwQQABAAAAAAAAAAAPD/AAAAAAAAwLEAAAD//w8f+wAA/929/B+xAPAbEd29H/sA8BERvRzM8Q8fERHxH73/Dx8REdER+///HxER0RGx//8fERHxH/v///AREb0czbEP8BsR3b0f+wAA/929/B+xAAAA//8PH/sAAAAAAADPsQAAAAAAAPD/AA==",
49
+ "dataEncoding": "base64",
50
+ "namespace": "myTiles.",
51
+ "mimeType": "image/x-mkcd-f4",
52
+ "tilemapTile": true,
53
+ "displayName": "enemySpawn"
54
+ },
55
+ "level1": {
56
+ "id": "level1",
57
+ "data": "MTAyYTAwMDgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDAwMDAwMDAwMDAwMDAwMDEwMTAwMDAwMTAxMDEwMDAwMDEwMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDEwMTAwMDAwNDAwMDAwMDAxMDEwMTAwMDAwMDAwMDAwMDAwMDEwMTAxMDAwMDAwMDAwNTAwMDAwMDAxMDEwMDAwMDUwMDAwMDUwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAzMDMwMzAzMDMwMzAzMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDIyMDIyMDAyMjAwMDAwMDAwMDAwMDAwMDAwMDAwMjAyMjAwMDAwMDIyMDIwMDAwMDAyMjAyMDAwMDAwMjAwMjAwMDAwMDAwMjIyMjIyMjIyMjIyMDIwMDAwMDAyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMg==",
58
+ "dataEncoding": "base64",
59
+ "namespace": "myTiles.",
60
+ "mimeType": "application/mkcd-tilemap",
61
+ "displayName": "level",
62
+ "tileset": [
63
+ "myTiles.transparency16",
64
+ "myTiles.tile1",
65
+ "myTiles.tile2",
66
+ "myTiles.tile3",
67
+ "myTiles.tile4",
68
+ "myTiles.tile5"
69
+ ]
70
+ }
71
+ }
@@ -0,0 +1,197 @@
1
+ // Auto-generated code. Do not edit.
2
+ namespace myImages {
3
+
4
+ helpers._registerFactory("image", function(name: string) {
5
+ switch(helpers.stringTrim(name)) {
6
+ case "myTiles.transparency16":return img`
7
+ . . . . . . . . . . . . . . . .
8
+ . . . . . . . . . . . . . . . .
9
+ . . . . . . . . . . . . . . . .
10
+ . . . . . . . . . . . . . . . .
11
+ . . . . . . . . . . . . . . . .
12
+ . . . . . . . . . . . . . . . .
13
+ . . . . . . . . . . . . . . . .
14
+ . . . . . . . . . . . . . . . .
15
+ . . . . . . . . . . . . . . . .
16
+ . . . . . . . . . . . . . . . .
17
+ . . . . . . . . . . . . . . . .
18
+ . . . . . . . . . . . . . . . .
19
+ . . . . . . . . . . . . . . . .
20
+ . . . . . . . . . . . . . . . .
21
+ . . . . . . . . . . . . . . . .
22
+ . . . . . . . . . . . . . . . .
23
+ `;
24
+ case "myTiles.tile1":
25
+ case "brick":return img`
26
+ d 1 d d d d d d d 1 d d d d d d
27
+ d 1 d d d d d d d 1 d d d d d d
28
+ d 1 d d d d d d d 1 d d d d d d
29
+ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
30
+ d d d d d 1 d d d d d d d 1 d d
31
+ d d d d d 1 d d d d d d d 1 d d
32
+ d d d d d 1 d d d d d d d 1 d d
33
+ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
34
+ d 1 d d d d d d d 1 d d d d d d
35
+ d 1 d d d d d d d 1 d d d d d d
36
+ d 1 d d d d d d d 1 d d d d d d
37
+ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
38
+ d d d d d 1 d d d d d d d 1 d d
39
+ d d d d d 1 d d d d d d d 1 d d
40
+ d d d d d 1 d d d d d d d 1 d d
41
+ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
42
+ `;
43
+ case "myTiles.tile2":
44
+ case "treasureChest":return img`
45
+ . . b b b b b b b b b b b b . .
46
+ . b e 4 4 4 4 4 4 4 4 4 4 e b .
47
+ b e 4 4 4 4 4 4 4 4 4 4 4 4 e b
48
+ b e 4 4 4 4 4 4 4 4 4 4 4 4 e b
49
+ b e 4 4 4 4 4 4 4 4 4 4 4 4 e b
50
+ b e e 4 4 4 4 4 4 4 4 4 4 e e b
51
+ b e e e e e e e e e e e e e e b
52
+ b e e e e e e e e e e e e e e b
53
+ b b b b b b b d d b b b b b b b
54
+ c b b b b b b c c b b b b b b c
55
+ c c c c c c b c c b c c c c c c
56
+ b e e e e e c b b c e e e e e b
57
+ b e e e e e e e e e e e e e e b
58
+ b c e e e e e e e e e e e e c b
59
+ b b b b b b b b b b b b b b b b
60
+ . b b . . . . . . . . . . b b .
61
+ `;
62
+ case "myTiles.tile3":
63
+ case "lava":return img`
64
+ 5 5 4 2 2 2 2 2 4 2 2 2 2 4 4 5
65
+ 5 4 2 2 2 2 2 4 4 4 4 4 4 4 5 5
66
+ 4 2 2 4 2 4 4 4 5 5 5 5 5 5 4 4
67
+ 2 2 2 2 4 4 5 5 4 4 4 5 4 5 4 4
68
+ 4 4 2 4 4 5 5 4 4 2 2 4 5 4 4 2
69
+ 4 4 2 4 5 4 4 2 2 2 2 4 5 4 4 2
70
+ 2 2 4 5 4 4 2 2 2 4 4 2 5 5 4 2
71
+ 4 4 5 5 4 2 2 2 2 4 4 2 4 5 5 4
72
+ 5 5 5 4 2 2 4 2 2 2 2 2 4 5 5 5
73
+ 4 5 4 4 2 2 2 2 2 2 2 2 4 5 4 4
74
+ 4 5 5 2 2 4 2 2 2 4 2 2 4 5 5 4
75
+ 5 5 4 2 4 2 4 2 2 2 2 4 5 5 5 5
76
+ 4 5 5 4 2 4 2 2 2 2 2 4 5 4 4 4
77
+ 4 5 5 5 2 2 2 4 4 4 5 5 5 4 2 2
78
+ 4 5 5 4 5 5 5 5 5 5 5 4 4 2 2 2
79
+ 4 5 5 4 4 4 4 4 4 4 4 2 2 2 4 4
80
+ `;
81
+ case "myTiles.tile4":
82
+ case "playerSpawn":return img`
83
+ . . . . . . . . . . . . . . . .
84
+ . . . . . . . . . . . . . . . .
85
+ . . . . . e e e . . . . e e e .
86
+ . . . . . c d d c . . c d d c .
87
+ . . . . . c b d d f f d d b c .
88
+ . . . . . c 3 b d b d d b 3 c .
89
+ . . . . . f b 3 d d d d 3 b f .
90
+ . . . . . e d d d d d d d d e .
91
+ . b f b . e d f d d d d f d e .
92
+ . f d f . f d d f d d f d d f .
93
+ . f d f . f 2 d d b b d d b f .
94
+ . f d f f b b 2 2 2 2 2 2 f . .
95
+ . f b d b b d d d d d d b f . .
96
+ . . f f f d d b d d d d d f . .
97
+ . . . . f d f f d f f f d f . .
98
+ . . . . f f . . f f . . f f . .
99
+ `;
100
+ case "myTiles.tile5":
101
+ case "enemySpawn":return img`
102
+ . . . . . . f f f f . . . . . .
103
+ . . . . f f 1 1 1 1 f f . . . .
104
+ . . . f b 1 1 1 1 1 1 b f . . .
105
+ . . . f 1 1 1 1 1 1 1 1 f . . .
106
+ . . f d 1 1 1 1 1 1 1 1 d f . .
107
+ . . f d 1 1 1 1 1 1 1 1 d f . .
108
+ . . f d d d 1 1 1 1 d d d f . .
109
+ . . f b d b f d d f b d b f . .
110
+ . . f c d c f 1 1 f c d c f . .
111
+ . . . f b 1 1 1 1 1 1 b f . . .
112
+ . . f f f c d b 1 b d f f f f .
113
+ f c 1 1 1 c b f b f c 1 1 1 c f
114
+ f 1 b 1 b 1 f f f f 1 b 1 b 1 f
115
+ f b f b f f f f f f b f b f b f
116
+ . . . . . f f f f f f . . . . .
117
+ . . . . . . . f f f . . . . . .
118
+ `;
119
+ }
120
+ return null;
121
+ })
122
+
123
+ helpers._registerFactory("animation", function(name: string) {
124
+ switch(helpers.stringTrim(name)) {
125
+
126
+ }
127
+ return null;
128
+ })
129
+
130
+ helpers._registerFactory("song", function(name: string) {
131
+ switch(helpers.stringTrim(name)) {
132
+
133
+ }
134
+ return null;
135
+ })
136
+
137
+ helpers._registerFactory("json", function(name: string) {
138
+ switch(helpers.stringTrim(name)) {
139
+
140
+ }
141
+ return null;
142
+ })
143
+
144
+ }
145
+ // Auto-generated code. Do not edit.
146
+
147
+ // Auto-generated code. Do not edit.
148
+ namespace myTiles {
149
+ //% fixedInstance jres blockIdentity=images._tile
150
+ export const transparency16 = image.ofBuffer(hex``);
151
+ //% fixedInstance jres blockIdentity=images._tile
152
+ export const tile1 = image.ofBuffer(hex``);
153
+ //% fixedInstance jres blockIdentity=images._tile
154
+ export const tile2 = image.ofBuffer(hex``);
155
+ //% fixedInstance jres blockIdentity=images._tile
156
+ export const tile3 = image.ofBuffer(hex``);
157
+ //% fixedInstance jres blockIdentity=images._tile
158
+ export const tile4 = image.ofBuffer(hex``);
159
+ //% fixedInstance jres blockIdentity=images._tile
160
+ export const tile5 = image.ofBuffer(hex``);
161
+
162
+ helpers._registerFactory("tilemap", function(name: string) {
163
+ switch(helpers.stringTrim(name)) {
164
+ case "level":
165
+ case "level1":return tiles.createTilemap(hex`2a000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000101000001010100000101000000000000000000000000000000000000000000010101000004000000010101000000000000000101010000000005000000010100000500000500000001010101010101010101010101030303030303030101010101010101010101010101010101010101010101010101`, img`
166
+ ..........................................
167
+ ..........................................
168
+ ..........................................
169
+ ..........................................
170
+ ..........................................
171
+ .......22..222..22.....................222
172
+ ......222.......222........22.........2222
173
+ 222222222.......22222222222222222222222222
174
+ `, [myTiles.transparency16,myTiles.tile1,myTiles.tile2,myTiles.tile3,myTiles.tile4,myTiles.tile5], TileScale.Sixteen);
175
+ }
176
+ return null;
177
+ })
178
+
179
+ helpers._registerFactory("tile", function(name: string) {
180
+ switch(helpers.stringTrim(name)) {
181
+ case "transparency16":return myTiles.transparency16;
182
+ case "brick":
183
+ case "tile1":return myTiles.tile1;
184
+ case "treasureChest":
185
+ case "tile2":return myTiles.tile2;
186
+ case "lava":
187
+ case "tile3":return myTiles.tile3;
188
+ case "playerSpawn":
189
+ case "tile4":return myTiles.tile4;
190
+ case "enemySpawn":
191
+ case "tile5":return myTiles.tile5;
192
+ }
193
+ return null;
194
+ })
195
+
196
+ }
197
+ // Auto-generated code. Do not edit.
@@ -1,2 +0,0 @@
1
- # Arcade Game
2
- 由 arcade-ai 脚手架生成。用 AI 修改 `main.ts`,`aca dev` 实时预览。
File without changes
File without changes
File without changes