Xúc xắc lăn thật sự: kiểu đổ 3D mới
Xúc xắc trên bàn cờ giờ là những khối lập phương 3D thật, lấy đà, lăn lộn rồi dừng đúng vào con số của bạn — không có độ trễ giả tạo, và nhẹ nhàng với chế độ giảm chuyển động.

Nói đơn giản
Trước đây việc đổ xúc xắc trông phẳng lì: các con số chỉ lật qua. Giờ thì hai viên xúc xắc di chuyển — và xúc xắc Tốc độ — là những khối lập phương 3D thật, biết khom xuống, bật lên và lăn lộn trong không trung trước khi dừng lại với mặt số ngửa lên trên bàn.
Và đây là phần thú vị: xúc xắc chỉ tiếp tục quay trong lúc ván chơi tính nước đi của bạn, rồi rơi xuống và dừng đúng vào những con số bạn đã đổ. Không có khoảng dừng giả tạo để câu giờ — ngay khi kết quả của bạn sẵn sàng, chúng rơi xuống. Với kết nối nhanh, tất cả kết thúc chỉ trong nháy mắt.
- Một cú lấy đà nhanh ngay khi bạn chạm Đổ xúc xắc, để cảm giác phản hồi tức thì.
- Một pha lăn lộn thật sự trong không trung trong lúc nước đi của bạn được tính toán — nó kéo dài một cách tự nhiên nếu máy chủ chậm, thay vì hiện một ảnh tạm.
- Một cú đáp mượt mà xuống đúng những con số thật của bạn, và quân cờ của bạn bắt đầu di chuyển ngay khi xúc xắc dừng lại.
- Xúc xắc Tốc độ cũng góp mặt, và vẫn hiện một dấu «?» khẽ khàng giữa các lượt đổ để bạn luôn biết nó đang trong cuộc.

Đây hoàn toàn là một thay đổi về mặt hình ảnh — luật chơi, xác suất và bàn cờ đều vận hành y hệt như trước. Chỉ là xúc xắc rốt cuộc đã có cảm giác như xúc xắc thật.
Cho người thích chi tiết kỹ thuật
Mỗi viên xúc xắc là một khối lập phương CSS 3D sáu mặt được điều khiển bởi một máy trạng thái nhỏ chạy qua idle → windup → loop → settle → jitter → landed. Hướng được lưu dưới dạng quaternion để ta có thể xoay quanh bất kỳ trục nào và chuyển thành một rotate3d() CSS duy nhất cho mỗi khung hình; FACE_Q ánh xạ mỗi giá trị 1–6 tới hướng đưa mặt chấm đó ra phía trước.
// apps/web/app/[locale]/game/[id]/dice3d.ts
// quaternion per face → one CSS rotate3d() at rest
const FACE_Q: Record<DieValue, Quat> = {
1: Q.identity,
2: Q.axis([0, 1, 0], +Math.PI / 2),
3: Q.axis([0, 1, 0], -Math.PI / 2),
4: Q.axis([1, 0, 0], +Math.PI / 2),
5: Q.axis([1, 0, 0], -Math.PI / 2),
6: Q.axis([1, 0, 0], Math.PI),
};Lượt đổ mang tính lạc quan: một cú chạm làm tăng một nonce lạc quan, khởi động pha windup 180ms và một vòng lặp lăn loop vô hạn định. Khi lượt đổ có thẩm quyền tới (một lần tăng rollSeq), viên xúc xắc đang quay sẽ dừng lại ngay lập tức theo vòng xoay tới trước ngắn nhất về FACE_Q[value] rồi rơi xuống — không có ngưỡng quay tối thiểu, nên một phản hồi nhanh sẽ cho một lượt đổ nhanh.
// authoritative values arrive → settle now, no spin floor
onRollSeq(values) {
values.forEach((v, i) => {
if (controller[i].isSpinning) controller[i].setResult(v, arrival + i * 78);
else controller[i].startRoll(arrival), controller[i].setResult(v, /* after windup */);
});
}Theo quyết định của chủ sở hữu, những cú nảy đáp đất tắt dần trong bản bàn giao đã bị loại bỏ (restitution bằng 0), nên pha dừng là một cú hạ xuống mượt mà, tăng tốc đều, chạm đúng vào giá trị, chỉ để lại một dao động tắt dần ngắn ngủi. Một vòng lặp rAF dùng chung duy nhất chỉ chạy khi có một viên xúc xắc đang được hoạt họa, mỗi pha dùng dấu thời gian tuyệt đối từ performance.now() (để một tab ở chế độ nền tua tới đúng cách), và một cơ chế dự phòng sẽ dừng ở các giá trị hiện tại trong store nếu không có lượt đổ nào tới. Chế độ giảm chuyển động bỏ qua hoàn toàn nonce lạc quan và chỉ đổi mặt xúc xắc. Chỉ riêng web: engine, daemon và cơ sở dữ liệu đều không bị đụng tới.
