┌─────────────────────────────────────────────────────────────────────┐
│ User Interface Layer │
│ Desktop (winit + wgpu) | WASM (WebGL) | Unity (C# + native code) │
└────────────────────┬────────────────────────────────────────────────┘
│
┌────────────────────▼────────────────────────────────────────────────┐
│ Game Bridge Layer (Rust) │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ GameBridge struct: │ │
│ │ - state: GameState (C FFI wrapper) │ │
│ │ - world: World3D (camera, entities) │ │
│ │ - renderer: WgpuRenderer │ │
│ └──────────────────────────────────────────────────────────────┘ │
└────────────────────┬────────────────────────────────────────────────┘
│
┌────────────────────▼────────────────────────────────────────────────┐
│ FFI Layer (Rust) │
│ nethack-sys (bindgen-generated ffi.rs + safe wrappers) │
│ │
│ Public API: │
│ - init_stage_1_early() ... init_stage_7_newgame() │
│ - full_init() (convenience) │
│ - get_dungeon_level(), player_pos() [planned] │
└────────────────────┬────────────────────────────────────────────────┘
│
┌────────────────────▼────────────────────────────────────────────────┐
│ C Library (NetHack 5.0 Core) │
│ 139 object files: │
│ - Game logic (allmain.c, cmd.c, do.c, etc.) │
│ - AI & dungeon generation (monmove.c, mklev.c, etc.) │
│ - Item & monster management (objects.c, mkobj.c, etc.) │
│ - Save/restore (save.c, restore.c) │
│ │
│ Global state: │
│ - struct you u (player) │
│ - int dlevel (dungeon level) │
│ - struct monst *fmon (monster list) │
│ - struct obj *fobj (object list) │
└─────────────────────────────────────────────────────────────────────┘
責務:
主要モジュール:
pub enum InitStage {
Uninitialized,
EarlyInit, // early_init() 実行
WindowsChosen, // choose_windows() 実行
OptionsInitialized, // initoptions() 実行
WindowsInitialized, // init_nhwindows() 実行
DlbInitialized, // dlb_init() 実行
VisionInitialized, // vision_init() 実行
GameReady, // newgame() 実行
}
pub struct GameState {
stage: InitStage,
}
依存関係:
lazy_static — グローバル GameState 管理bindgen — C FFI 自動生成テスト:
責務:
主要構造体:
pub struct Camera3D {
position: Vec3,
target: Vec3,
up: Vec3,
fov: f32,
}
pub enum ViewMode {
TopDown, // y=50, 俯瞰
Isometric, // 45° 角度
FirstPerson, // 目線高さ
ThirdPerson, // プレイヤーの後ろ
Cinematic, // ドラマティック
}
pub struct World3D {
entities: Vec<Entity>,
camera: Camera3D,
}
テスト:
責務(実装予定):
計画:
pub struct WgpuRenderer {
device: wgpu::Device,
queue: wgpu::Queue,
render_pipeline: wgpu::RenderPipeline,
texture: wgpu::Texture,
}
impl WgpuRenderer {
pub fn render(&self, world: &World3D, camera: &Camera3D) { }
}
責務(実装予定):
アーキテクチャ:
pub struct Application {
event_loop: EventLoop<()>,
window: Window,
renderer: WgpuRenderer,
game_bridge: GameBridge,
}
impl Application {
pub async fn run(self) { }
}
ターゲット:
責務(実装予定):
アーキテクチャ:
#[wasm_bindgen]
pub struct WasmGame {
game_bridge: GameBridge,
}
#[wasm_bindgen]
impl WasmGame {
#[wasm_bindgen(constructor)]
pub fn new() -> WasmGame { }
pub fn init(&mut self) -> Result<(), String> { }
pub fn send_command(&mut self, cmd: u8) { }
pub fn render(&self, canvas_id: &str) { }
}
JS 側インターフェース:
const game = new WasmGame();
await game.init();
// キー入力
document.addEventListener('keydown', (e) => {
game.send_command(e.keyCode);
});
// レンダリング
const canvas = document.getElementById('game-canvas');
game.render(canvas);
責務(実装予定):
C# インターフェース:
[DllImport("nethack_unity")]
static extern int nethack_init();
[DllImport("nethack_unity")]
static extern void nethack_send_command(byte cmd);
[DllImport("nethack_unity")]
static extern int nethack_get_dungeon_level();
public class NetHackGame : MonoBehaviour {
void Start() {
nethack_init();
}
void Update() {
if (Input.GetKeyDown(KeyCode.UpArrow)) {
nethack_send_command((byte)'k');
}
}
}
main()
↓
nethack_sys::full_init()
├─ Stage 1: early_init() [C] プログラム状態初期化
├─ Stage 2: choose_windows(1) [C] UI システム選択
├─ Stage 3: initoptions() [C] オプション読み込み
├─ Stage 4: init_nhwindows() [C] ウィンドウシステム初期化
├─ Stage 5: dlb_init() [C] ダンジョンDB初期化
├─ Stage 6: vision_init() [C] 視野システム初期化
└─ Stage 7: newgame() [C] ゲーム開始
├─ player_selection()
├─ u.ux = 0; u.uy = 0 (player位置)
├─ dlevel = 1 (ダンジョンレベル)
└─ moveloop(FALSE) [無限ループ → 要ゲームループ実装]
Loop:
┌─ Read player input (winit / browser / Unity)
│
├─ Call docommand() [C]
│ └─ Updates: u.ux, u.uy, dlevel, fmon, fobj, etc.
│
├─ Read game state [Rust]
│ ├─ world.update_player(u.ux, u.uy)
│ ├─ world.update_entities(fmon, fobj)
│ └─ camera.follow(player_pos)
│
├─ Render [wgpu]
│ ├─ camera.view_projection()
│ ├─ render_tiles(world.entities)
│ └─ render_ui()
│
└─ Present to screen (vulkan/dx12/gl)
// From decl.h / allmain.c
struct you u; // 1-2KB
int dlevel; // 4 bytes
int dunlevs; // 4 bytes
struct dungeon_topology dungeon; // ~500 bytes
struct monst *fmon; // Pointer (8 bytes)
struct obj *fobj; // Pointer (8 bytes)
lazy_static! {
static ref GAME_STATE: Mutex<GameState> = ...;
static ref WORLD_STATE: Mutex<World3D> = ...;
}
early_init() など初期化関数: 呼び出しオーバーヘッド無視できるdocommand() 毎フレーム呼び出し: unsafe ブロック最小化Mutex<GameState> でグローバル状態を保護// 新しい ViewMode を追加
enum ViewMode {
TopDown,
Isometric,
// ... add custom modes
Custom(CustomCamera),
}
// シェーダー追加
impl WgpuRenderer {
fn add_shader(&mut self, name: &str, wgsl: &str) { }
}
#[cfg(target_arch = "wasm32")]
fn save_game() { /* IndexedDB */ }
#[cfg(not(target_arch = "wasm32"))]
fn save_game() { /* File IO */ }
詳細は docs/LICENSE.md を参照
最終更新: 2026-05-06