Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

Miu2D Logo

A from-scratch 2D ARPG engine — raw WebGL, zero game-framework dependencies

Live Demo · 中文文档


Miu2D is a 176,000-line 2D ARPG engine written in TypeScript and Rust, rendering through raw WebGL with no dependency on Unity, Godot, Phaser, PixiJS, or any other game framework. Every subsystem — sprite batching, A* pathfinding, binary format decoders, scripting VM, weather particles, screen effects — is implemented from first principles.

As a proof of concept, Miu2D has been used to rebuild three classic Kingsoft (西山居) wuxia RPGs, all fully playable in any modern browser.

Vibe Coding — This project is developed with AI-assisted programming from day one.


Legend of Yue Ying (剑侠情缘外传:月影传说) · 2001

DeveloperXishanju (西山居 / Kingsoft)
GenreAction RPG
Highlights7+ endings · 100+ story events · 30-person team (20+ artists) · 14-month production

The largest production Xishanju had ever mounted at the time. The story branches dramatically based on player choices — loyalty, love, wealth — shaping the protagonist's morality and emotional alignment to produce seven or more distinct endings. Scenes were built with a pioneering 3D+2D hybrid rendering technique, and the soundtrack blended classical Chinese instruments with contemporary pop production.

Legend of Yue Ying


Swords of Legends 2 (剑侠情缘贰) · 1998

DeveloperXishanju (西山居 / Kingsoft)
GenreAction RPG
HighlightsDiablo-style real-time combat · 200+ NPCs · 640×480 16-bit color · theme song by 谢雨欣

Set twenty years after the original, hero 南宫飞云 — son of the first game's protagonists — stumbles into a perilous journey after rescuing a mysterious girl named 若雪. The sequel boldly abandoned turn-based combat for a real-time action system inspired by Diablo, a revolutionary move for Chinese RPGs of the era. Three years in development with a budget of nearly ¥3 million and a team of 30.

Swords of Legends 2


New Swords of Legends (新剑侠情缘) · 2001

DeveloperXishanju (西山居 / Kingsoft)
GenreAction RPG
HighlightsRemake of the 1997 original · 110+ maps · indoor map system · real-time combat engine from Swords 2

A remake of the franchise's 1997 debut, rebuilt with the acclaimed real-time action combat engine from Swords of Legends 2. The story remains faithful to the original while greatly expanding the map count to 110+ scenes and introducing seamless indoor/outdoor transitions.

New Swords of Legends


Mobile & Editor Screenshots

Mobile — virtual joystick + touch controls:

Mobile

Map Editor — visual tilemap editing, collision zones:

Map Editor

ASF Editor — sprite animation frame viewer & debugger:

ASF Editor

Real-time Lighting & Shadows — additive glow + SHD shadow rendering:

Real-time Lighting


Why Build a Game Engine from Scratch?

Most web game projects reach for PixiJS, Phaser, or a WASM-compiled Unity/Godot build. Miu2D takes a different path: the entire rendering pipeline talks directly to WebGLRenderingContext, the pathfinder lives in Rust compiled to WASM with zero-copy shared memory, and the scripting engine supports both 218 DSL commands through a custom parser/executor pair and a full Lua 5.4 runtime (via wasmoon) sharing the same GameAPI. The result is a system whose every layer is visible, debuggable, and tailored to 2D RPG mechanics.

What this buys you:

  • Full control over the render loop — a SpriteBatcher coalesces ~4,800 map tile draws into 1–5 WebGL draw calls; a RectBatcher reduces ~300 weather particles to a single call; a real-time lighting pass composites per-entity additive glow masks with SHD-based shadow rendering.
  • No abstraction tax — no unused scene graph, no 3D math overhead, no framework event model to work around.
  • Rust-speed where it matters — A* pathfinding runs in ~0.2 ms via WASM with obstacle data written directly into linear memory (no serialization, no FFI copy).
  • Clean architecture for study — an 8-level class hierarchy (Sprite → CharacterBase → Movement → Combat → Character → PlayerBase → PlayerCombat → Player) with clear separation of concerns, ideal for understanding how a full 2D RPG engine works under the hood.

Architecture at a Glance

LayerPackageDetails
UI@miu2d/gameReact 19 · 3 themes (Classic / Modern / Mobile) · 84 components
Engine@miu2d/enginePure TypeScript · 215 files · 19 modules · no React dependency
↳ Rendererrenderer/Raw WebGL · SpriteBatcher · Canvas2D fallback · GLSL filters
↳ Script VMscript/218 commands · custom parser + async executor · Lua 5.4 (wasmoon WASM)
↳ Charactercharacter/8-level inheritance chain · NPC AI · bezier movement
↳ Magicmagic/22 MoveKind trajectories · 10 SpecialKind effects
WASM@miu2d/engine-wasmRust → WebAssembly · A* pathfinder · decoders · SpatialHash · zstd
Backend@miu2d/serverHono + tRPC + Prisma ORM · 21 PostgreSQL tables · 19 routers
Editor@miu2d/dashboardVS Code-style layout · 13 editing modules

Tech Stack

LayerTechnology
LanguageTypeScript 5.9 (strict) · Rust · GLSL
FrontendReact 19 · Vite 8 (rolldown) · Tailwind CSS 4
RenderingRaw WebGL API (Canvas 2D fallback)
AudioWeb Audio API (OGG Vorbis)
PerformanceRust → WebAssembly (wasm-bindgen, zero-copy)
BackendHono (lightweight HTTP) · tRPC 11 · Prisma ORM
DatabasePostgreSQL 16 · MinIO / S3
ValidationZod 4 (shared schemas across client & server)
QualityBiome (lint + format) · TypeScript strict mode
Monorepopnpm workspaces (11 packages)

Engine Systems

Miu2D implements 17 integrated ARPG subsystems (218 script commands) entirely from first principles:

SystemModuleHighlights
Renderingrenderer/Raw WebGL sprite batcher (~4,800 tiles → 1–5 draw calls), Canvas2D fallback, GLSL color filters (poison / freeze / petrify), screen effects (fade, flash, water ripple), real-time lighting & shadows (additive lum masks, per-entity glow, SHD-based shadow rendering)
Charactercharacter/8-level inheritance chain (Sprite → CharacterBase → Movement → Combat → Character → PlayerBase → PlayerCombat → Player/NPC); stats, status flags, bezier-curve movement
Combatcharacter/Hit detection, damage formula, knockback, death & respawn, party/enemy faction logic
Magic / Skillmagic/22 MoveKind trajectories (line, spiral, homing, AoE, summon, time-stop…) × 10 SpecialKind effects; per-level config, passive XiuLian system
NPC & AInpc/Behavior state machine (idle / patrol / chase / flee / dead), interaction scripts, spatial grid for fast neighbor lookup
Playerplayer/Controller, inventory (goods system), equipment slots, magic slots, experience & leveling
Mapmap/Multi-layer tile parsing, obstacle grid, trap zones, event areas, layer-sorted rendering
Script / Eventscript/Custom VM: parser + async executor, 218 commands across 9 categories (dialog, player, NPC, state, audio, effects, objects, items, misc); Lua 5.4 scripting via wasmoon WASM with full GameAPI bindings (170 PascalCase functions)
Pathfindingwasm/Rust WASM A* with zero-copy shared memory; 5 strategies (greedy → full A*); ~0.2 ms per query, ≈10× faster than TS
Collisionwasm/SpatialHash in Rust/WASM for O(1) broad-phase entity queries
Audioaudio/Web Audio API manager: streamed BGM (OGG/MP3), positional SFX (WAV/OGG), fade transitions
Weather / Particlesweather/Wind-driven rain + splash + lightning flash; wobbling snowflakes; screen-droplet lens effect
Object / Propobj/Interactable scene objects (chests, doors, barriers, traps) with script hooks and sprite animation
GUI / HUDgui/Dialog system (branching choices, portraits), shop/buy panel, mini-map, status bars, UI bridge to React
Inventory / Itemsplayer/10 goods categories, equip/unequip, use effects, loot drops with configurable drop tables
Save / Loadstorage/Multiple save slots, full game-state serialization to IndexedDB + server-side cloud saves
Resource Loadingresource/Async loader for 8 binary formats (ASF, MPC, MAP, SHD, XNB, MSF, MMF, INI/OBJ); GBK/UTF-8 decoding

Engine Deep Dive

Renderer — Raw WebGL with Automatic Batching

The renderer directly calls WebGLRenderingContext — no wrapper library.

  • SpriteBatcher — accumulates vertex data and flushes per texture change; typical map frame: ~4,800 tiles → 1–5 draw calls
  • RectBatcher — weather particles and UI rectangles batched into a single draw call
  • GPU texture managementImageDataWebGLTexture with WeakMap caching and FinalizationRegistry for automatic GPU resource cleanup
  • GLSL color filters — grayscale (petrification), blue tint (frozen), green tint (poison) applied per-sprite in the fragment shader
  • Screen effects — fade in/out, color overlays, screen flash, water ripple, all composited in the render loop
  • Canvas 2D fallback — same Renderer interface, full feature parity for devices without WebGL
  • Local lighting (LumMask) — when SetMainLum darkens the scene, light-emitting entities (objects, NPCs, magic projectiles) generate an additive white 800×400 elliptical glow mask at their position. A per-tile dedup (matching C++ Weather::drawElementLum) prevents double-drawing. A noLum flag on magic sub-projectiles suppresses redundant light sources for dense spell patterns, accurately matching the C++ reference:
    • LineMove: 1-in-3 sub-projectiles emit light (i % 3 === 1)
    • Square region: 1-in-9 (i % 3 === 1 && j % 3 === 1)
    • Wave / Rectangle region: 1-in-4 (i % 2 !== 0 && j % 2 !== 0)

Script Engine — 218 Commands + Lua 5.4

The engine supports two scripting modes that share the same GameAPI:

DSL Mode (.txt / .npc) — A custom parser tokenizes game script files; an executor interprets them with blocking/async support. Commands span 9 categories:

CategoryExamples
DialogSay, Talk, Choose, ChooseMultiple, DisplayMessage
PlayerAddLife, AddMana, SetPlayerPos, PlayerGoto, Equip
NPCAddNpc, DelNpc, SetNpcRelation, NpcAttack, MergeNpc
Game StateLoadMap, Assign, If/Goto, RunScript, RunParallelScript
AudioPlayMusic, StopMusic, PlaySound
EffectsFadeIn, FadeOut, BeginRain, ShowSnow, OpenWaterEffect
ObjectsAddObj, DelObj, OpenObj, SetObjScript
ItemsAddGoods, DelGoods, ClearGoods, AddRandGoods
MiscSleep, Watch, PlayMovie, DisableInput, ReturnToTitle

Lua Mode (.lua) — Full Lua 5.4 runtime via wasmoon (Lua compiled to WASM). All 170 GameAPI functions are exposed as PascalCase Lua globals. wasmoon's proxy system automatically bridges JS async functions to Lua coroutines — blocking operations like PlayerWalkTo() or Talk() just work with co.await-style transparency. Dispatched by file extension; the same ScriptExecutor routes .lua files to LuaExecutor and .txt/.npc files to the DSL executor.

-- Example Lua game script
FadeOut()
LoadMap("map/town.map")
SetPlayerPos(10, 15)
FadeIn()
Talk(0, "Welcome to the village.")
local choice = Choose("Join the quest?", "Yes", "No")
if choice == 1 then
  AddMagic("magic/fireball.ini")
  AddExp(500)
end

Scripts drive the entire game narrative — cutscenes, branching dialogs, NPC spawning, map transitions, combat triggers, and weather changes.

Magic System — 22 Movement Types × 10 Special Effects

Every magic attack follows one of 22 MoveKind trajectories, each with its own physics and rendering:

MovementBehavior
LineMoveMulti-projectile line — count scales with level
CircleMoveOrbital ring pattern
SpiralMoveExpanding spiral outward
SectorMoveFan-shaped spread
HeartMoveHeart-shaped flight path
FollowEnemyHoming missile tracking
ThrowParabolic arc projectile
TransportTeleportation
SummonSpawn allied NPC
TimeStopFreeze all entities
VMoveV-shaped diverging spread
...and 11 more

Combined with 10 SpecialKind effects (freeze, poison, petrify, invisibility, heal, buff, transform, remove-debuff…), this produces hundreds of unique spell combinations. The system includes specialized sprite factories, a collision handler, and a passive effect manager (XiuLian/修炼).

Pathfinding — Rust WASM, Zero-Copy Memory

The A* pathfinder is written in Rust, compiled to WebAssembly. It eliminates all FFI overhead through shared linear memory:

  1. JavaScript writes obstacle bitmaps directly into WASM linear memory via Uint8Array views on wasm.memory.buffer
  2. WASM executes A* in-place on shared memory
  3. JavaScript reads path results via Int32Array pointer views — zero serialization, zero copying

Five path strategies (from greedy to full A* with configurable max iterations) let the game trade accuracy for speed. Typical pathfind: ~0.2 ms, roughly 10× faster than the equivalent TypeScript implementation.

Binary Format Decoders

The engine parses 8 binary file formats from the original game — all reverse-engineered and implemented without third-party parsing libraries:

FormatDescription
ASFSprite animation frames (RLE-compressed, palette-indexed RGBA)
MPCResource pack container (bundled sprite sheets)
MAPTile map data (multiple layers, obstacle grid, trap zones)
SHDShadow / height map data for terrain
XNBXNA Binary format (audio assets from the original game)
MSFMiu Sprite Format v2 — custom indexed-palette + zstd compression
MMFMiu Map Format — custom zstd-compressed binary map data
INI/OBJConfig files in GBK (Chinese legacy encoding) and UTF-8

Weather System — Particle-Driven

Particle physics and rendering:

  • Rain — wind-affected particles with splash on contact, periodic lightning flash illuminating the scene
  • Screen droplets — simulated refraction/lens effect of water running down the camera
  • Snow — individual snowflake physics with wobble, spin, drift, and gradual melt

Character System — 8-Level Inheritance

A deep, well-structured class hierarchy with clear separation of concerns:

Sprite
 └─ CharacterBase — stats, properties, status flags
     └─ CharacterMovement — A* pathfinding, tile walking, bezier curves
         └─ CharacterCombat — attack, damage calc, status effects
             └─ Character — shared NPC/Player logic [abstract]
                 ├─ PlayerBase → PlayerCombat → Player
                 └─ Npc — AI behavior, interaction scripts, spatial grid

Game Data Editor (Dashboard)

The project includes a VS Code-style game editor with Activity Bar, Sidebar, and Content panels:

ModuleWhat it edits
Magic EditorSpell config with live ASF sprite preview
NPC EditorStats, scripts, AI behavior, sprite preview
Scene EditorMap data, spawn points, traps, triggers
Item EditorWeapons, armor, consumables, drop tables
Shop EditorStore inventories and pricing
Dialog EditorBranching conversation trees + portrait assignment
Player EditorStarting stats, equipment, skill slots
Level EditorExperience curves and stat growth
Game ConfigGlobal game settings (drops, player defaults)
File ManagerFull file tree with drag-and-drop upload
ResourcesResource browser and viewer integration
StatisticsData overview dashboard

Project Structure

11 packages in a pnpm monorepo, ~176,000 lines total:

PackageRole
@miu2d/enginePure TS game engine — 19 modules, no React dependency
@miu2d/dashboardVS Code-style game data editor (13 modules)
@miu2d/gameGame runtime with 3 UI themes (classic/modern/mobile)
@miu2d/serverHono + tRPC backend (21 tables, 19 routers)
@miu2d/typesShared Zod 4 schemas (18 domain modules)
@miu2d/webApp shell, routing, landing page
@miu2d/converterRust CLI: ASF/MPC → MSF, MAP → MMF batch conversion
@miu2d/engine-wasmRust → WASM: pathfinder, decoders, spatial hash, zstd
@miu2d/viewerResource viewers (ASF/Map/MPC/Audio)
@miu2d/uiGeneric UI components (no business deps)
@miu2d/sharedi18n, tRPC client, React contexts

Also included: resources/ (game assets), docs/ (format specs), JxqyHD/ (C# reference from the original engine).


Quick Start

Requirements: Node.js 18+, pnpm 9+, modern browser with WebGL

git clone https://github.com/nicologies/miu2d.git
cd miu2d
pnpm install
pnpm dev            # → http://localhost:5173

Full Stack (with backend + database)

make init           # Docker: PostgreSQL + MinIO, migrate, seed
make dev            # web + server + db studio concurrently

Commands

CommandPurpose
pnpm devFrontend dev server (port 5173)
make devFull-stack dev (web + server + db)
make tscType check all packages
pnpm lintBiome lint
make testRun engine tests (vitest)
make convertBatch convert game resources (Rust CLI)
make convert-verifyPixel-perfect conversion verification

Controls

Desktop

InputAction
Left click (ground)Move to position
Left click (NPC / object)Interact
Right click (NPC / object)Alternate interact
Ctrl + Left clickAttack in place
QInteract with nearest object
EInteract with nearest NPC
A S D F GCast magic (skill slots 1 – 5)
Z X CUse item (quick slots 1 – 3)
VToggle sitting / meditate (修炼)

Mobile

InputAction
Virtual joystickMove
Tap (NPC / object)Interact

Deployment

TargetMethod
FrontendVercel — pnpm build:web → static SPA
Full StackDocker Compose — PostgreSQL + MinIO + Hono + Nginx

See deploy/ for production Docker configs.


Contributing

  1. Fork → feature branch → reference the dev guide → PR
  2. Run make tsc and pnpm lint before submitting

Credits

  • Original Game: Kingsoft (西山居) — 剑侠情缘外传:月影传说 (2001)
  • JxqyHD — C# reimplementation of the game engine, provided invaluable reference for game logic, magic systems, and data formats
  • JXQY-all-in-one — Cross-platform C++ engine, provided secondary verification and calibration for game mechanics

This is a fan-made learning project. Game assets and IP belong to their original creators.

License

The engine source code is licensed under the MIT License.

Game resources (images, audio, maps, scripts, etc.) are not covered by this license and remain the property of their original creators (Kingsoft / 西山居).


⚔️ Sword spirit spans thirty thousand miles ⚔️

Recreating classic wuxia with modern web technology

关于 About

Miu2D is a 2D RPG game engine built with Rust + TypeScript + React + Canvas, designed for the Web platform. 剑侠情缘2/月影传说/新剑侠情缘网页复刻版

语言 Languages

TypeScript91.2%
Rust4.1%
Python3.1%
JavaScript1.0%
PLpgSQL0.2%
Dockerfile0.1%
Makefile0.1%
Shell0.1%
CSS0.1%
HTML0.0%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
400
Total Commits
峰值: 92次/周
Less
More

核心贡献者 Contributors