Xuất Godot 4 sang Web (HTML5): hướng dẫn thực tế và 4 lỗi hay gặp
Việt Nam là một trong những thị trường web game lớn nhất Đông Nam Á. Người chơi không phải lúc nào cũng có máy mạnh, mạng nhanh, hay tâm trạng để tải app 200MB. Một game web load trong 5 giây trên 3G/4G là cách hiệu quả nhất để tiếp cận người chơi VN.
Godot 4 hỗ trợ xuất sang HTML5 ngay từ trong editor, nhưng nếu bạn chỉ click "Export" thì kết quả thường tệ: file 50MB, load 30 giây, tải xong vẫn lỗi SharedArrayBuffer is not defined. Bài viết này là hướng dẫn thực tế xuất Godot 4 sang web, kèm 4 lỗi mà tôi (và hầu hết dev VN khác) đã gặp lần đầu.

Phần 1: Setup ban đầu
Trong Godot, vào Project > Export…, thêm preset "Web" mới. Cài Web export template nếu chưa có (Godot sẽ tự nhắc).
Các option quan trọng:
| Option | Giá trị khuyên dùng | Lý do |
|---|---|---|
Variant > Extensions Support |
✓ bật nếu dùng GDExtension | Hầu hết indie không cần |
VRAM Texture Compression > For Mobile |
✓ | Người chơi VN nhiều mobile |
HTML > Custom HTML Shell |
tự custom (xem phần 4) | Default UI xấu |
Progressive Web App > Enable |
✓ | Cho phép install như app, offline-capable |
Progressive Web App > Ensure Cross Origin Isolation Headers |
✓ | Cần cho threading |
Sau khi export, bạn sẽ có:
index.html(entry point)index.js(loader)index.wasm(engine binary, ~30MB)index.pck(game data)- Các file PWA (manifest, service worker)
Tổng dung lượng thường 35-50MB. Nghe lớn nhưng phần lớn là engine WebAssembly, không phải game của bạn.
Phần 2: Hosting với COOP/COEP headers (Lỗi #1)
Đây là lỗi đầu tiên ai cũng dính. Mở index.html trong browser, console hiện:
SharedArrayBuffer is not defined
Lý do: Godot 4 dùng SharedArrayBuffer cho threading, mà browser yêu cầu 2 HTTP header đặc biệt mới cho phép:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
GitHub Pages, Netlify free, Vercel free đều KHÔNG hỗ trợ tự custom 2 header này. Giải pháp:
- itch.io: Hỗ trợ COOP/COEP nếu bạn check option "SharedArrayBuffer support" khi upload.
- Cloudflare Pages: Free, hỗ trợ custom headers qua file
_headers:/* Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp - Self-host trên VPS Việt Nam: Vultr/DigitalOcean Singapore + nginx config:
add_header Cross-Origin-Opener-Policy "same-origin"; add_header Cross-Origin-Embedder-Policy "require-corp";
Nếu game không cần threading (game đơn giản 2D), bạn có thể tắt threading trong Project Settings để bỏ qua hai header này. Chỉ tắt khi thật sự cần.
Phần 3: Tối ưu kích thước file (Lỗi #2)
50MB load qua mạng VN trung bình mất 15-30 giây. Giảm bằng:
- Bật Brotli compression: Hầu hết static host (Cloudflare, Vercel) tự bật. File
index.wasm30MB sẽ thành ~10MB sau khi compress. - Texture compression: Trong Project Settings > Rendering > Textures, set "VRAM Compression > Import S3TC BPTC" và "Import ETC2 ASTC" cùng bật.
- Loại bỏ asset không dùng: Godot không tự loại trừ asset trong
res://ngay cả khi không reference. Dọn manual. - Audio MP3 thay vì WAV: WAV không nén, MP3 tiết kiệm 90% dung lượng.
Phần 4: Custom HTML shell (Lỗi #3)
Default HTML shell của Godot là một loading screen xám xịt với progress bar tối thiểu. Người chơi VN thấy 30 giây loading mà không có gì xảy ra → đóng tab.
Tạo custom_shell.html với progress bar tiếng Việt:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Tên Game Của Bạn</title>
</head>
<body>
<div id="loading">
<h1>Đang tải game...</h1>
<div class="bar"><div class="fill" id="progress"></div></div>
<p id="status">Đang tải engine...</p>
</div>
<canvas id="canvas"></canvas>
<script src="$GODOT_URL"></script>
<script>
const engine = new Engine($GODOT_CONFIG);
engine.startGame({
onProgress: (current, total) => {
const pct = (current / total * 100).toFixed(0);
document.getElementById('progress').style.width = pct + '%';
document.getElementById('status').innerText = 'Đang tải: ' + pct + '%';
}
}).then(() => {
document.getElementById('loading').style.display = 'none';
});
</script>
</body>
</html>
Trong Export preset, set HTML > Custom HTML Shell đến file này. Bây giờ người chơi thấy progress bar đẹp với text tiếng Việt.
Phần 5: Audio context (Lỗi #4)
Browser hiện đại không cho phép audio tự động phát trước khi user tương tác. Game Godot load xong nhưng không có âm thanh, tới khi click chuột mới có. Người chơi nghĩ game lỗi.
Giải pháp: hiển thị nút "Bắt đầu" thay vì auto-start. User click → game bắt đầu → audio context được activate.
Đây là pattern chuẩn, không phải workaround. Tất cả web game pro (Wordle, Cookie Clicker, etc.) đều làm vậy.
Phần 6: AI và web export
Tôi sẽ thẳng thắn: tôi đang xây Ziva, một AI agent cho Godot. Nên đoạn này có bias rõ ràng.
Khi tôi hỏi ChatGPT hay Claude về web export Godot 4, các lỗi thường gặp:
- Đề xuất GitHub Pages mà không nhắc đến SharedArrayBuffer issue.
- Bỏ qua custom HTML shell — chỉ dùng default.
- Không nhắc đến PWA option dù nó cải thiện trải nghiệm rất nhiều.
- Suggest GLES2 backend — đã bị xóa ở Godot 4.0.
Đây không phải vấn đề của AI nói chung; đây là vấn đề data training cũ. Godot 4 thay đổi nhiều thứ ở web export so với 3.x, và phần lớn data trên internet vẫn là 3.x.
Bài viết của chúng tôi về AI tools cho Godot phân tích chi tiết hơn về sự khác biệt giữa các AI tool.
Tổng kết
Web export Godot 4 không khó nhưng có nhiều cạm bẫy. Checklist:
- Hosting có COOP/COEP headers (Cloudflare Pages free, hoặc itch.io)
- Brotli compression bật
- Custom HTML shell với progress bar tiếng Việt
- Nút "Bắt đầu" để activate audio context
- PWA enable cho install và offline
Sau khi áp dụng, tôi giảm load time game của tôi từ 28 giây xuống 7 giây trên mạng 4G Việt Nam. Người chơi không bỏ tab nữa, retention đầu tăng đáng kể.
Hãy thử và share kết quả nếu bạn cũng đang làm web game với Godot.
All rights reserved