Giáo trình: Làm game xây dựng nhiều người chơi trên web

Giới thiệu

Sau khoảng 20 tuần (8–10 giờ mỗi tuần), học viên tự làm được một game xây dựng thuộc địa 3D chạy trên trình duyệt, 2–4 người chơi cùng lúc qua mạng, có lưu game và có bản chạy thật cho bạn bè chơi.

Giáo trình đi theo đúng cách Thương Cảng 1800 (bản làm lại Anno 1800 nhiều người chơi) được làm: một mô phỏng tất định chạy y hệt trên server và mọi máy, Three.js để vẽ, React cho giao diện, kiểm thử tự động ở mọi tầng.

Dành cho ai: người đã biết lập trình cơ bản (biến, hàm, vòng lặp, mảng, đối tượng) bằng một ngôn ngữ bất kỳ. Không cần biết trước đồ hoạ 3D, lập trình mạng hay làm game.

Học xong làm được:

  • Viết mô phỏng tất định: cùng chuỗi lệnh vào thì mọi máy ra cùng trạng thái, tới từng bit.
  • Thiết kế luật kinh tế xây dựng (chuỗi sản xuất, dân, nhu cầu, thuế) và cân bằng nó bằng người chơi giả lập.
  • Vẽ thế giới 3D hàng trăm công trình vẫn mượt, kể cả trên điện thoại.
  • Dựng server nhiều người chơi kiểu lockstep, phát hiện và tự sửa khi các máy lệch nhau.
  • Nâng cấp định dạng save mà không làm mất thế giới của người chơi.
  • Kiểm thử, phát hành, sao lưu và quay lại bản cũ khi có sự cố.

Cách học: cả khoá xây một game nhỏ xuyên suốt tên Bến Nhỏ: một đảo, một tàu, 6–8 loại công trình, chuỗi gỗ → ván → nhà. Mỗi phần kết thúc bằng một mốc chạy được của Bến Nhỏ. Mục "Trong Thương Cảng 1800" ở mỗi phần cho thấy bản đầy đủ làm cùng việc đó ra sao; nên chơi thử game này vài giờ trước khi bắt đầu.

Mỗi phần gồm: mục tiêu, kiến thức cần học, bài thực hành (mốc của Bến Nhỏ), bẫy thường gặp.

Lộ trình tổng quan

Khoá học kéo dài 20 tuần; mỗi phần kết thúc bằng một mốc chạy được của Bến Nhỏ, còn kiểm thử và làm việc cùng AI đi xuyên suốt từ tuần 3.

Vuốt ngang để xem hết sơ đồ →

Bến Nhỏ lớn dần qua 10 mốc trong 20 tuần Thanh đặc: các tuần của mỗi phần · nét đứt: làm xuyên suốt · tô màu: nền của mọi phần sau 12345 678910 1112131415 1617181920 Tuần Phần 0 · Nền tảng Phần 1 · Mô phỏng tất định Phần 2 · Luật chơi và cân bằng Phần 3 · Đồ hoạ 3D Phần 4 · Giao diện, nhập liệu Phần 5 · Nhiều người chơi Phần 6 · Lưu game, nâng save Phần 7 · Hướng dẫn chơi Phần 8 · Kiểm thử Phần 9 · Vận hành Phần 10 · Làm việc cùng AI Mốc 0: repo 4 gói, test đầu tiên Mốc 1: hai bản chạy song song cùng hash Mốc 2: bot đạt 50 dân, không âm tiền Mốc 3: đặt nhà bằng chuột, 500 công trình Mốc 4: chơi bằng chuột và điện thoại Mốc 5: 2–4 người chung một thế giới Mốc 6: save v1 nâng lên v2 có test Mốc 7: bot người học qua 5 bản đồ Mốc 8: cổng typecheck + test + E2E Mốc 9: tên miền thật, có sao lưu Xuyên suốt: CLAUDE.md, gói việc, cổng
Phần 1 là nền của mọi phần sau: sim chưa tất định thì Phần 5 không làm được. Học dưới 6 giờ mỗi tuần thì nên giãn lịch ra khoảng 30 tuần, giữ nguyên thứ tự.

Phần 0 — Nền tảng tuần 1–2

Mục tiêu: dựng một repo gồm 4 gói, chạy một lệnh là thấy trang web hiện số tick và server trả lời qua WebSocket. Kiến trúc đặt đúng từ tuần 1 rẻ hơn nhiều so với tách lại sau.

Kiến thức:

  • TypeScript ở chế độ strict: kiểu, union, readonly, generic cơ bản. Game lớn sống nhờ trình biên dịch bắt lỗi trước người chơi.
  • Node.js, ES module, pnpm workspace (nhiều gói trong một repo, dùng chung mã).
  • Vite: máy chủ dev, build, proxy WebSocket sang server.
  • Git: nhánh, commit nhỏ, tag cho mỗi bản phát hành.

Cấu trúc dựng ngay từ đầu:

GóiVai tròChạy ở đâu
simLuật chơi; không biết gì về màn hình hay mạngServer và mọi client
protocolKiểu tin nhắn, kiểm tra dữ liệu từ clientServer và client
serverPhòng chơi, xếp thứ tự lệnh, lưu save, phục vụ fileNode.js
clientVẽ 3D, giao diện, nhập liệuTrình duyệt

Quy tắc vàng: sim không import từ client hay server, không đụng DOM, Date hay Math.random.

Thực hành — mốc 0:

  1. Tạo monorepo pnpm với 4 gói trên.
  2. sim có hàm step(state) tăng tick, kèm một test vitest.
  3. client (Vite + React) hiện số tick; server (thư viện ws) trả "pong".
  4. pnpm typecheck && pnpm test chạy xanh; từ nay mọi commit đều qua hai lệnh này.

Phần 1 — Mô phỏng tất định tuần 3–5

Cùng trạng thái đầu và cùng chuỗi lệnh phải luôn cho ra cùng trạng thái sau, trên mọi máy. Khi đạt được điều này, mạng chỉ cần gửi lệnh chứ không gửi cả thế giới. Đây là phần quan trọng nhất; mọi phần sau dựa vào nó.

Mục tiêu: sim của Bến Nhỏ chạy không cần màn hình, nhận lệnh, tiến theo tick 100 ms, có snapshot(), load() và hash(). Hai bản chạy song song luôn cho cùng hash.

Kiến thức:

  • Vòng lặp theo tick cố định: luật chạy 10 tick/giây, hình vẽ 60 khung/giây, nội suy giữa hai tick.
  • Lệnh là cách duy nhất đổi trạng thái: step(cmds). Lệnh là dữ liệu JSON thuần, không phải hàm.
  • ECS: entity là một id chuỗi (b12), component là bản ghi dữ liệu, system là hàm chạy mỗi tick. Dùng thư viện kho (atom, component, system, scope) hoặc tự viết bản tối giản trước để hiểu.
  • Tất định: chỉ số nguyên (tỉ lệ tính bằng phần nghìn ‰); không Math.random, Date.now, Math.sin/cos trong tick; duyệt Map/Set theo thứ tự chèn, sort khoá khi cần.
  • Ngẫu nhiên tất định: băm (seed, tick, id, loại) ra số nguyên, thay vì một bộ sinh số có trạng thái.
  • Snapshot và hash: serializer tường minh, thứ tự field cố định; hash dùng chính serializer đó.
// Cùng đầu vào → cùng kết quả trên mọi máy
const roll = hashU32(seed, tick, n, KIND_FISH) % 1000; // 0…999
if (roll < chancePerMille) catchFish();
const out = Math.floor((stock * productivity) / 1000); // productivity tính bằng ‰

Thực hành — mốc 1:

  1. Trạng thái: lưới 64×64 (nước/đất), công trình, kho hàng, vàng.
  2. Lệnh place {kind, x, y, rot} và demolish {id}; system sản xuất: tiều phu ra 1 gỗ mỗi N tick.
  3. snapshot() ra JSON, load(json), hash().
  4. Test "live vs loaded": game A chạy 500 tick với lệnh ngẫu nhiên (seed cố định); giữa chừng nạp snapshot của A vào game B; chạy tiếp cả hai cùng lệnh; hash phải bằng nhau sau 1 và sau 100 tick.

Phần 2 — Luật chơi xây dựng và cân bằng tuần 6–8

Một game xây dựng hay nhờ vòng lặp rõ ràng: xây → sản xuất → dân được đáp ứng → thuế → xây tiếp. Phần này dạy viết vòng lặp đó thành dữ liệu và kiểm tra nó bằng người chơi giả lập, trước khi có hình vẽ nào.

Mục tiêu: Bến Nhỏ chơi được — bằng bot, trong terminal. Một người chơi giả lập đạt 50 dân trong 10–15 phút game và không bao giờ âm tiền.

Kiến thức:

  • Định nghĩa bằng dữ liệu: bảng GOODS, BUILDINGS (kích thước, chi phí, duy trì, đầu vào/đầu ra, chu kỳ), TIERS (nhu cầu, thuế). Thêm nội dung = thêm một dòng, không thêm code.
  • Đặt công trình trên lưới: xoay, ô bị chiếm, luật địa hình (ven biển, trên mỏ). Một hàm kiểm tra trả về lý do (notOwned, needsCoast) để giao diện có chữ giải thích.
  • Đường và kết nối: BFS từ kho cảng; công trình không nối đường thì không chạy.
  • Chuỗi sản xuất: bộ đệm vào/ra nhỏ, xe hàng chở về kho theo chu kỳ; lý do ngừng có thứ tự cố định (thiếu đường → thiếu đầu vào → kho đầy).
  • Dân và nhu cầu: nhà lớn dần khi được đáp ứng; mức đáp ứng tính bằng ‰; thuế tính bằng milli-vàng để không mất phần lẻ.
  • Cân bằng: đặt cửa sổ mục tiêu ("100 dân ở phút 8–14") rồi đo bằng bot, không cân bằng bằng cảm giác.

Thực hành — mốc 2:

  1. Viết defs/ cho 6–8 công trình: kho cảng, nhà, tiều phu, xưởng cưa, ngư trại, chợ, đường.
  2. Viết checkPlacement trả lý do và test từng lý do.
  3. Viết bot newcomer chỉ gửi lệnh thường, mỗi 5 giây game một thao tác.
  4. In bảng theo phút: vàng, thu/chi, dân, kho, công trình nhàn rỗi. Chỉnh defs/ tới khi bot lọt cửa sổ mục tiêu, rồi biến cửa sổ đó thành test.

Phần 3 — Đồ hoạ 3D với Three.js tuần 9–11

Hình vẽ chỉ đọc trạng thái của sim và gửi lệnh; nó không bao giờ tự sửa thế giới. Giữ được ranh giới này thì đồ hoạ có thể đổi thoải mái mà không làm lệch mạng.

Mục tiêu: thấy đảo của Bến Nhỏ trong 3D, đặt công trình bằng chuột, 500 công trình vẫn giữ 60 khung/giây.

Kiến thức:

  • Cơ bản Three.js: scene, camera, renderer, ánh sáng, bóng đổ, vật liệu.
  • Camera kiểu game chiến thuật: kéo để di chuyển, cuộn để zoom, xoay quanh một điểm, giới hạn góc nghiêng.
  • Địa hình từ lưới: dựng mesh từ mảng ô; làm mượt bờ biển nhưng tâm mỗi ô nước vẫn phải là nước đúng như sim.
  • Model GLB: tải bằng GLTFLoader, nén meshopt, nguồn CC0 (Kenney, Quaternius); ghi công tác giả ngay khi thêm.
  • Vẽ nhiều vật: InstancedMesh/BatchedMesh để hàng trăm công trình chỉ tốn vài chục draw call.
  • Chọn vật (picking): từ chuột ra tia, giao với mặt phẳng đất rồi đổi ra toạ độ ô; bóng ma xanh/đỏ dùng chính hàm kiểm tra của sim.
  • Nội suy: tàu và xe hàng di chuyển mượt giữa hai tick.
  • Đo đạc: draw call, số tam giác, thời gian khung (renderer.info, DevTools Performance).

Thực hành — mốc 3:

  1. Render system đọc trạng thái sim mỗi khung, không giữ bản sao riêng.
  2. Đảo từ lưới 64×64, nước có màu theo độ sâu.
  3. Bóng ma công trình theo chuột, bấm để gửi lệnh place, phím R để xoay.
  4. Cảnh thử 500 công trình: ghi lại draw call và thời gian khung trước và sau khi dùng instancing.

Phần 4 — Giao diện và nhập liệu tuần 12–13

Giao diện (HUD) là nơi người chơi hiểu luật: vì sao xưởng đứng, thiếu gì, bấm đâu. Chuột, cảm ứng và tay cầm đều phải đi qua một bộ hành động chung, nếu không mỗi thiết bị sẽ lệch luật một kiểu.

Mục tiêu: HUD của Bến Nhỏ có thanh vàng/dân/hàng, menu xây dựng, bảng thông tin công trình; chơi được bằng chuột và bằng điện thoại.

Kiến thức:

  • React: component, state, hook; tách store giao diện (phòng, công cụ đang chọn) khỏi store game (tạo lại mỗi lần nạp snapshot).
  • Đọc trạng thái game có tiết chế (throttle), không render lại React 10 lần/giây.
  • Đa ngôn ngữ: mọi chữ qua t(); file tiếng Việt là chuẩn, file tiếng Anh bị TypeScript ép cùng cấu trúc.
  • HUD ưu tiên icon: con số = icon + số, nhãn nằm trong tooltip và chữ ẩn cho trình đọc màn hình.
  • Tầng nhập liệu: hàm click, context, drag, cancel dùng chung; chuột, cảm ứng (kéo, chụm, nhấn giữ) và tay cầm (Gamepad API) chỉ là lớp chuyển đổi.
  • Điện thoại là một "vỏ" riêng: thanh dưới, một bảng trượt (sheet) mỗi lúc, nút ≥ 44 px, đặt nhà phải bấm ✓ xác nhận.

Thực hành — mốc 4:

  1. Thanh trên, menu xây dựng (phím số), bảng công trình hiện lý do ngừng bằng icon.
  2. Phím tắt: R xoay, Esc huỷ, Delete phá dỡ; hộp thoại chặn phím tắt khi mở.
  3. Cảm ứng: kéo bản đồ, chụm zoom, chạm chọn, ✓ để đặt.
  4. Thử trên 3 cỡ màn hình (390×844, 844×390, 1280×760): không chồng, không tràn.

Phần 5 — Nhiều người chơi kiểu lockstep tuần 14–16

Trong lockstep có host, server giữ thứ tự lệnh chứ không tính hộ ai. Mỗi máy tự chạy sim; server cũng chạy một bản để lưu game và làm chuẩn khi so hash.

Vuốt ngang để xem hết sơ đồ →

Server chỉ xếp thứ tự lệnh; mọi máy tự chạy cùng một sim Client AServerClient B cmd: đặt nhà ở ô (12, 8) gom lệnh · mỗi 100 ms chạy step(cmds) ticks: from, n, cticks: from, n, c step(cmds) y hệt → vẽstep(cmds) y hệt → vẽ hash, mỗi 50 tickhash, mỗi 50 tick hash lệch! xin resync snapshot đầy đủ nạp snapshot, chạy tiếp khớp → chạy tiếp
Lệnh đi lên server, thứ tự đi xuống mọi máy; chỉ khi hash lệch (phần tô màu) mới phải gửi cả trạng thái.

Mục tiêu: 2–4 người chơi chung một thế giới qua Internet; người vào giữa ván nhận snapshot; mất mạng thì tự nối lại; máy lệch thì tự đồng bộ lại.

Kiến thức:

  • Ba mô hình mạng: gửi trạng thái, lockstep, client-server có dự đoán. Game xây dựng hợp lockstep vì thế giới lớn, lệnh ít, trễ 200 ms vẫn chơi được.
  • Vòng lặp: client gửi cmd → server gom lệnh, mỗi 100 ms chạy step(cmds) → phát ticks {from, n, c} → mọi client chạy y hệt.
  • Phát hiện lệch: server gửi hash định kỳ; client khác hash thì xin resync và nạp lại snapshot.
  • Vào giữa ván: welcome chứa snapshot và tick hiện tại.
  • Không tin client: kiểm tra kiểu, khoảng giá trị và quyền của mọi lệnh; giới hạn số lệnh mỗi tick theo loại; token bucket mỗi kết nối; ngắt client để bộ đệm gửi phình to.
  • Phiên bản: client gửi dấu vân tay của mã sim; khác server thì tải lại trang lấy bản mới.
  • Khởi động lại êm: server nhận SIGTERM thì lưu mọi phòng rồi thoát; client tự nối lại, không bị đá về sảnh.

Thực hành — mốc 5:

  1. Server ws: phòng, hello/welcome, hàng đợi lệnh, vòng tick 100 ms.
  2. Client gửi lệnh qua socket thay vì gọi step trực tiếp.
  3. Hash mỗi 50 tick; cố ý sửa sim ở một client để gây lệch, xem resync hoạt động.
  4. Bộ lọc lệnh (sanitizer) và test với dữ liệu rác: số âm, NaN, chuỗi 1 MB, id không tồn tại.
  5. Mở hai tab, ngắt mạng một tab 30 giây rồi nối lại.

Phần 6 — Lưu game và nâng cấp định dạng save tuần 17

Khi đã có người chơi thật, save của họ quan trọng hơn code của bạn. Mọi thay đổi luật về sau phải đọc được save cũ và biến nó thành thế giới hợp lệ theo luật mới.

Mục tiêu: Bến Nhỏ có SNAPSHOT_VERSION, nâng một save v1 lên v2 có test, và có sao lưu trước khi nâng.

Kiến thức:

  • Loader phủ dữ liệu lưu lên giá trị mặc định: thêm field hoặc loại hàng mới không làm hỏng save cũ.
  • Bảng GOODS/BUILDINGS chỉ được thêm vào cuối, vì chỉ số của chúng nằm trong save.
  • Thay đổi phá vỡ định dạng: tăng version, viết các bước di trú đánh số, idempotent (chạy hai lần vẫn ra cùng kết quả).
  • Fixture: sinh save mẫu bằng code cũ trước khi sửa, rồi không bao giờ sinh lại.
  • Nén: lưới ô mã hoá RLE thay vì mảng số dài.
  • Ghi file an toàn: ghi ra file tạm rồi đổi tên, không ghi đè trực tiếp.

Thực hành — mốc 6:

  1. Sinh fixture world.v1.json từ code hiện tại.
  2. Đổi luật: kho hàng từ "mỗi người một kho" sang "mỗi đảo một kho". Tăng version lên 2.
  3. Viết bước di trú v1 → v2 và test: nạp fixture, chạy 100 tick, không lỗi, tổng hàng không mất.
  4. Server chép <phòng>.pre-v2-<giờ>.json trước khi ghi bản đã nâng; người chơi thấy một dòng chat giải thích thay đổi.

Phần 7 — Người chơi mới: hướng dẫn chơi tuần 18

Game xây dựng mất người chơi ở 10 phút đầu. Hướng dẫn tốt là một kịch bản dữ liệu, được kiểm bằng một "người học giả lập" trên nhiều bản đồ, không phải bằng cảm giác của người làm game.

Mục tiêu: hướng dẫn 6 bước cho Bến Nhỏ (lập kho → đường → nhà → gỗ → ván → 50 dân); bot người học qua hết trên 5 bản đồ, lập kho dưới 3 phút.

Kiến thức:

  • Kịch bản thuần: mỗi bước có done, progress, guide chỉ đọc trạng thái sim, không đụng DOM, nên bot chạy không cần trình duyệt cũng import được.
  • Chỉ đếm công trình/dân của chính người chơi; bước đã đạt thì tự qua.
  • Giao diện mở dần: phần HUD chưa giới thiệu không có trong DOM; mỗi bước mở thêm một phần kèm nhãn "Mới".
  • Chỉ chỗ trên bản đồ: khung chỗ đặt, đường nối có điểm đầu/cuối; nhãn đứng cạnh đích, không đè lên đích; bấm vào nhãn là làm luôn.
  • Gỡ kẹt: cùng một bước quá 45 giây thì hiện "Làm giúp"; chỉ dẫn tự đổi khi người chơi lạc (hết ván, hết cây).
  • Telemetry ẩn danh, chỉ khi người chơi đồng ý: thời gian mỗi bước, chỗ bỏ cuộc.

Thực hành — mốc 7:

  1. Viết kịch bản 6 bước dạng dữ liệu, chữ qua t().
  2. Hàm planGuide thuần: tìm chỗ đặt hợp lệ gần kho cảng và đường nối ngắn nhất.
  3. Bot người học chỉ làm đúng điều bước đang chỉ, mỗi 5 giây game một thao tác; in thời gian từng bước và chỗ kẹt.
  4. Nhờ một người chưa chơi bao giờ thử; ghi lại chỗ họ dừng lại quá 10 giây.

Phần 8 — Kiểm thử tuần 19, và suốt khoá

Test là thứ cho phép sửa một game đang có người chơi mà không sợ. Tuần 19 dành để bổ sung tầng còn thiếu, nhưng từ mốc 1 trở đi mỗi tính năng đều phải có test đi kèm.

Mục tiêu: Bến Nhỏ có đủ năm tầng test dưới đây và một lệnh "cổng" phải xanh trước mỗi lần phát hành.

TầngKiểm gìCông cụKhi nào chạy
Luật chơiTừng luật, từng lý do từ chối, di trú savevitestMọi commit
LockstepGame chạy liên tục và game nạp snapshot giữa chừng có cùng hashvitest, lệnh ngẫu nhiên seed cố địnhMọi commit
Cân bằngBot đạt cửa sổ mục tiêu trong 60 phút gamevitest, cấu hình riêng (chậm)Khi đổi defs/ hay luật kinh tế
ServerSao lưu, khôi phục, mật khẩu, giới hạn lạm dụngnode:testMọi commit phần server
E2ENgười thật bấm chuột, chạm, tay cầm; chụp ảnhPlaywright, Chromium dùng GPUTrước khi phát hành

Kiến thức:

  • E2E chạy trên bản build tĩnh và một server mới, cổng riêng, thư mục save mới mỗi lần; không bao giờ chạm server thật.
  • Hook kiểm thử window.__game chỉ có trong bản dev hoặc bản build debug: test hỏi "ô này ở đâu trên màn hình" rồi bấm chuột thật vào đó.
  • Cảnh dựng sẵn: script tạo save 300 công trình, hoặc thế giới dừng ngay trước một bước hướng dẫn.
  • Lỗi người chơi báo → giữ save làm fixture → viết test tái hiện trước khi sửa.
  • WebGL headless: có GPU thì nhanh hơn khoảng 30 lần so với SwiftShader; in tên renderer để biết đang dùng gì.

Thực hành — mốc 8:

  1. Test lockstep với 3 người chơi, người thứ ba vào giữa ván.
  2. Suite Playwright "play": lập kho, kéo đường, đặt nhà, phá dỡ — chỉ bằng chuột thật.
  3. Suite "phone" giả lập điện thoại: đặt nhà bằng chạm và ✓, kiểm không nút nào nhỏ hơn 44 px.
  4. Script gate: typecheck && test && e2e.

Phần 9 — Vận hành và phát hành cho bạn bè tuần 20

Game chỉ thật sự tồn tại khi có người chơi. Phát hành từng phần nhỏ ngay khi xong, nhưng mỗi lần phải có đường lùi và bản sao lưu.

Mục tiêu: Bến Nhỏ chạy trên một tên miền thật, có server thử nghiệm riêng, sao lưu hằng ngày, nút báo lỗi, và đã diễn tập quay lại bản cũ một lần.

Kiến thức:

  • Một cổng duy nhất: server Node phục vụ cả file tĩnh (ETag, nén brotli/gzip, file có hash thì cache vĩnh viễn) lẫn WebSocket.
  • Giám sát tiến trình: script tự khởi động lại khi server chết; ghi PID và log ra file.
  • Git worktree: nhánh main để làm việc, nhánh release chạy production, mỗi bản phát hành một tag.
  • Server thử nghiệm: cổng khác, thư mục save riêng, không bao giờ trỏ vào save thật.
  • Sao lưu: mỗi ngày chép mọi save sang ổ khác, giữ 7 ngày; công cụ khôi phục kiểm save có nạp được không rồi mới đặt vào.
  • Báo lỗi trong game: một phím gửi mô tả, ảnh chụp màn hình và trạng thái; có giới hạn dung lượng và số lần mỗi IP.
  • Thế giới có mật khẩu: băm bằng scrypt, giới hạn số lần nhập sai.

Thực hành — mốc 9:

  1. Script serve.sh: build client, dừng bản cũ bằng SIGTERM, chạy bản mới dưới giám sát.
  2. Viết runbook một trang: triển khai, quay lại tag trước, khôi phục save từ bản sao lưu.
  3. Diễn tập: triển khai bản có nâng save lên server thử nghiệm (chạy trên bản sao save thật), rồi quay lại theo đúng runbook.
  4. Mời 3 người bạn chơi một buổi tối; đọc mọi báo lỗi hôm sau.

Phần 10 — Làm việc cùng AI suốt khoá, từ tuần 3

Thương Cảng 1800 được làm chủ yếu bằng cách giao việc cho trợ lý lập trình AI (Claude Code); người làm game thiết kế, giao việc, kiểm tra và chơi thử. AI làm nhanh, nhưng chỉ an toàn khi dự án có tài liệu tốt và cổng test tự động.

Mục tiêu: học viên giao được một tính năng trọn vẹn cho AI và nhận về code mình hiểu, có test, không phá tính năng cũ.

Kiến thức:

  • CLAUDE.md ở gốc repo: cấu trúc, lệnh, quy ước, bẫy đã gặp. Mỗi lần AI vấp một bẫy mới, ghi thêm một dòng.
  • Thiết kế trước, code sau: mỗi tính năng lớn có tài liệu thiết kế (mục tiêu, luật, con số, câu hỏi mở), chia thành gói việc nhỏ, mỗi gói có tiêu chí xong.
  • Cổng kiểm tra: AI chỉ commit khi typecheck && test xanh; tính năng giao diện phải có E2E và ảnh chụp để người xem lại.
  • Dùng AI để hiểu, không chỉ để viết: nhờ giải thích một đoạn code, so sánh hai cách, tìm nguyên nhân lỗi từ save người chơi gửi.
  • Đọc lại diff, nhất là trong sim: một dòng sai ở đó làm lệch mọi máy.
  • Giới hạn: AI không thay bạn cảm nhận game có vui không, nhịp có đúng không, chữ có dễ hiểu không. Phải tự chơi và nhờ người khác chơi.

Thực hành (áp dụng xuyên các mốc):

  1. Từ tuần 3, viết CLAUDE.md cho Bến Nhỏ; cập nhật mỗi tuần.
  2. Ở Phần 4 hoặc 5, viết một tài liệu thiết kế (ví dụ "Tàu và lập thuộc địa") rồi giao AI làm từng gói.
  3. So sánh một tính năng tự làm tay với một tính năng giao AI: thời gian, số lỗi, chỗ phải làm lại.

Đồ án cuối khoá và tiêu chí đánh giá

Đồ án: tự thiết kế và thêm một tính năng vào Bến Nhỏ đi qua mọi tầng, rồi tổ chức một buổi chơi thật với ít nhất 3 người trong 45 phút. Gợi ý tính năng: tầng dân thứ hai, tuyến tàu chở hàng giữa hai đảo, hoặc buôn bán giữa hai người chơi.

Nộp: repo, tài liệu thiết kế của tính năng, link chơi được, và báo cáo một trang sau buổi chơi (bao nhiêu người, lỗi gặp, đã sửa gì).

Tiêu chíĐạtTốt
Tất địnhTest lockstep xanh, có lệnh mới trong bộ sinh lệnh ngẫu nhiênKhông có lần resync nào trong buổi chơi 45 phút (đọc log server)
Luật và cân bằngCó cửa sổ mục tiêu, bot đạtHai chiến lược bot; mỗi con số có lý do trong tài liệu thiết kế
SaveSave trước tính năng vẫn nạp đượcFixture cũ, test di trú, bản sao trước khi nâng
Đồ hoạ60 khung/giây với 300 công trình trên máy tầm trungĐo cả trên điện thoại; số đo được ghi lại
Giao diệnDùng được bằng chuột và phímCả cảm ứng; không chồng, không tràn ở 3 cỡ màn hình
Người mớiCó bước hướng dẫn cho tính năng mớiBot người học qua trên 5 bản đồ; một người lạ chơi qua không cần hỏi
Kiểm thửtypecheck và unit test xanhE2E bấm chuột thật cho tính năng mới
Vận hànhChạy trên tên miền thậtCó runbook, đã diễn tập quay lại bản cũ

Hoàn thành khoá khi mọi tiêu chí ở mức "Đạt" và ít nhất ba tiêu chí ở mức "Tốt".

Tài liệu tham khảo

Đọc theo đúng phần đang học, không cần đọc hết trước. Tài liệu tiếng Anh; phần đánh dấu "bắt buộc" nên đọc kỹ.

Tài liệuPhầnĐọc gì
TypeScript Handbook0Toàn bộ phần cơ bản
pnpm workspaces0Cách các gói dùng chung mã
Game Programming Patterns (Robert Nystrom)1, 2Bắt buộc: Game Loop, Update Method, Component, Command
Fix Your Timestep! (Glenn Fiedler)1Bắt buộc: tick cố định và nội suy
kho1, 4README: atom, effect, ECS, system, dùng với React
Introduction to the A* Algorithm (Red Blob Games)2Tìm đường cho tàu và xe hàng; cả trang web về lưới ô
three.js manual3Fundamentals, Optimizing Lots of Objects, Picking
Discover three.js3Giáo trình Three.js từ đầu, dễ hơn manual
React — Learn4Component, state, hook
Deterministic Lockstep (Glenn Fiedler)5Bắt buộc
1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond5Bắt buộc: lockstep trong một game chiến thuật thật, cách họ tìm lỗi lệch
Playwright8Getting started, Mouse, Touchscreen, Screenshots
Kenney · Quaternius · game-icons.net3, 4Model CC0 và icon CC BY 3.0 — nhớ ghi công

Chơi thêm Anno 1800 vài giờ với câu hỏi "vì sao bước này dễ hiểu?" sẽ giúp Phần 2, 4 và 7 nhiều hơn mọi tài liệu trên.