这里给大家分享我在网上总结出来的一些知识 ,希望对大家有所帮助
shape上有一个属性表示了孔洞了 ,接下来就好办了
function createDoorWall() {
let { shape } = genwallShape();
const door = new THREE.Path();
//门的位置
door.moveTo(baseWidth / 2 + 5, 0);
door.lineTo(baseWidth / 2 + 5, 16);
door.lineTo(baseWidth / 2 - 5, 16);
door.lineTo(baseWidth / 2 - 5, 0);
door.lineTo(baseWidth / 2 + 5, 0);
// 形状上的孔洞
shape.holes.push(door);
let mesh = createIrregularWall(shape, [
-baseWidth / 2,
0,
baseLength / 2 - 1,
]);
mesh.name = "带门的墙";
}
调用后如下
可以看到门的形状已经出来了
4 、创建屋顶
这里我们开始创建屋顶 ,首先求出屋顶的宽度 ,也就是我们要创建的几何体的z轴的延展
function createRoof() {
//屋顶宽
let width = Math.sqrt((baseWidth / 2) ** 2 + 5 ** 2) + 5;//+5让有一点屋檐的效果
const geometry = new THREE.BoxGeometry(baseLength + 2, width, 1);
const texture = new THREE.TextureLoader().load("/img/tile.jpg");
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(2, 2);
const material = new THREE.MeshPhongMaterial({ map: texture });
const mesh = new THREE.Mesh(geometry, material);
mesh.rotateZ(THREE.MathUtils.degToRad(75));
mesh.rotateY(-Math.PI / 2);
mesh.position.set(baseWidth / 3 - 1, 22, 0);
mesh.name = "右屋顶";
group.add(mesh);
return { roof: mesh, width };
}
方法执行后如下图 ,我们有了一个右边的屋顶
如法炮制 ,再来一个左边的屋顶
let { roof, width } = createRoof();
return
let leftRoof = roof.clone();
leftRoof.rotateX(THREE.MathUtils.degToRad(30));
leftRoof.position.set(-baseWidth / 3 + 1, 22, 0);
leftRoof.name = "左屋顶";
group.add(leftRoof);
随后可以在画面中看到如下图 ,我们的房子 ,哦不 ,准确的说是仓库已经出来了 。 。。
5 、创建门
然后我们开始创建门,门的创建也是用的内置的BoxGeometry几何体 。 添加一个createDoor方法 ,如下
function createDoor() {
//纹理贴图
const texture = new THREE.TextureLoader().load("/img/door.jpg");
//门的大小 、尺寸
const door = new THREE.BoxGeometry(10, 15, 0.5);
const material = new THREE.MeshPhongMaterial({
map: texture,
transparent: true,
opacity: 1,
});
const doorMesh = new THREE.Mesh(door, material);
doorMesh.name = "门";
doorMesh.position.x = 5;
group.add(doorGroup);
}
调用后即可看到原本的门洞中出现了一扇门 ,如下图
6 、为场景添加点击
接下来我想做一点击开门的效果,那么首先要获取到鼠标点击了哪些物体 。很巧的是three.js为我们提供了一个Raycaster类 ,用来检测射线触碰到了哪些物体 。 添加如下代码
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
function onPointerMove(event) {
// 将鼠标位置归一化为设备坐标 。x 和 y 方向的取值范围是 (-1 to +1)
pointer.x = (event.clientX / width) * 2 - 1;
pointer.y = -(event.clientY / height) * 2 + 1;
}
然后为canva添加点击事件监听
canvas.addEventListener(
"click",
() => {
const intersects = raycaster.intersectObjects(scene.children);
console.log(intersects[0]);
console.log("点击了", intersects[0]?.object?.name);
},
false
);
随后我们点击场景中 ,在控制台中便能清晰的打印出我们所点击的物体 。说明:intersectObjects方法会返回射线经过的所有物体组成的数组,数组的第0位为离点击区域最近的物体 ,因此可以视为被点击的物体 。
现在已经知道点击的是哪个物体 ,下面就来添加门的动画效果
7 、添加关门 、开门动画效果
调整一下对上节的方法 ,如下 ,匹配到点击的物体是门的时候再来触发 。
canvas.addEventListener(
"click",
() => {
const intersects = raycaster.intersectObjects(scene.children);
console.log(intersects[0]);
console.log("点击了", intersects[0]?.object?.name);
if (intersects[0]?.object?.name == "门") {
// console.log(intersects[0].object.parent.rotation.y);
let speed = 0.05;
//再次点击关门
if (intersects[0].object.parent.rotation.y <= -2.5) {
// console.log("关门");
let a = setInterval(() => {
if (intersects[0].object.parent.rotation.y >= 0) {
intersects[0].object.parent.rotation.y = 0;
clearInterval(a);
return;
}
intersects[0].object.parent.rotation.y += speed;
}, 1000 / 60);
} else {
// console.log("开门");
let a = setInterval(() => {
if (intersects[0].object.parent.rotation.y <= -2.5) {
clearInterval(a);
return;
}
intersects[0].object.parent.rotation.y -= speed;
}, 1000 / 60);
}
}
},
false
);
这样就能正确的运行了吗?当然不 ,上述代码中让门以y轴做旋转 ,但是在three.js中 ,物体的旋转轴为物体的中心 ,因此我们需要改变一下门的旋转轴 ,使之在视觉上呈现出以旋转中心的改变,下面改造一下createDoor方法 ,如下 ,
//创建门
function createDoor() {
const texture = new THREE.TextureLoader().load("/img/door.jpg");
const door = new THREE.BoxGeometry(10, 15, 0.5);
const material = new THREE.MeshPhongMaterial({
map: texture,
transparent: true,
opacity: 1,
});
const doorMesh = new THREE.Mesh(door, material);
// doorMesh.rotateY(Math.PI / 2);
// doorMesh.position.set(-baseLength / 2, 7, 0);
doorMesh.name = "门";
//以下代码做出了更改
const doorGroup = new THREE.Group();//添加一个门的父级
doorGroup.name = "门的包裹";
doorGroup.position.set(-5, 8, baseLength / 2);//通过父级来改变门的旋转轴
//现在这个是相对于父级
doorMesh.position.x = 5;
doorGroup.add(doorMesh);
group.add(doorGroup);
return doorGroup;
}
改造完后点击门,会发现门绕着预期的旋转轴打开了 。如下图
四 、创建天空盒
这里的天空盒非常的简单 。使用内置的SphereGeometry几何体创建一个与地面半径一致的圆 ,然后载入贴图
//天空盒
function createSkyBox() {
const texture = new THREE.TextureLoader().load("/img/sky.jpg");
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
// texture.repeat.set(1, 1);
const skyBox = new THREE.SphereGeometry(500, 100, 100);
const material = new THREE.MeshPhongMaterial({
map: texture,
side: THREE.BackSide,
});
const skyBoxMesh = new THREE.Mesh(skyBox, material);
scene.add(skyBoxMesh);
}
最终呈现出的效果如下图
在最后你通过循环多创建几个房子 ,像这样
或者查看文档切换成第一人称控制器在自己创建的场景中遨游。
本文转载于:
https://juejin.cn/post/7200571354926858301
如果对您有所帮助,欢迎您点个关注 ,我会定时更新技术文档 ,大家一起讨论学习 ,一起进步 。
声明:本站所有文章 ,如无特殊说明或标注 ,均为本站原创发布 。任何个人或组织 ,在未征得本站同意时 ,禁止复制 、盗用 、采集、发布本站内容到任何网站 、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益 ,可联系我们进行处理 。