赞
踩
入门 three.js 也有一阵子了,我发现用它做 3D 挺有趣的,而且学习门槛也不算高。在这篇博文中,我想分享一下利用 three.js 做一个 3D 版汉诺塔(河内塔)的过程,以及对 three.js 相关知识点进行一次较为全面的实战总结。希望能与大家交流技术心得和经验,一起共同进步。
游戏规则:将串在左边柱杆(A 柱)上的盘子全部挪进右边柱杆(C 柱)即可获胜;一次只能挪动最上面的一个盘子;每个盘子的上面只能放置比它小的盘子;可利用中间的柱杆(B 柱)来中转、倒换盘子。
可自由选择游戏难度(盘子数量),游戏中途可随时重开,获胜后会有该局耗时和步数的统计信息。
编程语言:JavaScript(ES6+)
代码架构:MVP
用到的框架 / 库:
- three.js - JavaScript 3D 框架
- tween.js - 提供动画支持
- canvas-confetti - 提供彩屑特效支持
为了方便演示,避免引入底层框架(Vue、React、Angular...)的代码增加复杂度,本文中的案例没有使用前端底层框架和工程脚手架,而采用传统的 HTML 单文件方式来编写代码。
首先,准备一个空白容器,让它的尺寸与浏览器视窗大小相同,以充分利用屏幕空间。
- <style>
- body {
- padding: 0;
- margin: 0;
- font: normal 14px/1.42857 Tahoma;
- }
-
- #app {height: 100vh;}
- </style>
-
- <div id="app"></div> <!-- 空白容器 -->
-
对于 JS 脚本,使用 导入映射 配置资源的 CDN 地址,这样就可以像使用 npm 包一样导入相关资源。
- <script type="importmap">
- {
- "imports": {
- "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/+esm",
- "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
- }
- }
- </script>
- <script type="module">
- import * as THREE from 'three'; // 丝滑导入 three.js
- </script>
-
接下来,创建一个场景(Scene)、一个透视相机(PerspectiveCamera)和一个 WebGL 渲染器(WebGLRenderer),并将渲染器添加到 DOM 中。同时,编写一个渲染函数,使用 requestAnimationFrame
方法循环渲染场景。以下是最基础的初始化代码:
- <script type="module">
- import * as THREE from 'three';
-
- const containerEl = document.getElementById('app');
- const { width, height } = containerEl.getBoundingClientRect();
-
- /* 场景 */
- const scene = new THREE.Scene();
-
- /* 相机 */
- const fov = 45; // 视野角度
- const camera = new THREE.PerspectiveCamera(fov, width / height, 1, 500);
-
- /* 渲染器 */
- const renderer = new THREE.WebGLRenderer({ alpha: true });
- renderer.setSize(width, height);
- renderer.setClearColor('#f8f8f6', 1); // 设置初始化背景
- containerEl.appendChild(renderer.domElement);
-
- // 渲染场景(循环)
- (function animate() {
- requestAnimationFrame(animate);
- renderer.render(scene, camera);
- }());
- </script>
-
上面
PerspectiveCamera
设置了 4 个参数,其中最后 2 个参数分别是相机视锥体的近端面和远端面,默认是 0.1 和 2000。这里将其设为 1 和 500,让相机与物体产生的视椎体 “更小、更接近”,以节省渲染性能。
在汉诺塔游戏中,场景里主要的 3D 物体包括桌台、柱杆和盘子,我们先来添加最简单的桌台到场景中。
桌台的形状是一个长方体,我们可以使用 BoxGeometry 来实现它,网格材质则使用 MeshLambertMaterial 模拟木质的非光泽表面。
- const tableSize = {
- width: 30, // 长
- depth: 10, // 宽
- height: 0.5 // 高
- };
- const geometry = new THREE.BoxGeometry( // 立方缓冲几何体
- ...['width', 'height', 'depth'].map(key => tableSize[key])
- );
- const material = new THREE.MeshLambertMaterial({ color: '#cccca6' }); // 材质
- const table = new THREE.Mesh(geometry, material);
- scene.add(table); // 添加到场景
-
为方便调试
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。