遇事不决,掷一掷
基于 Three.js 的 3D 骰子决策小游戏,支持自定义骰子文字。
项目简介
这是一个有趣的 3D 骰子决策小游戏。当你面临选择困难时,不妨让骰子帮你做决定。
项目使用 Three.js 实现了真实的 3D 骰子物理效果,包括抛掷、旋转、弹跳和落地动画。骰子的六个面可以自定义文字内容,让你可以根据不同场景定制决策选项。
游戏界面
核心特性
🎲 真实物理模拟
骰子动画分为三个阶段,模拟真实的物理效果:
- 抛起阶段(0-50%):骰子从桌面抛起,快速随机旋转
- 下落阶段(50-80%):旋转逐渐减速,向最终角度过渡
- 着陆阶段(80-100%):小幅弹跳后平稳落地
// 阶段1: 抛起和快速旋转
if (progress < 0.5) {
const rotationSpeed = Math.sin(progress * Math.PI) * 1.2 + 0.8;
const rotationProgress = progress * 2 * rotationSpeed;
this.dice.rotation.x = initialRotation.x + totalRotation.x * rotationProgress;
this.dice.rotation.y = initialRotation.y + totalRotation.y * rotationProgress;
this.dice.rotation.z = initialRotation.z + totalRotation.z * rotationProgress;
// 从桌面抛起
const bounceHeight = 0.9 + 2.5 * Math.sin(progress * Math.PI);
this.dice.position.y = bounceHeight;
}
🎨 自定义骰子文字
支持自定义骰子六个面的文字内容,可以输入任意长度的文本(骰子上显示前两个字符):
createTextTexture(text, color = '#000000') {
const displayText = text.substring(0, 2);
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
// 绘制背景和边框
context.fillStyle = '#ffffff';
context.fillRect(0, 0, size, size);
// 绘制文字
context.fillStyle = color;
context.font = 'bold 100px Arial, "Microsoft YaHei"';
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillText(displayText, size / 2, size / 2);
return new THREE.CanvasTexture(canvas);
}
📱 移动端优化
- 支持向上滑动手势触发掷骰子
- 响应式布局,适配不同屏幕尺寸
- 横屏模式自动调整布局
/* 移动端适配 */
@media (max-width: 414px) {
.game-container {
max-width: 100%;
padding: 15px;
}
#scene-container {
height: 250px;
}
}
/* 横屏模式 */
@media (orientation: landscape) and (max-height: 600px) {
.game-container {
flex-direction: row;
flex-wrap: wrap;
}
}
技术实现
Three.js 场景搭建
使用 Three.js 创建 3D 场景,包括相机、渲染器、光源和桌面:
createScene() {
// 创建场景
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x000020);
// 创建相机 - 从斜上方观察
this.camera = new THREE.PerspectiveCamera(40, width / height, 0.1, 1000);
this.camera.position.set(3, 5, 8);
this.camera.lookAt(0, 0.9, 0);
// 创建渲染器
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
this.renderer.shadowMap.enabled = true;
}
骰子材质与纹理
使用 Canvas 动态生成骰子每个面的纹理,支持中文显示:
createDice() {
const geometry = new THREE.BoxGeometry(1.8, 1.8, 1.8);
// 为六个面创建不同的材质
const materials = [
new THREE.MeshLambertMaterial({
map: this.createTextTexture(this.faceTexts[4], this.getRandomColor())
}),
// ... 其他五个面
];
this.dice = new THREE.Mesh(geometry, materials);
this.dice.castShadow = true;
this.dice.position.set(0, 0.9, 0);
}
角度插值算法
确保骰子旋转时选择最短路径,避免不自然的旋转:
lerpAngle(startAngle, endAngle, t) {
// 标准化角度到-PI到PI之间
const normalizeAngle = (angle) => {
while (angle > Math.PI) angle -= Math.PI * 2;
while (angle < -Math.PI) angle += Math.PI * 2;
return angle;
};
// 计算最短路径的角度差
let delta = normalizeAngle(endAngle - startAngle);
// 线性插值
return startAngle + delta * t;
}
性能优化
- Canvas 纹理缓存:动态生成的文字纹理使用 CanvasTexture,避免重复计算
- 阴影优化:使用 PCFSoftShadowMap 实现柔和阴影,同时控制阴影贴图大小
- 像素比限制:限制渲染器的像素比,避免在高分辨率设备上过度渲染
- 事件节流:窗口 resize 事件使用节流,减少重复计算
使用体验
- 点击"摇起来"按钮或向上滑动屏幕触发掷骰子
- 在"骰子配置"面板中自定义六个面的文字
- 点击"更新骰子"按钮应用新的配置
- 骰子会随机旋转 2-3 秒后落地,显示最终结果
应用场景
- 日常决策辅助(吃什么、去哪玩)
- 团队活动抽签
- 游戏随机事件
- 趣味互动工具
技术栈
- Three.js r128:3D 场景渲染
- 原生 JavaScript:游戏逻辑实现
- CSS3:响应式布局和动画效果
- Canvas API:动态纹理生成
未来规划
- 支持多个骰子同时投掷
- 添加音效和震动反馈
- 支持自定义骰子颜色和材质
- 添加历史记录功能
- 支持分享结果到社交平台