A browser-based isometric island builder with the soft, sun-bleached look of Mykonos. Vanilla ES modules, no bundler, mobile-friendly.
Mykonos Island Voxels
一款基于浏览器的等距岛屿建造工具,呈现米科诺斯岛柔和而阳光晒褪的地中海风情:白墙之上的钴蓝色穹顶、从石缝中溢出的九重葛、橄榄树、风车、狭窄的鹅卵石小径,以及轻触即可雕琢的海面。
这是一个小巧而独立的创意玩具——在14×14的网格上放置方块,一个小村庄便会在你眼前逐渐成型。没有目标,没有资源积累,没有计分;只有如同拼接拼图般的乐趣,不断调整,直到一切看起来恰到好处。
🌐 立即体验:https://mykonos-island-voxels.netlify.app

功能特点
- 点击建造等距网格。 从右侧面板中选择资产,点击单元格,资产便会带着弹性放置动画弹出。
- 一键“铺满草地” 功能,瞬间为岛屿铺上绿地,让你即刻开始规划。
- 75+ 手绘风格资产,分为地形、自然、道具、水域和建筑类别——包括小教堂、风车、两层别墅、柏树、橄榄树、龙舌兰、水井、灯笼、围栏、桥梁等等。
- 移动优先的用户界面。 点击放置,长按删除,拖动刷涂,双指缩放和平移。布局从桌面设备自适应到小屏手机,并为iPhone刘海屏提供安全区域适配。
- 高保真资产处理流程。 源PNG图片在加载时预渲染至6倍显示分辨率,烘焙成高DPI缓存图层,然后逐帧合成,确保画布在任何缩放级别和屏幕密度下都保持清晰。
- 自动保存。 你的岛屿会保存到
localStorage中,并在下次访问时自动加载。 - 雅致的音效设计。 为水域、石头、木材、小型植被、大型植被和UI点击等不同操作提供独特的放置音效,并通过去抖动重叠处理,避免刷涂时音效过于密集。
- 纯ES模块。 无需打包工具,无需转译器,无需
node_modules——打开index.html即可运行。
操作控制
鼠标 + 键盘
| 输入 | 操作 |
|---|---|
| 点击 | 放置选中的资产 |
| 拖动 | 刷涂放置多个单元格 |
| 右键点击 | 擦除方块 |
| 右键拖动 | 刷涂擦除 |
| Shift + 拖动 | 平移相机 |
| 滚轮 | 缩放 |
H / V |
翻转放置预览 |
E |
切换擦除模式 |
G |
切换网格显示 |
1–5 |
切换面板类别 |
S / R |
保存 / 重置 |
触摸操作
| 手势 | 操作 |
|---|---|
| 轻点 | 放置所选资源 |
| 拖动 | 在单元格上连续放置 |
| 长按(约420毫秒) | 擦除手指下方的瓦片 |
| 双指捏合 | 缩放 |
| 双指拖动 | 平移相机 |
本地运行
该项目是纯 HTML / CSS / ES 模块,开发时无需构建步骤。由于浏览器拒绝从 file:// URL 加载 ES 模块,因此需要通过 HTTP 提供服务。选择以下任意一种最简单的方式:
# any one of these from the project root:
python3 -m http.server 8000
npx serve .
npx http-server -c-1 .
然后打开 http://localhost:8000。
部署
网站部署于 Netlify。所包含的 netlify.toml 和 netlify-build.mjs 会生成一个干净的 dist/ 文件夹,其中仅包含运行时文件(无设计参考文件、无 .DS_Store、无 .webp 重复文件),并附带正确的缓存头(资源文件设为 immutable,HTML/CSS/JS 文件设为 must-revalidate)。
netlify deploy --prod
项目布局
.
├── index.html # entry point
├── styles.css # the entire UI (no framework)
├── src/
│ ├── main.js # boot, asset loading, starter scene
│ ├── config.js # grid size, tile dims, palette, debug flags
│ ├── core/
│ │ ├── Game.js # game state + tool dispatch
│ │ ├── Camera.js # pan / zoom / change notifications
│ │ ├── Renderer.js # layered canvas caching + animations
│ │ └── InputManager.js # mouse + touch + keyboard
│ ├── grid/
│ │ ├── IsoGrid.js # screen ↔ cell math
│ │ └── TileMap.js # terrain + objects, occupancy index
│ ├── building/
│ │ └── PlacementSystem.js
│ ├── assets/
│ │ ├── assetManifest.js # the 75+ asset definitions
│ │ ├── assetLoader.js # PNG → display canvas + shadow canvas
│ │ ├── imageToAsset.js # silhouette extraction, anchor inference
│ │ └── voxelRenderer.js # procedural fallback when PNGs missing
│ ├── ui/
│ │ ├── UIManager.js
│ │ ├── Toolbar.js
│ │ ├── AssetPalette.js
│ │ ├── HUD.js
│ │ └── Audio.js # WebAudio clip routing + debouncing
│ └── persistence/
│ └── SaveSystem.js
├── assets/ # PNG asset pack (pre-generated)
├── *.ogg # placement / UI sound effects
├── netlify.toml
└── netlify-build.mjs
架构说明
如果你想修改渲染器,以下几点设计选择值得注意:
- 分层缓存渲染。
Renderer.js维护四个缓存画布: 一对屏幕空间的背景和暗角(在调整大小时重建)、一个世界空间的平台层(在网格调整大小时重建)、一个世界空间的地形层(当地形版本计数器变化时重建),以及一个世界空间的静态物体层(在添加/移除物体时重建)。每一帧,渲染器会合成这些缓存,并只叠加当前正在动画的瓦片。静止帧的渲染成本几乎为零。 - 高 DPI 缓存画布。缓存画布的尺寸为
world × CACHE_SCALE(标准显示器上为 2 倍,视网膜显示器上为 3 倍),这样即使在相机对其进行放大后,像素也能达到或接近最终的屏幕分辨率。资源的 displayCanvases 预渲染时的尺寸最高可达其参考尺寸的 6 倍,因此细节得以保留,不会出现中间环节的“模糊”。 TileMap中的空间占用索引。对象查找和空闲单元格检查的每个单元格操作复杂度为 O(1),而非遍历对象列表的 O(N)。- 脏标记渲染。在相机、输入或工具转换时会调用
markDirty();当场景静止且没有动画待处理时,循环会提前退出。
贡献指南
欢迎提交 PR。请务必:
- 保持无框架依赖——运行时不使用打包工具、转译器和
node_modules。 - 控制资源数量,并保持视觉风格的一致性(钴蓝配奶油白、柔和阴影、轻微的弹性运动)。
- 不要添加每帧
ctx.filter、ImageBitmap 相关的复杂操作,或任何可能在复杂场景中导致掉帧的内容。渲染器的缓存机制对性能至关重要。
许可证
MIT——详见 LICENSE。
assets/ 目录中的 PNG 资源包以相同许可证发布,专为本项目生成。音频片段为单独创作;如需注明出处,请查看文件元数据。
下载使用量
项目总下载次数(含Clone、Pull、 zip 包及 release 下载),每日凌晨更新