---
doc_id: "DOC-CLI-002"
title: "Hệ Thống Tọa Độ Isometric, Lưới Va Chạm & Chân Đế Đạo Cụ 2.5D"
category: "client"
diataxis_type: "reference"
status: "canonical"
version: "2026.1"
owner_role: "ios_client_engineer"
last_updated: "2026-09-29"
tags: ["isometric-2.5d", "collision-grid", "prop-footprint", "depth-sorting"]
related_code:
  - "client/webapp/js/engine/iso_math.js"
  - "server/world/spatial_grid.py"
related_docs:
  - "docs/client/IOS_METAL_AND_CLIENT_ENGINE.md"
summary: "Đặc tả toán học chuyển đổi hệ tọa độ Descartes sang Isometric 2:1, lưới va chạm tĩnh/động và thuật toán sắp xếp chiều sâu Y-sorting."
---

# TÀI LIỆU THIẾT KẾ KIẾN TRÚC: HỆ THỐNG BIÊN BẢN ĐỒ, VA CHẠM ĐỊA HÌNH & CHÂN ĐẾ NỘI THẤT 2.5D
# ISOMETRIC WORLD BOUNDARIES, COLLISION GRID & PROP FOOTPRINT ARCHITECTURE 2026

> **Trạng thái**: SPECIFICATION & IMPLEMENTATION BLUEPRINT (REFACTOR PHASE)  
> **Phiên bản**: 2.5D Grimdark Collision Kernel v1.0  
> **Chủ quản**: [Trưởng Ban Kiến Trúc Client](file:///c:/Projects/FreeExile/AGENTS.md) (`client_architect`), [Kỹ Sư Hệ Thống Server](file:///c:/Projects/FreeExile/AGENTS.md) (`server_systems_architect`), [Trưởng Nhóm Thiết Kế Game](file:///c:/Projects/FreeExile/AGENTS.md) (`lead_systems_designer`).  
> **Tham chiếu liên quan**: [docs/architecture/MAP_DUNGEON_AND_SECRET_CHAMBER_ARCHITECTURE.md](file:///c:/Projects/FreeExile/docs/architecture/MAP_DUNGEON_AND_SECRET_CHAMBER_ARCHITECTURE.md), [server/world/spatial_grid.py](file:///c:/Projects/FreeExile/server/world/spatial_grid.py), [server/world/movement_authority.py](file:///c:/Projects/FreeExile/server/world/movement_authority.py).

---

## 1. BỐI CẢNH & PHÂN TÍCH NGUYÊN NHÂN GỐC RỄ (ROOT CAUSE ANALYSIS)

Trong quá trình vận hành mô phỏng 2.5D Isometric, ba khiếm khuyết vật lý cơ bản đang làm suy giảm chất lượng trải nghiệm:

```mermaid
flowchart LR
    subgraph Current_Defects["HIỆN TRẠNG LỖI CƠ HỌC"]
        D1["1. Đi Xuyên Biên Cõi<br/>(Vượt Ra Khỏi Viewport)"]
        D2["2. Vô Giới Hạn Địa Hình<br/>(Đi Xuyên Sông, Núi, Vách)"]
        D3["3. Đè Lấp Nội Thất<br/>(Z-Fighting, Xuyên Cột/Bàn Thờ)"]
    end

    subgraph Root_Causes["NGUYÊN NHÂN GỐC RỄ"]
        R1["Camera Cố Định Tại (0,0)<br/>Không Có Viewport Clamping"]
        R2["Thiếu Ma Trận Tile Passability<br/>Di Chuyển Thuần Vector Tự Do"]
        R3["Lẫn Lộn Bounding Box Sprite<br/>Vs. Chân Đế Tiếp Đất (Footprint)"]
    end

    subgraph Solutions["HỆ THỐNG GIẢI PHÁP 2026"]
        S1["Tracking Camera + World Bounds Clamping"]
        S2["Sub-Grid Collision Bitmask + Smooth Wall-Slide"]
        S3["Ground Footprint Matrix + Dynamic Y-Depth Sorting"]
    end

    D1 --> R1 --> S1
    D2 --> R2 --> S2
    D3 --> R3 --> S3
```

### 1.1. Hiện tượng nhân vật đi vọt ra ngoài Viewport (Camera-Desync Out-of-Bounds)
- **Cơ chế lỗi**: Hệ thống chiếu `worldToIso(wx, wy)` neo cứng tâm màn hình tại `cx = clientWidth / 2`, `cy = clientHeight / 2` tương ứng với tọa độ $(0, 0)$ của bản đồ.
- **Hệ quả**: Khi người chơi di chuyển liên tục, tọa độ nhân vật $(wx, wy)$ tăng tịnh tiến vô hạn mà không có Camera Tracking kéo màn hình theo, cũng như không có hàm `clamp(wx, minWx, maxWx)` khóa biên thế giới. Nhân vật biến mất hoàn toàn khỏi màn hình và trôi vào hư vô.

### 1.2. Hiện tượng không bị giới hạn địa hình (Unconstrained Movement)
- **Cơ chế lỗi**: Vector di chuyển `player.wx += dirX * speed * dt` được cộng trực tiếp vào tọa độ thế giới mà không qua bước kiểm tra giao cắt (Collision Sweep).
- **Hệ quả**: Nhân vật đi xuyên qua vách đá, đi bộ trên mặt hồ nước sâu và xuyên qua bờ rào man tộc.

### 1.3. Hiện tượng các vật thể trong nhà đè lên nhau (Interior Prop Overlap & Z-Fighting)
- **Cơ chế lỗi**: Sử dụng toàn bộ kích thước ảnh vẽ tĩnh $(width, height)$ để định vị. Bàn thờ, ghế dài, cột đình và vách ngăn xếp gần nhau có phần mái hoặc bóng đổ chồng lấn.
- **Hệ quả**: Thiếu chân đế tiếp đất (Ground Footprint) dẫn đến lỗi Z-sorting: nhân vật đứng trước cây cột nhưng chân cây cột lại vẽ đè lên đầu nhân vật, hoặc hai vật thể nội thất cắt xẻ hình học vào nhau (Z-fighting).

---

## 2. KIẾN TRÚC HỆ THỐNG GIẢI PHÁP (SYSTEM ARCHITECTURE)

```mermaid
flowchart TD
    subgraph Input_Pipeline["1. INPUT & VELOCITY INTENT"]
        JOY["Joystick Touch / WASD Keyboard"] --> VEC["Raw Velocity Vector (vx, vy)"]
    end

    subgraph Movement_Pipeline["2. MOVEMENT & COLLISION RESOLVER"]
        VEC --> SWEEP["Swept Circle vs Tile Collision"]
        SWEEP --> SLIDE["Wall-Slide Decomposition (X-then-Y)"]
        SLIDE --> BOUND_CLAMP["World Boundary Hard Clamping"]
        BOUND_CLAMP --> NEW_POS["Authoritative Position (wx, wy)"]
    end

    subgraph Camera_Pipeline["3. TRACKING CAMERA PIPELINE"]
        NEW_POS --> LERP_CAM["Camera Deadzone & Exponential Lerp"]
        LERP_CAM --> CAM_CLAMP["Camera Viewport Bounds Clamping"]
        CAM_CLAMP --> REL_ISO["worldToIso(wx - camWx, wy - camWy)"]
    end

    subgraph Render_Pipeline["4. PROPS & OCCLUSION RENDERING"]
        REL_ISO --> SORT["Y-Depth Sort (Ground Footprint Pivot)"]
        SORT --> OCCLUSION["Smart Roof / Wall Alpha Fade (0.35)"]
        OCCLUSION --> CANVAS["Final 120Hz Screen Frame"]
    end
```

---

## 3. THIẾT KẾ KỸ THUẬT CHI TIẾT (TECHNICAL SPECIFICATIONS)

### 3.1. Phân Tầng Biên Bản Đồ & Camera Theo Dấu (World Bounds & Dynamic Camera)

Mỗi phân khu (Zone) định nghĩa ranh giới hình học thế giới:

$$\text{Bounds} = \{ \text{minWx}, \text{maxWx}, \text{minWy}, \text{maxWy} \}$$

| Phân Vùng (Zone) | Tọa Độ Thế Giới $(\text{minWx}, \text{maxWx})$ | Tọa Độ Thế Giới $(\text{minWy}, \text{maxWy})$ | Bán Kính Chơi Thực Tế | Quy Chuẩn Camera |
| :--- | :--- | :--- | :--- | :--- |
| **Doanh Trại Bến Lưu Đày (Sanctuary)** | $[-8.0, +8.0]$ | $[-6.0, +6.0]$ | $\approx 24\text{m} \times 18\text{m}$ | Smooth Follow (Deadzone $1.2\text{m}$) |
| **Sào Huyệt Động Thiên (Hideout)** | $[-6.0, +6.0]$ | $[-5.0, +5.0]$ | $\approx 18\text{m} \times 15\text{m}$ | Clamped to Interior Walls |
| **Bờ Đá Tàn Xương (Wilderness)** | $[-16.0, +16.0]$ | $[-12.0, +12.0]$ | $\approx 48\text{m} \times 36\text{m}$ | Full Unbounded Following |
| **Phó Bản / Mật Thất (Crypt)** | $[-5.0, +5.0]$ | $[-4.0, +4.0]$ | $\approx 15\text{m} \times 12\text{m}$ | Static Room / Soft Deadzone |

#### Thuật Toán Camera Theo Dấu Mượt Mà (Exponential Smoothing):
```typescript
// Camera State
interface Camera2D {
  wx: number;      // Tọa độ tâm camera trong thế giới
  wy: number;
  targetWx: number;
  targetWy: number;
  followSpeed: number; // Mặc định 8.5 (phản hồi trong ~120ms)
}

function updateCamera(cam: Camera2D, targetWx: number, targetWy: number, dt: number, bounds: ZoneBounds): void {
  // 1. Tính toán Deadzone (Không rung giật khi nhân vật xê dịch nhẹ)
  const dx = targetWx - cam.wx;
  const dy = targetWy - cam.wy;
  const dist = Math.hypot(dx, dy);
  const deadzone = 0.45; // mét

  if (dist > deadzone) {
    const factor = 1.0 - Math.exp(-cam.followSpeed * dt);
    cam.wx += (targetWx - cam.wx) * factor;
    cam.wy += (targetWy - cam.wy) * factor;
  }

  // 2. Camera Clamping (Không bao giờ chiếu ra ngoài mép bản đồ vẽ)
  cam.wx = Math.max(bounds.minCamWx, Math.min(bounds.maxCamWx, cam.wx));
  cam.wy = Math.max(bounds.minCamWy, Math.min(bounds.maxCamWy, cam.wy));
}
```

---

### 3.2. Ma Trận Va Chạm Địa Hình & Thuật Toán Trượt Tường (Collision Grid & Wall-Slide)

Mỗi vùng đất được chia thành lưới ma trận ô con (Sub-Tile Grid) với độ phân giải $0.5\text{m} \times 0.5\text{m}$.

```mermaid
classDiagram
    class TileCollisionFlag {
        <<enumeration>>
        WALKABLE = 0x00
        SOLID_WALL = 0x01
        DEEP_WATER = 0x02
        CHASM_VOID = 0x04
        LOW_OBSTACLE = 0x08
        INDOOR_FLOOR = 0x10
        PORTAL_TRIGGER = 0x20
    }

    class CollisionMap {
        +int widthTiles
        +int heightTiles
        +float tileSizeMeters
        +Uint8Array bitmask
        +isWalkable(float wx, float wy, float radius) bool
        +resolveSlide(float curX, float curY, float dx, float dy, float radius) Vector2
    }

    class PropFootprint {
        +string propId
        +float centerWx
        +float centerWy
        +float radiusX
        +float radiusY
        +bool isSolid
        +checkOverlap(PropFootprint other) bool
    }

    CollisionMap --> TileCollisionFlag
    CollisionMap --> PropFootprint
```

#### Bảng Phân Cấp Cản Trở Vật Lý (Collision Layer Bitmask):

| Giá Trị Bit | Ý Nghĩa Địa Hình | Tác Động Lên Nhân Vật | Tác Động Chiêu Thức / Projectile |
| :--- | :--- | :--- | :--- |
| `0x00` | **Bình Địa (Walkable Ground)** | Di chuyển bình thường 100% tốc độ | Bay qua bình thường |
| `0x01` | **Tường Cứng (Solid Wall / Pillar)** | Cản tuyệt đối (Vận tốc pháp tuyến = 0) | Chạm nổ / Tiêu hủy đòn đánh |
| `0x02` | **Vực Sâu / Nước Sâu (Chasm / Water)** | Cản bước chân (Không thể đi bộ) | **Bay xuyên qua** (Cung/Kiếm khí bay qua được) |
| `0x08` | **Vật Cản Thấp (Low Barricade)** | Cản bước chạy thường; **Né đòn (i-frame) vượt qua** | Bay xuyên qua |
| `0x10` | **Sàn Nhà Nội Thất (Indoor Floor)** | Kích hoạt hiệu ứng âm thanh bước chân gỗ/đá lát | Bay qua bình thường |

#### Thuật Toán Trượt Tường (Wall Sliding Algorithm - Trục Kép Độc Lập):
Khi nhân vật va vào bờ tường theo góc chéo, thay vì khựng lại đột ngột:
```typescript
function resolveMovementWithSliding(
  curWx: number,
  curWy: number,
  moveDistX: number,
  moveDistY: number,
  radius: number,
  collisionMap: CollisionMap
): { wx: number; wy: number } {
  let targetX = curWx + moveDistX;
  let targetY = curWy + moveDistY;

  // Thử nghiệm bước di chuyển hoàn chỉnh
  if (collisionMap.isAreaClear(targetX, targetY, radius)) {
    return { wx: targetX, wy: targetY };
  }

  // Phân rã trượt trục X (Nếu trục Y bị tường cản, cho phép tiếp tục trượt ngang theo X)
  let resolvedX = curWx;
  if (collisionMap.isAreaClear(curWx + moveDistX, curWy, radius)) {
    resolvedX = curWx + moveDistX;
  }

  // Phân rã trượt trục Y (Nếu trục X bị tường cản, cho phép tiếp tục trượt dọc theo Y)
  let resolvedY = curWy;
  if (collisionMap.isAreaClear(curWx, curWy + moveDistY, radius)) {
    resolvedY = curWy + moveDistY;
  }

  return { wx: resolvedX, wy: resolvedY };
}
```

---

### 3.3. Hệ Thống Chân Đế Vật Thể Tiếp Đất (Prop Footprint & Anti-Overlap)

Khắc phục triệt để lỗi các vật thể trong nhà (bàn thờ, vách bình phong, cột đình, lò rèn) đè lên nhau hoặc che lấp nhân vật sai logic:

```
                  ┌──────────────────────────────┐
                  │      PHẦN MÁI & HOA VĂN      │  <-- Sprite Visual Elevation
                  │   (Chỉ dùng hiển thị đồ họa) │      (Làm mờ alpha 0.35 khi nhân vật
                  │      Bán trong suốt khi che  │       đứng phía sau phần này)
                  ├──────────────────────────────┤
                  │     THÂN CỘT / MẶT BÀN       │
                  ├──────────────────────────────┤
                  │ ╔══════════════════════════╗ │
  Mặt Đất (Z=0) ──┼─╢    CHÂN ĐẾ TIẾP ĐẤT      ╟─┼──  <-- GROUND FOOTPRINT (Solid Bounding)
                  │ ╚══════════════════════════╝ │      (Dùng để Z-sorting & chặn va chạm)
                  └──────────────────────────────┘
```

#### Phân Tách Thuộc Tính Vật Thể Nội Thất (Prop Data Contract):
```typescript
interface IsometricProp {
  id: string;
  type: string;             // 'pavilion_main', 'altar_shrine', 'weapon_rack', 'stone_pillar'
  originWx: number;         // Tọa độ gốc trên bản đồ thế giới
  originWy: number;
  
  // 1. Chân đế vật lý tiếp đất (Footprint) - Nằm trên mặt phẳng Z=0
  footprint: {
    shape: 'ellipse' | 'rectangle';
    radiusWx: number;       // Bán kính va chạm vật lý theo chiều X (VD: 0.6m)
    radiusWy: number;       // Bán kính va chạm vật lý theo chiều Y (VD: 0.35m)
    offsetWx: number;       // Độ lệch tâm tiếp đất
    offsetWy: number;
    isPassable: boolean;    // false = cản đường hoàn toàn; true = thảm lót/hoa văn
  };

  // 2. Kích thước hình vẽ hiển thị (Sprite Visuals)
  visual: {
    spriteKey: string;
    widthPx: number;
    heightPx: number;
    anchorX: number;        // Điểm neo gốc chân đế trên ảnh (thường 0.5)
    anchorY: number;        // Điểm neo đáy ảnh (thường 0.85 - 0.95)
    occlusionThresholdY: number; // Ngưỡng kích hoạt làm mờ mái khi nhân vật đứng sau
  };
}
```

#### Quy Tắc Chống Đè Lẫn Nhau Khi Bày Biện (Anti-Overlap Placement Rule):
Khi khởi tạo hoặc bố trí kiến trúc nội thất trong Sào Huyệt/Tiên Phủ:
1. **Kiểm tra va chạm chân đế**: Hai vật thể kiên cố $A$ và $B$ chỉ được phép đặt cạnh nhau nếu khoảng cách giữa hai tâm chân đế thỏa mãn:
   $$\Delta_{\text{dist}} \ge \sqrt{(\text{radiusWx}_A + \text{radiusWx}_B)^2 + (\text{radiusWy}_A + \text{radiusWy}_B)^2} + \epsilon_{\text{gap}}$$
   với $\epsilon_{\text{gap}} \ge 0.15\text{m}$ (khoảng đệm thẩm mỹ).
2. **Triệt tiêu Z-Fighting bằng Dynamic Y-Sorting**:
   - Trục sắp xếp thứ tự vẽ `depth` trên Canvas **bắt buộc** tính theo tọa độ tiếp đất thực tế của chân đế:
     $$\text{Depth} = (\text{originWx} + \text{offsetWx}) + (\text{originWy} + \text{offsetWy})$$
   - Tuyệt đối **không** dùng đỉnh của sprite hay tâm hình chữ nhật để làm trục depth.

---

## 4. MA TRẬN DỮ LIỆU ĐỊA HÌNH VÀ VẬT THỂ MẪU (CANONICAL PROPS & FOOTPRINTS)

| Mã Vật Thể (Prop ID) | Tên Vật Thể Nội Thất | Kích Thước Sprite (px) | Chân Đế Tiếp Đất (Footprint) | Loại Va Chạm | Tác Động Che Khuất (Occlusion) |
| :--- | :--- | :--- | :--- | :--- | :--- |
| `prop_pavilion_main` | **Chính Điện Sào Huyệt (VLTK1)** | $270 \times 216$ | Bầu dục $1.8\text{m} \times 0.9\text{m}$ | Cản toàn bộ phần bệ đá | Khi nhân vật sau mái: $\alpha = 0.35$ |
| `prop_map_device_altar` | **Đài Tế Tinh Đồ Nghi (PoE2)** | $120 \times 90$ | Bầu dục $1.2\text{m} \times 0.6\text{m}$ | Cản trung tâm; cho phép đi quanh | Không che khuất tầm nhìn |
| `prop_bone_shrine` | **Bàn Thờ Huyết Cốt** | $80 \times 110$ | Chữ nhật $0.8\text{m} \times 0.4\text{m}$ | Chướng ngại vật cố định | Làm mờ khi đứng sát sau bàn thờ |
| `prop_bamboo_cluster` | **Bụi Trúc Hoang Dã (VLTK1)** | $140 \times 180$ | Bầu dục $0.5\text{m} \times 0.3\text{m}$ | Chân gốc cản bước | Tán trúc đung đưa bán trong suốt |
| `prop_weapon_rack` | **Giá Binh Khí Cổ Võ** | $64 \times 75$ | Chữ nhật $0.6\text{m} \times 0.2\text{m}$ | Cản vật lý | Không che khuất |
| `prop_secret_crypt_gate`| **Bia Đá Cổ Quan Mật Thất** | $96 \times 130$ | Chữ nhật $1.0\text{m} \times 0.3\text{m}$ | Cản vách tường mật đạo | Hiệu ứng rung chấn khi mở |

---

## 5. KẾ HOẠCH TRIỂN KHAI THEO GIAI ĐOẠN (IMPLEMENTATION ROADMAP)

> [!IMPORTANT]
> **Nguyên tắc an toàn**: Không thực hiện sửa code trong phiên này. Mọi module sẽ được hiện thực hóa theo thứ tự ưu tiên sau khi quá trình refactor kiến trúc module webapp hoàn tất.

```mermaid
flowchart TD
    M1["GIAI ĐOẠN 1: BẢO VỆ BIÊN & CAMERA THEO DẤU (P0)"]
    M2["GIAI ĐOẠN 2: LƯỚI VA CHẠM SUB-GRID & TRƯỢT TƯỜNG (P0)"]
    M3["GIAI ĐOẠN 3: ĐỘNG THIÊN NỘI THẤT & PHÂN TÁCH FOOTPRINT (P1)"]
    M4["GIAI ĐOẠN 4: ĐỒNG BỘ THẨM QUYỀN MÁY CHỦ ZERO-TRUST (P1)"]

    M1 --> M2 --> M3 --> M4
```

### Kế hoạch hành động cụ thể:

1. **Giai Đoạn 1: Giới Hạn Biên Cõi & Camera Bám Theo (Priority P0)**
   - Triển khai `ZoneBounds` trong `client/webapp/js/data/zones_catalog.js`.
   - Bổ sung camera tracking `camWx, camWy` trong `client/webapp/js/engine/canvas_renderer.js` và `iso_math.js`.
   - Khóa chặt nhân vật không cho chạy vượt quá tọa độ ranh giới thế giới (`Math.max(minWx, Math.min(maxWx, player.wx))`).

2. **Giai Đoạn 2: Ma Trận Ô Cản & Trượt Tường Mượt Mà (Priority P0)**
   - Xây dựng module `client/webapp/js/engine/collision_engine.js`.
   - Hiện thực hóa hàm `resolveMovementWithSliding()`: Tách 2 trục X và Y để nhân vật trượt êm dọc theo gờ tường đá thay vì bị kẹt cứng.
   - Thêm lớp cản nước sâu / vực thẳm (`DEEP_WATER`, `CHASM_VOID`) để projectile bay qua nhưng chân người bị cản.

3. **Giai Đoạn 3: Chân Đế Tiếp Đất & Chống Đè Vật Thể Nội Thất (Priority P1)**
   - Chuẩn hóa cấu trúc danh mục `mapProps` với trường `footprint` độc lập với `visual`.
   - Cập nhật thuật toán Z-sorting trong `entity_renderer.js` theo tọa độ đáy tiếp đất.
   - Cải tiến bộ lọc làm mờ động (`alpha = 0.35`) khi nhân vật lọt vào sau mái nhà/vách đá cao.

4. **Giai Đoạn 4: Đồng Bộ Thẩm Quyền Máy Chủ (Server-Authoritative Sync) (Priority P1)**
   - Cập nhật [server/world/movement_authority.py](file:///c:/Projects/FreeExile/server/world/movement_authority.py) đồng bộ bảng collision grid để phát hiện và giật lùi (rubberband) các hành vi hack noclip/speedhack.
   - Bổ sung kiểm thử tự hành TDD trong `tests/unit/test_movement_authority.py` xác nhận 100% không thể đi xuyên tường hay trôi ra ngoài biên.
