ãã®ææžã¯ãC-Dogs SDL 2.4.0 ã Emscripten/WebAssembly ã§ãã©ãŠã¶ãžç§»æ€ããå®äœæ¥ãããšã«ããæ¥æ¬èªã®å®åããã¥ã¢ã«ã§ããã
察象èªè : ããã³ããšã³ããšã³ãžãã¢ãJavaScriptã»ãã©ãŠã¶ã»DOMã»ã€ãã³ãã«ãŒãã®ç¥èã¯ããåæãC èšèªã¯ããã€ã³ã¿ãæ§é äœãmalloc ãåãããçšåºŠã®åºç€ç¥èãæ³å®ãããEmscripten ã¯åããŠäœ¿ãåæã§ãå°éçšèªã¯ãã¹ãŠæ¬æãŸãã¯çšèªéã§èª¬æããã
ä»åã®ç§»æ€ã§ã¯ããã©ãŠã¶ã§ã·ã³ã°ã«ãã¬ã€ãåããããšããŽãŒã«ã«ãããLAN/UDP/ENet ã®ãã«ããã¬ã€ããã£ã³ããŒã³ãšãã£ã¿ãWebSocket/WebRTC ãžã®é信眮æã¯å¯Ÿè±¡å€ã«ããã
Emscripten ã¯ãC/C++ ã®ã³ãŒãã WebAssembly ã«ã³ã³ãã€ã«ããŠããã©ãŠã¶ã§åããããã®ããŒã«äžåŒãã§ãããåãªãã³ã³ãã€ã©ã§ã¯ãªãããã©ãŠã¶ã®äžã«ãã OS ç°å¢ãäœãã©ã³ã¿ã€ã ãŸã§å«ãã§ããã®ããã€ã³ãã
ãã€ãã£ãã® C ããã°ã©ã 㯠OS ã®æ©èœïŒãã¡ã€ã«èªã¿æžãããŠã£ã³ããŠãé³å£°åºåãã¹ã¬ãããæèšâŠïŒãåæã«æžãããŠããããã©ãŠã¶ã«ã¯ããããçŽæ¥ã¯ååšããªãã®ã§ãEmscripten ãæ¬¡ã®ããã«ã翻蚳ãããã
| ãã€ãã£ãã®äžç | ãã©ãŠã¶ã§ã®å®äœïŒEmscripten ãå€æïŒ |
|---|---|
gcc / clang ã§ã³ã³ãã€ã« |
emcc ã§ã³ã³ãã€ã«ïŒåºåã .wasm ã«ãªãïŒ |
fopen("data.txt") ãªã©ã®ãã¡ã€ã« IO |
ã¡ã¢ãªäžã®ä»®æ³ãã¡ã€ã«ã·ã¹ãã ïŒåŸè¿°ã® MEMFS / IDBFSïŒ |
| SDL2 ã®ãŠã£ã³ããŠã»æç» | <canvas> + WebGL |
| SDL_mixer ã®é³å£°åºå | Web Audio API |
| ããŒããŒãã»ããŠã¹å ¥å | DOM ã€ãã³ã â SDL ã€ãã³ãã«å€æ |
while ã«ãŒãã§åãã¡ã€ã³ã«ãŒã |
requestAnimationFrame ã«èŒãæ¿ãïŒåŸè¿°ïŒ |
emcc ã« -o index.html ãæå®ãããšã次ã®4ãã¡ã€ã«ãçæããããããããã®åœ¹å²ãç¥ã£ãŠãããšãããã°ã楜ã«ãªãã
index.htmlâ å ¥å£ã®ããŒãžã<canvas>ãšãããŒãé²æè¡šç€ºã»Fullscreen ãã¿ã³ãªã©ãå ¥ã£ããshellããšåŒã°ãããã³ãã¬ãŒã HTMLãindex.jsâ ããŒããŒå Œã©ã³ã¿ã€ã ã.wasmã® fetch ãšã€ã³ã¹ã¿ã³ã¹åãä»®æ³ãã¡ã€ã«ã·ã¹ãã ã®æ§ç¯ãSDLâWeb API ã®æ©æž¡ãïŒJavaScript åŽã®å®è£ ïŒããã¹ãŠããã«å ¥ã£ãŠãããæ°çŸ KB ããã®ã¯ãã®ãããindex.wasmâ C ã³ãŒãã®ã³ã³ãã€ã«çµææ¬äœãã²ãŒã ããžãã¯ã¯ãããindex.dataâ--preload-fileã§æå®ããã²ãŒã ã¢ã»ããïŒç»åã»é³ã»ãããçïŒã1ãã¡ã€ã«ã«åºããã¢ãŒã«ã€ããèµ·åæã« fetch ãããã¡ã¢ãªäžã®ä»®æ³ãã¡ã€ã«ã·ã¹ãã ã«å±éãããã
ãã©ãŠã¶ã§ã®èµ·åã·ãŒã±ã³ã¹ã¯ãindex.html â index.js ã index.wasm ãš index.data ã fetch â ä»®æ³ FS æ§ç¯ â C ã® main() å®è¡ããšããæµãã«ãªãã
æ¬æã«ç»å Žãã Emscripten / äœã¬ã€ã€çšèªã®ãªãã¡ã¬ã³ã¹ãèªã¿é²ããŠåãããªãåèªãåºããããã«æ»ãã
| çšèª | æå³ |
|---|---|
| WebAssembly (wasm) | ãã©ãŠã¶ã§å®è¡ã§ãããã€ããªåœ¢åŒãC/C++/Rust ãªã©ããã³ã³ãã€ã«ããããJS ããäºæž¬å¯èœãªæ§èœã§ãã²ãŒã ã®ããžãã¯éšåã¯ããã§åã |
| emsdk | Emscripten SDKãemcc æ¬äœãšäŸåããŒã«ïŒLLVMãNode.js ãªã©ïŒããŸãšããŠç®¡çããã€ã³ã¹ããŒã© |
| emcc | Emscripten ã®ã³ã³ãã€ã©ã³ãã³ããäœ¿ãæ¹ã¯ gcc ãšã»ãŒåãïŒ-cã-oã-Iã-D ãªã©ïŒã§ããªã³ã¯æã« Web åºæã®ãªãã·ã§ã³ïŒ-s XXXïŒãè¶³ã |
-s ãªãã·ã§ã³ |
emcc ã®ãªã³ã¯æèšå®ã-s USE_SDL=2 ã®ããã«ãã©ã³ã¿ã€ã ã®æåãã©ã€ãã©ãªã®éžæãæå®ãã |
| Emscripten port | SDL2 ã libpng ãªã©ã®æåã©ã€ãã©ãªã Emscripten ã Web çšã«ãã«ãæžã¿ã§æäŸããä»çµã¿ã-s USE_SDL=2 ãšæžãã ãã§ Web 察å¿ç SDL2 ããªã³ã¯ããããã¹ãïŒPC åŽïŒã®ã©ã€ãã©ãªã¯äžå䜿ããªã |
| ä»®æ³ãã¡ã€ã«ã·ã¹ãã (FS) | Emscripten ããã©ãŠã¶å
ã«äœã POSIX 颚ã®ãã¡ã€ã«ã·ã¹ãã ãC ã³ãŒãããã¯æ®éã« fopen("/graphics/foo.png") ã§èªãã |
| MEMFS | ä»®æ³ FS ã®ããã©ã«ãå®è£ ãå®äœã¯ãã ã®ã¡ã¢ãªãããŒãžããªããŒããããšæ¶ãã |
| IDBFS | IndexedDB ãè£ã«æã€ä»®æ³ FSãæç€ºçã«åæïŒFS.syncfsïŒããã¿ã€ãã³ã°ã§ IndexedDB ã«æ°žç¶åããããã»ãŒãããŒã¿çœ®ãå Žã«äœ¿ã |
FS.syncfs(direction, callback) |
IDBFS ãš IndexedDB ã®åæãè¡ã JS APIãsyncfs(true, ...) 㯠IndexedDBâã¡ã¢ãªïŒèªã¿èŸŒã¿ïŒãsyncfs(false, ...) ã¯ã¡ã¢ãªâIndexedDBïŒæžã蟌ã¿ïŒãéåæã§ããç¹ã«æ³šæ |
--preload-file |
æå®ãããã£ã¬ã¯ããªã index.data ã«åºããŠãèµ·åæã« MEMFS ãžå±éãããªã³ã¯ãªãã·ã§ã³ |
| ã¡ã€ã³ã«ãŒã (main loop) | ã²ãŒã ã®ãå
¥ååŠçâæŽæ°âæç»ããæ¯ãã¬ãŒã ç¹°ãè¿ãã«ãŒãããã©ãŠã¶ã§ã¯ C ã® while ã«ãŒãã®ãŸãŸã§ã¯åãããªãïŒåŸè¿°ïŒ |
emscripten_set_main_loop_arg |
C ã®é¢æ°ãããã©ãŠã¶ã«æ¯ãã¬ãŒã åŒãã§ãããã圢ã§ç»é²ãã Emscripten APIãJS ã® requestAnimationFrame ã«çžåœããä»çµã¿ãžã®ç»é²å£ |
| ASYNCIFY | C ã®åæçãªã³ãŒãïŒããããã³ã°åŠçïŒããäžæåæ¢ã»åéã§ãã圢ã«å€æãããªã³ã¯ãªãã·ã§ã³ãJS ã§èšãã°ãåæã³ãŒããåæã« async/await åããŠããããã€ã¡ãŒãžãã€ãã³ãã«ãŒããæ¢ããã«æžã |
__EMSCRIPTEN__ |
Emscripten ã§ã³ã³ãã€ã«ãããšãã ãå®çŸ©ããããã¯ãã#ifdef __EMSCRIPTEN__ ã§ãã©ãŠã¶å°çš/é€å€ã³ãŒããåå²ãã |
| EM_ASM | C ã³ãŒãã®äžã« JavaScript ãçŽæ¥åã蟌ããã¯ããEM_ASM( console.log('hi'); ); ã®ããã«æžãããCâJS åŒã³åºãã®æçææ®µ |
| Module | Emscripten ã©ã³ã¿ã€ã ã®èšå®ã»ç¶æ ãæã€ JS ã®ã°ããŒãã«ãªããžã§ã¯ããshell HTML ã¯ãããéã㊠canvas ãã¹ããŒã¿ã¹è¡šç€ºãã©ã³ã¿ã€ã ã«æž¡ã |
| shell (HTML) | emcc -o index.html ãçæããããã©ã«ãã®ããŒãžãã³ãã¬ãŒã |
| SAFE_HEAP | ã¡ã¢ãªã¢ã¯ã»ã¹ãå®è¡æãã§ãã¯ãããããã°ãªãã·ã§ã³ãäžæ£ãªçªå°ã»ã¢ã©ã€ã³ã¡ã³ãéåãããã®å Žã§ãšã©ãŒã«ããŠããã |
| ASSERTIONS | Emscripten ã©ã³ã¿ã€ã å éšã®æŽåæ§ãã§ãã¯ãæå¹åãããããã°ãªãã·ã§ã³ |
| source map | .wasm ã®åœä»€äœçœ®ãš C ã®ãœãŒã¹è¡ã察å¿ä»ãããã¡ã€ã«ããã©ãŠã¶ã® DevTools ã§ã¹ã¿ãã¯ãã¬ãŒã¹ã vswap.c:53 ã®ããã« C ã®è¡çªå·ã§åºãããã«ãªã |
| ã¢ã©ã€ã³ã¡ã³ã (alignment) | ã4ãã€ãã®æŽæ°ã¯4ã®åæ°ã®ã¢ãã¬ã¹ããèªãããšããã¡ã¢ãªé çœ®ã®æ±ºãŸãã詳现ã¯è©²åœç« ã§èª¬æ |
| packed struct | #pragma pack(1) ã§è©°ãç©ïŒããã£ã³ã°ïŒãé€å»ããæ§é äœããã¡ã€ã«ã®ãã€ããªã¬ã€ã¢ãŠããšäžèŽãããããã«äœ¿ãããããã¢ã©ã€ã³ã¡ã³ãéåã®æž©åºã«ãªã |
æåã«ããã¹ãããšã¯ããããªã emcc ã§å
šéšããã«ãããããšã§ã¯ãªãããŸããå
ã³ãŒãããã©ãŠã¶ã«æã¡èŸŒãã圢ãããŠãããã確èªããã
-
å ã³ãŒãã®ããŒãžã§ã³ãåºå®ããã
- ä»å㯠C-Dogs SDL 2.4.0 ã察象ã«ããã
- ç§»æ€äžã¯ upstream ã®ææ°çã远ãç¶ããªãããŸã 1 ã€ã®ã¿ã°ãã³ãããã«åºå®ããåã baseline ãäœãã
-
æ¢åã® Web/Emscripten 察å¿ãæ¢ãã
rg -n "EMSCRIPTEN|emscripten|IDBFS|syncfs|set_main_loop|SDL_Delay" src- ä»åã® upstream ã«ã¯
make_emscripten.shã__EMSCRIPTEN__åå²ãIDBFS ã®åæåã³ãŒãïŒã»ãŒãããŒã¿ã IndexedDB ã«çœ®ãä»çµã¿ãåŸè¿°ïŒããã©ãŠã¶çšã¡ã€ã³ã«ãŒããžã®å¯Ÿå¿ïŒåŸè¿°ïŒãæ¢ã«äžéšãã£ãã - æ¢åã³ãŒããããå Žåã§ãããã®ãŸãŸçŸåšã® emsdk ã§åããšã¯éããªããäŸåããŒã«ãSDL port æå®ãã¡ã¢ãª/ã¹ã¿ãã¯èšå®ãCMake èŠä»¶ãå€ãããšãããã
-
ãã€ãã£ãåæã®äŸåãåé¡ããã
- SDL2 / SDL_image / SDL_mixer: Emscripten port ã§ä»£æ¿ã§ããïŒïŒWeb 察å¿çãå ¬åŒã«çšæãããŠããïŒã
- OpenGL / editor / native UI: ãã©ãŠã¶çã®å¯Ÿè±¡å€ãªããã«ãããªãã
- UDP / LAN scan / raw socket / ENet: ãã©ãŠã¶ã¯çã® UDP ãœã±ãããäžåéããªãããããã®ãŸãŸã§ã¯åããªããä»å㯠UI ããé ããŠèµ·åããªãæ¹éã«ããã
- ãã¡ã€ã«ã·ã¹ãã : èªã¿èŸŒã¿å°çšã®ã²ãŒã ããŒã¿ã¯ preloadïŒ
index.dataã«åºããïŒãèšå®ãã»ãŒã㯠IDBFSïŒIndexedDB æ°žç¶åïŒã«åããã
-
ãã«ãã·ã¹ãã ãäœãçæããŠããããèŠãã
- CMake ã configure ãæ¬åœã«å¿
èŠãªã®ããåã«
config.hã®çæã ããªã®ãã確èªããã - ä»å㯠upstream ã®
cmake .ã host SDL2ãOpenGLãeditorãENet ã® CMake èŠä»¶ãåŒã蟌ãã ãããCMake å šäœãéãã®ã§ã¯ãªãsys_config.hãš YAJL header ã ããçæããå°çšã¹ã¯ãªããã«ããã
- CMake ã configure ãæ¬åœã«å¿
èŠãªã®ããåã«
-
ãã©ãŠã¶ã«åºãå ¥åºåãæŽãåºãã
- å ¥å: ããŒããŒããããŠã¹ãã²ãŒã ãããããã€ã³ã¿ããã¯ããã«ã¹ã¯ãªãŒã³ã
- åºå: SDL2 ã® canvas æç»ãSDL_mixer ã® Web Audio åºåããã°ããã©ãŠã¶ consoleã
- æ°žç¶å: configãplayer templateãscoreãautosaveã
- é
åžç©:
index.html,index.js,index.wasm,index.data, favicon, OGP ç»åã
åºæ¬æ¹éã¯ãWeb å°çšã®å¥ã²ãŒã ãäœããªããããšãå
ã³ãŒãã®å€§éšåã¯ãã®ãŸãŸäœ¿ãããã©ãŠã¶ã§æç«ããªãå¢çã ãã __EMSCRIPTEN__ïŒEmscripten ãã«ãæã®ã¿æå¹ã«ãªã #ifdef åå²ïŒã§åãæ¿ããã
å ã³ãŒãã«æãå ¥ããå¿ èŠã¯ããããã ããå ¥ããå Žæã¯éå®ããã
-
å¿ èŠãªå€æŽ:
- ãã©ãŠã¶ main loop ãžã®æ¥ç¶ã
- ããããã³ã° delayïŒåŠçãæ¢ããŠåŸ
ã€
SDL_Delayãªã©ïŒã®åæžã - UDP/ãã«ããã¬ã€å°ç·ã®ç¡å¹åã
- config/save ã®ä¿åå ã IDBFS ã«åããã
- wasm ã§é¡åšåããæªå®çŸ©åäœãç¹ã«ã¢ã©ã€ã³ã¡ã³ãéåã®ä¿®æ£ã
- native linker ã§ã¯éã£ãŠããéè€å®çŸ©ãªã©ã®ä¿®æ£ã
-
é¿ãã倿Ž:
- ã²ãŒã ããžãã¯ã Web çšã«å€§ããåå²ããã
- SDL2 ãçŽæ¥ DOM API ã«çœ®ãæããã
- ãã€ãã£ãçã®æåãŸã§å€ããã
- åããåã« asset åå²ã UI äœã蟌ã¿ãžé²ãã
æåã® milestone ã¯ãã¡ã€ã³ã¡ãã¥ãŒã衚瀺ããããããšã次ã«ãã·ã³ã°ã«ãã¬ã€ãéå§ã§ãããããã®æ¬¡ã«ãé³ã鳎ãããä¿åãæ®ããããã«ã¹ã¯ãªãŒã³ãé ä¿¡ãã¹ãå£ããªããã確èªããã
Emscripten ã®ã€ã³ã¹ããŒã«ã¯ emsdk ã§è¡ããemsdk 㯠emcc æ¬äœãšããã®äŸåïŒLLVMãNode.jsïŒãããŒãžã§ã³ç®¡çä»ãã§ãŸãšããŠå
¥ããŠãããã€ã³ã¹ããŒã©ã§ãnvm ã® Emscripten çã®ãããªãã®ã ãšæãã°ããã
ä»åã®äœæ¥ã§ã¯ããã¹ãç°å¢ãå€ãããšãåé¡ã«ãªã£ããEmscripten 6 㯠Python 3.10+ ãèŠæ±ãïŒemcc ã®å®äœã¯ Python ã¹ã¯ãªããïŒãupstream ã® CMake åšèŸºãæ°ããã® CMake ãèŠæ±ãããã·ã¹ãã å
šäœãæŽæ°ããã®ã§ã¯ãªãããªããžããªé
äžã® tools/ ã«ããŒã«ã«ã€ã³ã¹ããŒã«ããŠéã蟌ããã
確èªããæ§æ:
- Emscripten: 6.0.3
- SDL: Emscripten port ã® SDL 2.32.10
- SDL_mixer: Emscripten port ã® SDL_mixer 2.8.0
- CMake: 3.29.6
- Python: 3.10.18
ã»ããã¢ããäŸ:
mkdir -p tools
curl -fsSL \
https://github.com/Kitware/CMake/releases/download/v3.29.6/cmake-3.29.6-linux-x86_64.tar.gz \
-o tools/cmake-3.29.6-linux-x86_64.tar.gz
tar -xzf tools/cmake-3.29.6-linux-x86_64.tar.gz -C tools
git clone https://github.com/emscripten-core/emsdk.git tools/emsdk
cd tools/emsdk
./emsdk install latest
./emsdk activate latest
cd ../..
mkdir -p tools/python-shim
ln -sf /usr/bin/python3.10 tools/python-shim/python3python-shim ã¯å°æ: emcc 㯠PATH äžã® python3 ã䜿ãã®ã§ãpython3.10 ãžã®ã·ã³ããªãã¯ãªã³ã¯ã ãå
¥ãããã£ã¬ã¯ããªã PATH ã®å
é ã«è¶³ãã°ãã·ã¹ãã ã® Python ãè§Šããã«èŠä»¶ãæºãããã
build_wasm.sh ã§ã¯æ¬¡ã®ããã« toolchain ãæç€ºããïŒemsdk_env.sh ã source ãããš emcc ã PATH ã«å
¥ãïŒã
source "$ROOT_DIR/tools/emsdk/emsdk_env.sh" >/dev/null
export PATH="$ROOT_DIR/tools/python-shim:$ROOT_DIR/tools/cmake-3.29.6-linux-x86_64/bin:$PATH"upstream ã® make_emscripten.sh 㯠cmake . ããå§ãŸãããããããã® configure ã¯ãã©ãŠã¶ãã«ãã«äžèŠãª host SDL2ãOpenGLãeditor targetãENet ã® CMake èŠä»¶ãŸã§èŠã«è¡ããEmscripten ã§ã¯ SDL2/SDL_image/SDL_mixer 㯠Emscripten portïŒWeb 察å¿çã®ãã«ãæžã¿ã©ã€ãã©ãªïŒã䜿ããããPC ã«ã€ã³ã¹ããŒã«ããã SDL ãæ¢ãå¿
èŠããããããªãã
ä»åã® build_wasm.sh 㯠CMake ãäžžããšäœ¿ãããæ¬¡ã ããçŽæ¥è¡ãã
-
sys_config.h.cmakeããsrc/cdogs/sys_config.hãçæããã- version ã¯
2.4.0ã - data dir ã¯
./ïŒä»®æ³ FS ã®ã«ãŒãçŽäžã«ã¢ã»ãããå±éãããããïŒã - config dir ã¯ç©ºã«ããŠãå®éã®ä¿åå
㯠Emscripten åŽã®
/persistent_dataã«å¯ããã
- version ã¯
-
YAJL ã® public header ã build include ã«é 眮ãã
yajl_version.hãçæããã -
å¿ èŠãª C ãœãŒã¹ã
findã§éããŠemcc -cããïŒgcc -cãšåãæèŠã§ã1ãã¡ã€ã«ãã€ãªããžã§ã¯ããã¡ã€ã«ã«ã³ã³ãã€ã«ïŒãsrc/*.csrc/base64/*.cãã ãmain.cã¯é€å€src/cdogs/**/*.csrc/json/*.csrc/proto/*.csrc/proto/nanopb/*.c
-
Emscripten port ãš asset preload ãæå®ã㊠link ããã
-D "PB_FIELD_16BIT=1" # nanopb ã®èšå®ãã¯ãïŒEmscripten åºæã§ã¯ãªãïŒ
-s USE_SDL=2 # SDL2 ã® Emscripten port ã䜿ã
-s USE_SDL_IMAGE=2 # SDL2_image ã® port ã䜿ã
-s SDL2_IMAGE_FORMATS='["png"]' # ç»åãã³ãŒã㯠PNG ã®ã¿çµã¿èŸŒãïŒãµã€ãºåæžïŒ
-s USE_SDL_MIXER=2 # SDL2_mixer ã® port ã䜿ã
-s SDL2_MIXER_FORMATS='["ogg"]' # é³å£°ãã©ãŒããã㯠OGG ã®ã¿
-s USE_VORBIS=1 # OGG åçã«å¿
èŠãª vorbis ãã³ãŒã
-s USE_OGG=1 # åãã ogg ã³ã³ãã
-lidbfs.js # IDBFSïŒIndexedDB æ°žç¶å FSïŒããªã³ã¯ãã
-s ASYNCIFY # ããããã³ã°ã³ãŒããã€ãã³ãã«ãŒãã«è¿ãããã倿
-s STACK_SIZE=2097152 # C ã®ã¹ã¿ãã¯ã 2MB ã«æ¡åŒµïŒããã©ã«ã㯠64KB ãããªãïŒ
-s INITIAL_MEMORY=128mb # wasm ã®åæã¡ã¢ãªãã²ãŒã ïŒã¢ã»ããå±éåã®äœè£ãæãããç¹ã«æ³šæãã¹ã㯠STACK_SIZEããã€ãã£ãã®ã¹ã¬ããã¹ã¿ãã¯ã¯éåžž 8MB çšåºŠããããEmscripten ã®ããã©ã«ã㯠64KB ãããªãããã€ãã£ãã§æ®éã«åããŠããã³ãŒããããã©ãŠã¶ã§ã ãã¹ã¿ãã¯ãªãŒããŒãããŒã§èœã¡ãåå ã«ãªãïŒå®éã«èœã¡ãããããã°ç« åç
§ïŒã
debug build ã§ã¯æ¬¡ãè¶³ãã
-O0 # æé©åãªãïŒå€æ°ãã¹ã¿ãã¯ãã¬ãŒã¹ãçŽ çŽã«ãªãïŒ
-gsource-map # source map ãçæïŒC ã®è¡çªå·ã§ stack trace ãåºãïŒ
--source-map-base ./ # source map ã®åç
§ãã¹ããµããã£ã¬ã¯ããªé
ä¿¡ã§ã解決ã§ããããçžå¯Ÿã«
-s ASSERTIONS=2 # ã©ã³ã¿ã€ã å
éšã®æŽåæ§ãã§ãã¯
-s SAFE_HEAP=1 # ãã¹ãŠã®ã¡ã¢ãªã¢ã¯ã»ã¹ãå®è¡æãã§ãã¯
-s STACK_OVERFLOW_CHECK=2 # ã¹ã¿ãã¯ãªãŒããŒãããŒæ€åºSAFE_HEAP=1 ã¯å®è¡ãç®ã«èŠããŠé
ããªãããwasm ç§»æ€ã§ã¯æéèŠã®ãããã°ãªãã·ã§ã³ãåŸè¿°ã® packed struct ã¢ã©ã€ã³ã¡ã³ããã°ã¯ããããæå¹ã«ããŠãããããã§ãã©ã®ãœãŒã¹è¡ãæªããããŸã§ç¹å®ã§ããã
ãã©ãŠã¶ã® JavaScript ã«ã¯ããŒã«ã«ãã¡ã€ã«ãçŽæ¥èªãææ®µããªããEmscripten ã¯ããã解決ããããã«ããã©ãŠã¶ã®ã¡ã¢ãªäžã« POSIX 颚ã®ä»®æ³ãã¡ã€ã«ã·ã¹ãã ãäœããC ã³ãŒãåŽã¯ä»ãŸã§éã fopen("/graphics/foo.png", "rb") ãšæžãã ãã§ãããEmscripten ã©ã³ã¿ã€ã ãã¡ã¢ãªäžã®ããŒã¿ãè¿ãã
ã²ãŒã ããŒã¿ããã®ä»®æ³ FS ã«èŒããäžçªç°¡åãªæ¹æ³ã --preload-file ã§ãæå®ãã£ã¬ã¯ããªããã«ãæã« index.data ãšãã1ã€ã®ã¢ãŒã«ã€ãã«åºããèµ·åæã« fetch ããŠã¡ã¢ãªäžïŒMEMFSïŒã«å±éããã
--preload-file data
--preload-file doc
--preload-file dogfights
--preload-file graphics
--preload-file missions
--preload-file music
--preload-file soundssys_config.h ã® data dir ã ./ ã«ããŠãããããã²ãŒã åŽã¯ /graphics, /missions, /music ã®ãããªéåžžã®ãã¹ã§èªããã
ä»åã® index.data ã¯çŽ 94MBãæåã¯å€§ãããŠãããããŸãæ£ããåããããã®åŸã§ editor-only assetãäžèŠ docsãæªäœ¿çšé³æºã远å campaign ãªã©ãåããæåãã lazy load ãåå²é
ä¿¡ãèšèšãããšãããã¡ã€ã«ãç¡ããŠèœã¡ãŠããã®ããç§»æ€ãã°ã§èœã¡ãŠããã®ããã®åãåããé£ãããªãã
MEMFS ã¯ãã ã®ã¡ã¢ãªãªã®ã§ãããŒãžããªããŒããããšãã¹ãŠæ¶ãããèšå®ãã»ãŒãããŒã¿ã®ããã«æ®ããããã®ã¯ãIndexedDB ãè£ã«æã€ IDBFS ã«çœ®ãã
IDBFS ã®èãæ¹ã¯ããã³ããšã³ããšã³ãžãã¢ã«ã¯éŠŽæã¿ããã:
- C ã³ãŒãããèŠããš
/persistent_dataãšããæ®éã®ãã£ã¬ã¯ããªã - å®äœã¯ã¡ã¢ãªäžïŒMEMFS ãšåãïŒã ãã
FS.syncfs()ãåŒãã ã¿ã€ãã³ã°ã§ IndexedDB ãšåæãããã FS.syncfs(true, cb)= IndexedDB â ã¡ã¢ãªïŒèµ·åæã®èªã¿æ»ãïŒãFS.syncfs(false, cb)= ã¡ã¢ãª â IndexedDBïŒä¿ååŸã®æžãåºãïŒã- localStorage ãšéãåæã¯éåæã§ãã³ãŒã«ããã¯ã§å®äºãç¥ãã
ä»åã®å
ã³ãŒãåŽã§ã¯ã__EMSCRIPTEN__ ã®ãšã config dir ã /persistent_data/ ã«ããã
#ifdef __EMSCRIPTEN__
return "/persistent_data/";
#endifèµ·åæã« IDBFS ã mount ããIndexedDB ããèªã¿æ»ããããã§äœ¿ã£ãŠãã EM_ASM(...) ã¯ãC ã³ãŒãã®äžã« JavaScript ããã®ãŸãŸæžãããEmscripten ã®ãã¯ãã§ãäžèº«ã¯èŠãŠã®éããã ã® JS ã§ããã
#ifdef __EMSCRIPTEN__
EM_ASM(
FS.mkdir('/persistent_data'); // ä»®æ³FSã«ãã£ã¬ã¯ããªäœæ
FS.mount(IDBFS, {}, '/persistent_data'); // ããã« IDBFS ãããŠã³ã
FS.syncfs(true, function(err) { assert(!err); }); // IndexedDB ããèªã¿æ»ã
);
#endifä¿ååŸã¯ FS.syncfs(false, ...) ãåŒãã§ IndexedDB ãžæžãæ»ããä»åã®ã³ãŒãã§ã¯ configãautosaveãplayer templateãYAJL çµç±ã®ä¿ååŠçãªã©è€æ°ç®æãã sync ããã
泚æç¹:
FS.syncfsã¯éåæãC åŽã®ä¿å颿°ã return ããæç¹ã§ã¯ããŸã IndexedDB ã«æžãçµãã£ãŠããªããä¿åçŽåŸã«ãã©ãŠã¶ãéããã±ãŒã¹ãèæ ®ããã- è€æ°ç®æããåæã«åŒã¶ãš
N FS.syncfs operations in flightèŠåãåºããä»åã¯éèŽåœãšããŠæ®ããããä»äžãã§ã¯ debounce ãŸãã¯ãã¥ãŒåããã - IndexedDB 㯠originïŒãããã³ã«+ãã¡ã€ã³+ããŒãïŒããšã«åé¢ããããé ä¿¡ URL ãå€ãããšå¥ã®ä¿åé åã«ãªãããŠãŒã¶ãŒããèŠããšã»ãŒãããŒã¿ãæ¶ããããã«èŠããã
ããã Emscripten ç§»æ€ã®æéèŠãã€ã³ãã
ãã€ãã£ãã²ãŒã ã¯å žåçã«æ¬¡ã®ãããªç¡éã«ãŒãã§åãã
while (running) {
poll_input();
update();
draw();
delay(); // 次ã®ãã¬ãŒã ãŸã§åŸ
ã€
}äžæ¹ãã©ãŠã¶ã® JavaScript ã¯ã·ã³ã°ã«ã¹ã¬ããã®ã€ãã³ãã«ãŒãã§åããŠãããwasm ã®ã³ãŒããåãã¹ã¬ããã§å®è¡ããããããC ã® while ã«ãŒããåãã£ã±ãªãã«ãããš JS ã§ while(true){} ãæžããã®ãšåãç¶æ
ã«ãªã: ç»é¢ã¯æŽæ°ããããã¯ãªãã¯ãå¹ãããã¿ãããå¿çãªããã«ãªãã
ããã§ Emscripten ã§ã¯ãã«ãŒããèªåã§åããã®ããããã1ãã¬ãŒã åã®åŠçã颿°ã«ããŠããã©ãŠã¶ã«æ¯ãã¬ãŒã åŒãã§ãããã圢ã«å€ãããããã emscripten_set_main_loop_arg ã§ãJS ã® requestAnimationFrame(callback) ã C ãã䜿ãããã® API ã ãšæãã°ããã
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop_arg(EmscriptenMainLoop, &ctx, 0, 1);
#else
while (!ctx.done)
{
GameLoopStep(&ctx);
}
#endifåŒæ°ã®æå³:
- 第1åŒæ°: æ¯ãã¬ãŒã åŒã°ãã颿°ïŒ1ãã¬ãŒã åã® pollâupdateâdraw ãè¡ãïŒã
- 第2åŒæ°: ãã®é¢æ°ã«æž¡ããã€ã³ã¿ïŒJS ã®ã³ãŒã«ããã¯ã«
thisçžåœãæž¡ãã€ã¡ãŒãžïŒã - 第3åŒæ° = FPS æå®ã
0ã«ãããšrequestAnimationFrameããŒã¹ã«ãªãããã£ã¹ãã¬ã€ã®ãªãã¬ãã·ã¥ã«åæãããæ£ã®å€ãå ¥ãããšsetTimeoutããŒã¹ã«ãªãããã©ãŠã¶ããèŠåãåºããããã¬ãŒã ããŒã·ã³ã°ãæªããåºæ¬ã¯0äžæã - 第4åŒæ° = 1 ã«ãããšããã®åŒã³åºãããåŸããžåŠçãè¿ããªãïŒã«ãŒãç»é²ããŠå³åº§ã«å¶åŸ¡ããã©ãŠã¶ã«è¿ãïŒã
ãŸããloading screen ãªã©ã§ SDL_Delay(70)ïŒ70ms éã¹ã¬ãããæ¢ããŠåŸ
ã€é¢æ°ïŒãåŒãã§ããç®æã¯ __EMSCRIPTEN__ ã§ skip ããããã€ãã£ãã§ã¯ç¡å®³ãªçã delay ã§ãããã©ãŠã¶ã§ã¯ã€ãã³ãã«ãŒãã 70ms æ¢ããããšã«ãªããèŠãç®ä»¥äžã«æªããããã
#ifndef __EMSCRIPTEN__
SDL_Delay(70);
#endifãšã¯ãããæ¢åã® C ã³ãŒããããåŠçãæ¢ããŠåŸ
ã€ããã¿ãŒã³ãå®å
šã«æé€ããã®ã¯å€§å€ã§ãã¢ã»ããã®ããŒãåŸ
ã¡ããã¡ã€ã«åæåŸ
ã¡ãªã©ãæ®ããããã§ -s ASYNCIFY ã䜿ãã
ASYNCIFY ã¯ãC ã®åæã³ãŒããäžæåæ¢ã»åéã§ãã圢ã«ã³ã³ãã€ã«æå€æããä»çµã¿ãããããã³ã°ããç®æã«æ¥ããšãC ã®å®è¡ç¶æ ïŒã³ãŒã«ã¹ã¿ãã¯ïŒãä¿åããŠãã©ãŠã¶ã®ã€ãã³ãã«ãŒãã«å¶åŸ¡ãè¿ããæ¡ä»¶ãæŽã£ããä¿åããç¶æ ããåéãããJS ã§èšãã°ãåæé¢æ°ãèªåã§ async/await ã«æžãæããŠãããããããªãã®ã
ãã ã ASYNCIFY ã¯äžèœã§ã¯ãªãããã€ããªãµã€ãºãšå®è¡ã³ã¹ããå¢ãããããæ°žä¹ ã«ãŒããé·æé delay ãæŸçœ®ããŠããããšããæå³ã§ããªãããããŸã§æ®ã£ãŠããŸã£ãããããã³ã°ãã¿ãŒã³ã®ææžçãšèããã
SDL2 ã®ããŒããŒããããŠã¹ãã²ãŒã ãããå
¥å㯠Emscripten ã® SDL2 port ã DOM ã€ãã³ãïŒkeydown / mousemove ãªã©ïŒãã SDL ã€ãã³ããžå€æããŠããããã²ãŒã åŽã¯ååãšããŠæ¢åã® SDL_PollEvent çµè·¯ããã®ãŸãŸç¶æããã°ããã
ç§»æ€æã«ç¢ºèªããããš:
- ååã¯ãªãã¯åŸã« canvas ã focus ãæã€ãïŒfocus ããªããšããŒå ¥åã canvas ã«å±ããªãïŒã
- ããŒããŒãæäœã§ã¡ãã¥ãŒãç§»åã§ãããã
- mouse capture / pointer lockïŒããŠã¹ã«ãŒãœã«ãæ¶ããŠçžå¯Ÿç§»åã ãåã APIïŒã䜿ããªããuser gestureïŒã¯ãªãã¯çã®ãŠãŒã¶ãŒæäœïŒã®äžã§åŒãã§ãããããã©ãŠã¶ã®ã»ãã¥ãªãã£å¶çŽã§ããŠãŒã¶ãŒæäœèµ·ç¹ã§ãªããšæåŠãããã
- ãã«ã¹ã¯ãªãŒã³åæ¿ã SDL2 port ãšç«¶åããªããïŒäžèšã®å®äŸåç §ïŒã
- mobile/touch ã¯å¥ã¿ã¹ã¯ã«åããC-Dogs SDL ã®ãããªããŒããŒãåæã²ãŒã ã§ã¯ããŸã desktop browser ãæç«ãããã
ä»åèžãã äžã§äžçªãEmscripten ãããããã°ãªã®ã§è©³ããæžãã
çç¶: shell HTML ã® Fullscreen ãã¿ã³ãæŒããšç»é¢ãçã£é»ã«ãªãïŒé³ã¯é³Žãç¶ããïŒããã«ã¹ã¯ãªãŒã³ãè§£é€ãããš canvas ããšèŠããªããªãã
åå : shell ã® Fullscreen ãã¿ã³ã¯ Module.requestFullscreen() ãåŒã³ãEmscripten ã©ã³ã¿ã€ã å
éšã® Browser.updateCanvasDimensions() ãšãã canvas ãªãµã€ãºåŠçã«å
¥ãããã®åŠç㯠canvas.widthNative ãšããç¬èªããããã£ãåç
§ããããSDL2 port 㯠canvas ãµã€ãºãèªåã§ç®¡çããŠããŠãã®ããããã£ãèšå®ããªããçµæãundefined ã䜿ã£ãèšç®ã§ canvas.width = NaN ãšãªããcanvas ã« NaN ã代å
¥ãããš 0 ã«äžžãããããã canvas ã 0x0 ã«ãªããè§£é€æãåãèšç®ãéãã®ã§ 0x0 ã®ãŸãŸæ»ããªãã
ã€ãŸããshell ã® Fullscreen ãã¿ã³ããšãSDL2 portãã¯çµã¿åãããæªããã©ã³ã¿ã€ã ã®ã³ãŒãã¯çŽããªãïŒçæç©ãªã®ã§ïŒãããEmscripten ã®çµè·¯ã䜿ãããçŽ ã® Fullscreen API ã canvas ã«çŽæ¥äœ¿ããã HTML ãåŸåŠçããã
<style>
#canvas:fullscreen {
width: 100%;
height: 100%;
object-fit: contain; /* ã¢ã¹ãã¯ãæ¯ãä¿ã£ãŠã¬ã¿ãŒããã¯ã¹è¡šç€º */
background: #000;
image-rendering: pixelated; /* ãããçµµããŒãããæ¡å€§ */
}
</style>
<script>
addEventListener('load', () => {
const canvas = document.getElementById('canvas');
const btn = document.querySelector('input[value=Fullscreen]');
if (!canvas || !btn) return;
btn.onclick = () => {
canvas.requestFullscreen().then(() => {
const lock = document.getElementById('pointerLock');
if (lock && lock.checked && canvas.requestPointerLock) {
canvas.requestPointerLock();
}
}).catch(e => console.error('fullscreen failed:', e));
};
});
</script>ãã€ã³ãã¯ãcanvas ã®æç»ãããã¡ïŒcanvas.width/heightãããã§ã¯ 640x480ïŒã«ã¯äžåè§ŠãããCSS ã ãã§æ¡å€§è¡šç€ºããããšããã«ã¹ã¯ãªãŒã³è§£é€æã®åŸ©å
ããã©ãŠã¶ãåæã«ãã£ãŠããããSDL2 port ã¯ããŠã¹åº§æšã CSS ãµã€ãºãšã®æ¯çã§å€æããŠãããã®ã§ãã¯ãªãã¯åº§æšããããªãã
SDL2 renderer 㯠Emscripten port ã«ãã WebGL/canvas ã«åºããã²ãŒã åŽã§çŽæ¥ DOM ãè§Šãå¿ èŠã¯ãªãã確èªããã®ã¯æ¬¡ã®ç¹ã
dist/index.htmlã HTTP ã§é ä¿¡ããŠããããfile://ã§çŽæ¥éããŠãåããªãïŒ.wasmãš.dataã® fetch ããããã¯ãããïŒã- canvas ã衚瀺ããããã
- loading screen ãåºããã
- menu ãš gameplay ã®äž¡æ¹ã§ç»é¢æŽæ°ããããã
- ãã«ã¹ã¯ãªãŒã³åŸã« canvas ãµã€ãºãå£ããªããã
SDL_mixer 㯠Emscripten port ã® Web Audio çµç±ã§åããä»åã® build ã§ã¯ OGG ã䜿ããããlink æã« SDL_mixer formats ãš ogg/vorbis ãæå®ããã
-s USE_SDL_MIXER=2
-s SDL2_MIXER_FORMATS='["ogg"]'
-s USE_VORBIS=1
-s USE_OGG=1ãã©ãŠã¶ã«ã¯ autoplay å¶éïŒãŠãŒã¶ãŒæäœããåã«é³ãåºããªãå¶çŽïŒããããé³ã鳎ããªãå Žåã¯ãååã¯ãªãã¯ãããŒå
¥ååŸã« AudioContext ã resume ãããŠããããèŠããä»åã®äœæ¥ã§ã¯ SDL_mixer åæåãšã¡ãã¥ãŒå°éã確èªããScriptProcessorNode is deprecated ã® warning 㯠Emscripten/SDL å
éšç±æ¥ã®éèŽåœ warning ãšããŠæ±ã£ãã
C ã® printf ããã®ã³ã°ã¯ããã®ãŸãŸãã©ãŠã¶ã® console ã«åºãïŒshell HTML ã§ã¯ç»é¢äžã® textarea ã«ãåºãïŒãèµ·åã§è©°ãŸããšãã¯ãå
ã³ãŒãã«äžæç㪠checkpoint log ãå
¥ãã䟡å€ãããã
ä»åå ¥ãã芳枬ç¹:
- font loading ååŸ
- loading screen åæå
- picture loading
- data dir / config dir
- campaign load
ãã ããåäœç¢ºèªåŸã¯ãã°ãåãã debug éå®ã«ããã
ä»åã®å ã³ãŒãåŽã®å€æŽã¯ãããããæ¬¡ã®ã«ããŽãªã
-
src/game_loop.cemscripten_set_main_loop_arg(EmscriptenMainLoop, &ctx, 0, 1)ã䜿ãã- frame rate
0ã«ããŠrequestAnimationFrameã«ä¹ããã
-
src/loading_screens.c- wasm ã§ã¯
SDL_Delay(70)ã skipã
- wasm ã§ã¯
-
src/mainmenu.cJoin LAN gameã__EMSCRIPTEN__ã§ã¯è¡šç€ºããªãã
-
src/prep.cStartServeroptions ã__EMSCRIPTEN__ã§ã¯è¡šç€ºããªãã- wasm ã§ã¯ server startup ãžé²ãŸãªãã
æ¹éãšããŠãENet ã³ãŒããå®å šåé€ããããããŸã㯠compile å¯Ÿè±¡ã«æ®ã㊠UI/èµ·åå°ç·ãå¡ãã ãããã«ãã upstream ãšã®å·®åãå°ãããã·ã³ã°ã«ãã¬ã€ã® bring-up ã«éäžã§ããã
-
src/cdogs.c- èµ·åæã«
/persistent_dataãäœæãIDBFS mountãåæFS.syncfs(true)ã
- èµ·åæã«
-
src/cdogs/files.c- Emscripten ã§ã¯ config dir ã
/persistent_data/ã«ããã
- Emscripten ã§ã¯ config dir ã
-
src/autosave.c -
src/cdogs/player_template.c -
src/cdogs/config_json.c -
src/cdogs/yajl_utils.c- ä¿ååŸã«
FS.syncfs(false, ...)ã
- ä¿ååŸã«
wasm ç§»æ€ã§äžçªéèŠã ã£ããã°çŸ€ããŸãåæç¥èããã
ã¢ã©ã€ã³ã¡ã³ããšã¯: CPU ãã¡ã¢ãªãã 4 ãã€ãæŽæ°ïŒuint32_tïŒãèªããšããã4 ã®åæ°ã®ã¢ãã¬ã¹ããèªããããšãæåŸ
ãããŠããããããã¢ã©ã€ã³ã¡ã³ãïŒæŽåïŒãšèšããx86 ç³» CPU ã¯ãºã¬ãã¢ãã¬ã¹ïŒunalignedïŒããã®èªã¿åºããé»ã£ãŠåŠçããŠãããããC èšèªã®ä»æ§äžã¯æªå®çŸ©åäœã§ãããWebAssembly + SAFE_HEAP ã§ã¯å®è¡æãšã©ãŒïŒalignment faultïŒã«ãªãã
ãªãã²ãŒã ã®ãã¡ã€ã«èªã¿èŸŒã¿ã§èµ·ããã: å€ãã²ãŒã ã®ãã€ããªãã¡ã€ã«åœ¢åŒã¯ 1 ãã€ãåäœã§è©°ãŸã£ãŠãããC åŽã§ã¯ #pragma pack(1) ã§ããã£ã³ã°ãé€å»ããæ§é äœïŒpacked structïŒããã¡ã€ã«ã®ãã€ãåã«ãã®ãŸãŸéããŠèªãã®ãåžžå¥ææ®µã ã£ããäŸãã° 6 ãã€ãã®ãããã®çŽåŸã« uint32_t ã®é
åãç¶ããã©ãŒãããã§ã¯:
ãã¡ã€ã«å
é ããã®ãªãã»ãã: 0 1 2 3 4 5 | 6 7 8 9 | ...
ããã(6byte) | uint32_t | uint32_t ...
â ãªãã»ãã6 = 4ã®åæ°ã§ãªãïŒ
ãã®ããªãã»ãã 6ãã®ã¢ãã¬ã¹ã uint32_t * ã«ãã£ã¹ãããŠèªããšãx86 ã§ã¯åãã wasm ã§ã¯ alignment fault ã§ abort ããã
å žåãã¿ãŒã³:
- file buffer äžã®
#pragma pack(1)struct ã typed pointer ãšããŠèªãã - 6 byte header ã®çŽåŸã
uint32_t *ã« cast ããŠèªãã - packed struct ã®
uint16_t/uint32_tfield ãçŽæ¥åç §ããã
察ç: unaligned ã®å¯èœæ§ãããã¢ãã¬ã¹ãçŽæ¥ dereferenceïŒãã€ã³ã¿çµç±ã§èªãïŒããªããå¿
ã memcpy ã§ã¢ã©ã€ã³ãããããŒã«ã«å€æ°ãžã³ããŒããŠãã䜿ããmemcpy ã¯ãã€ãåäœã§åãã®ã§ã¢ã©ã€ã³ã¡ã³ãã®å¶çŽããªãã
static uint32_t ReadU32(const void *p)
{
uint32_t v;
memcpy(&v, p, sizeof v); /* ãã€ãåäœã³ããŒãªã®ã§å®å
š */
return v;
}ä»åã®å€æŽç®æ:
-
src/cdogs/cwolfmap/cwolfmap.hCWLevelGetWidthCWLevelGetHeight
-
src/cdogs/cwolfmap/cwolfmap.cMapHeadMagicMapHeadPtrAtLevelLenAtLevelOffAt- packed level header ã® field access ã accessor åã
-
src/cdogs/cwolfmap/vswap.c- VSWAP chunk offset / length table ã aligned allocation ã« deep-copyã
-
src/cdogs/cwolfmap/audio.c- packed
uint16_tmusic value ãmemcpyhelper ã§èªãã
- packed
-
src/cdogs/map_wolf.clevel->header.width/heightçŽæ¥åç §ãCWLevelGetWidth/Heightã«çœ®æã
ãã㯠wasm å°çš hack ã§ã¯ãªããC ãšããŠæ£ããä¿®æ£ãstrict alignment ã®ä» platform ã«ãå¹ãã
src/cdogs/cwolfmap/wad/waderrno.hsrc/cdogs/cwolfmap/wad/waderrno.c
header ã« int waderrno; ããããè€æ°ã® .c ãã¡ã€ã«ã§åã倿°ãå®çŸ©ãããç¶æ
ã ã£ãããã€ãã£ãã® linker ã¯ããããcommon symbolããšããŠé»èªããããšãããããEmscripten ã® linker ã§ã¯ duplicate symbol ãšã©ãŒã«ãªãã
ä¿®æ£ã¯ C ã®äœæ³ã©ãã: header 㯠extern int waderrno;ïŒå®£èšã®ã¿ïŒã«ãã.c ãã¡ã€ã«1ã€ã«å®äœå®çŸ©ã眮ãã
ç§»æ€äžã¯ release build ã§ç²ããªãããŸã debug build ã䜿ãã
WASM_DEBUG=1 ./build_wasm.sh
python3 -m http.server 8081 --directory distãã©ãŠã¶ã§éã:
http://127.0.0.1:8081/
debug build ã§ã¯ source map ãçæããããããDevTools ã®ã¹ã¿ãã¯ãã¬ãŒã¹ã index.wasm:0x3258 ã®ãã㪠wasm ãªãã»ããã§ã¯ãªã vswap.c:53:24 ã®ãã㪠C ã®ãœãŒã¹è¡ã§è¡šç€ºãããããã®å·®ã¯æ±ºå®çã«å€§ããã
æ¯å hard reload ããããã©ãŠã¶ã¯ index.js, index.wasm, index.data ã匷ã cache ãããä¿®æ£ããã®ã« stack trace ãå€ããsource map ãå¹ããªããwasm offset ããåºãªãããšãããšã㯠cache ãåå ã®ããšãå€ãã
ããåºãåé¡ãšå¯ŸåŠ:
| çç¶ | åå ãšå¯ŸåŠ |
|---|---|
Aborted(stack overflow ...) |
Emscripten ã®ããã©ã«ãã¹ã¿ãã¯ïŒ64KBïŒãå°ããããã-s STACK_SIZE=2097152 ã«ãã |
Aborted(alignment fault) |
packed struct / raw file buffer / typed pointer cast ãæ¢ããmemcpy accessor ã«çœ®ãæããïŒåç« åç
§ïŒ |
Cannot set timing mode for main loop since a main loop does not exist |
SDL renderer åæåäžã® warningãä»åã®ç¯å²ã§ã¯éèŽåœ |
ScriptProcessorNode is deprecated |
SDL/Emscripten ã® Web Audio å®è£ å éšç±æ¥ãéèŽåœ |
N FS.syncfs operations in flight |
è€æ°ã®ä¿åçµè·¯ããåæã« IndexedDB åæããŠãããéèŽåœã ããä»äžãã§ debounce ãã |
| ãã«ã¹ã¯ãªãŒã³ã§é»ç»é¢ãè§£é€ããŠãæ»ããªã | shell ã® Fullscreen çµè·¯ã SDL2 port ãšéäºæïŒå
¥åç« ã®å®äŸåç
§ïŒãcanvas ã«çŽæ¥ requestFullscreen() ããCSS ã§æ¡å€§ãã |
| é³ãåºãªã | autoplay å¶éãååã®ãŠãŒã¶ãŒæäœåŸã« AudioContext ã resume ãããŠãããç¢ºèª |
| ä¿®æ£ãåæ ãããªã / stack trace ãå€ã | ãã©ãŠã¶ãã£ãã·ã¥ãhard reloadïŒCtrl+Shift+RïŒãã |
çŸåšã® build 㯠dist/ çŽäžã«åºãã
dist/index.html
dist/index.js
dist/index.wasm
dist/index.data
dist/index.wasm.map # debug build ã®ã¿
dist/favicon.ico
dist/og.jpg
ãã¹ãŠéçãã¡ã€ã«ãªã®ã§ãé
ä¿¡ã¯ã©ã®éçãã¹ãã£ã³ã°ã§ãããïŒä»å㯠GitHub Pages + gh-pages ããã±ãŒãžïŒã
ãµããã£ã¬ã¯ããªé
ä¿¡ïŒhttps://example.com/game/ ã®ãããªé
眮ïŒãæ³å®ããåç
§ã¯çžå¯Ÿãã¹ã«ãããEmscripten ãçæããããŒããŒã¯ããšããš index.wasm / index.data ãã¹ã¯ãªããäœçœ®åºæºã®çžå¯Ÿã§è§£æ±ºããã®ã§ã远å ã§æ³šæããã®ã¯æ¬¡ã®2ç¹:
- debug build ã® source map ã¯
--source-map-base ./ã«ããïŒ/ã ãšãã¡ã€ã³çŽäžãèŠã«è¡ãïŒã - favicon ã¯ãã©ãŠã¶ããã¡ã€ã³çŽäžã®
/favicon.icoãèŠã«è¡ããã¡ãªã®ã§ãçæ HTML ã«æç€ºçãªçžå¯Ÿ link ãæ³šå ¥ããã
sed -i 's|</title>|</title>\n <link rel="icon" href="favicon.ico">|' "$DIST_DIR/index.html"OGP ã® og:image ã¯éã«ãSNS ã®ã¯ããŒã©ãŒãçžå¯Ÿãã¹ã解決ããªããããå
¬é URL ã®çµ¶å¯Ÿ URL ã䜿ãã
- upstream ã®å¯Ÿè±¡ tag/commit ãåºå®ããã
rgã§æ¢åã® Emscripten 察å¿ãfilesystemãdelayãnetworkãSDL åæåã調æ»ããã- host toolchain ã®å€ãã確èªããã
- emsdkãPython 3.10+ãCMake ãããŒã«ã«ã«çšæããã
- æ¢åã® Emscripten build script ãããã°ãã®å€±æãã°ãåãã
- CMake/configure ãæ¬åœã«å¿ èŠãåè§£ããã
- wasm å°çšã®æå° build script ãäœãã
- asset ã¯ãŸãå šéš preload ããŠãmissing file åé¡ãæ¶ãã
WASM_DEBUG=1ã§ source mapãSAFE_HEAPãstack check ãæå¹ã«ããã- ã¡ã€ã³ã¡ãã¥ãŒå°éãŸã§çŽãã
- ã·ã³ã°ã«ãã¬ã€éå§ãŸã§çŽãã
- é³å£°åæåãš BGM/SE ã確èªããã
- config/save ã IDBFS ã«èŒããreload åŸã«æ®ãã確èªããã
- fullscreenãresizeãsubdirectory hosting ã確èªããã
- äžèŠãã°åé€ãsyncfs ã® debounceãasset åæžãåŸå·¥çšã§è¡ãã
æäœéã®å®äºæ¡ä»¶:
./build_wasm.shã§dist/ãçæãããã- HTTP server çµç±ã§
dist/index.htmlãèµ·åããã - canvas ã« SDL2 video ã衚瀺ãããã
- main menu ã«å°éããã
- multiplayer ã® join/host å°ç·ã Web çã«åºãªãã
- Quick Play ãŸãã¯æšæº campaign ãéå§ã§ããã
- SDL_mixer ãåæåããã广é³ãŸã㯠BGM ãåçã§ããã
- config ã player template ã reload åŸãæ®ãã
- debug build ã§ source map ãå¹ãã
- æ¢ç¥å¶éãš build æé ãææžåãããŠããã
- Emscripten å ¬åŒããã¥ã¡ã³ã
- File System APIïŒMEMFS / IDBFS / FS.syncfsïŒ
- Emscripten main loopïŒBrowser main loop ã®è§£èª¬ïŒ
- Asyncify ã®ä»çµã¿
- SDL2 ã䜿ã£ãç§»æ€ã¬ã€ã
ä»åã® root ãªããžããªã§ã¯ã詳ããçŸç¶ã¡ã¢ã¯ docs/wasm-port.mdïŒè±èªïŒãæ§æå³ã¯ docs/architecture.mdïŒè±èªïŒãå®éã® build æé 㯠build_wasm.sh ã«ããããã®ææžã¯ãããããããšã«ç§»æ€æ
åœè
ãæåããåãéã蟿ããããã«ãåæç¥èã»èª¿æ»é ã»å€æåºæºã»å
ã³ãŒããžã®ä»å
¥ç¯å²ãæ¥æ¬èªã§ãŸãšãããã®ã