文章目录
- new 加入动画开关门
- 建房子
- 点击监听
- old
- 建房子
- 点击监听
new 加入动画开关门
建房子
for (let i = 0; i < 30; i++) {
const w = Math.random() * 10 + 3;
const h = House(w, w * 2, w > 7);
h.position.set(
Math.random() * 200 - 100,
0.001,
Math.random() * 200 - 100
);
h.rotateY(Math.random());
h.rotateX(Math.random());
h.rotateZ(Math.random());
scene.add(h);
}
function House(width: number = 5, length: number = 10, openDoor?: boolean) {
/*
记录该实例的组成元素 将每个元素的id和类型存入每个实例的自定义输入
可以在用户点击任意属于该实例组成部分时控制其他部分
例如:点击房盖 控制房门打开关闭
*/
const house: any = {};
const elements = [];
const group = new THREE.Group();
const material = new THREE.MeshLambertMaterial({
map: texture1,
side: THREE.DoubleSide,
});
texture2.repeat.set(2, 2);
const material2 = new THREE.MeshLambertMaterial({
map: texture2,
side: THREE.DoubleSide,
});
const door1Mesh = new THREE.MeshLambertMaterial({
map: door1Texture,
side: THREE.DoubleSide,
});
//控制房子大小主要数据
const floorProps = [width, length]; //width length
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(...floorProps),
material
);
floor.rotateX(halfPI);
floor.rotateZ(halfPI);
elements.push({ element: floor, type: "floor" });
const sideProps = [floorProps[0], 4]; //width,height
const leftSide = new THREE.Mesh(
new THREE.PlaneGeometry(...sideProps),
material
);
leftSide.position.set(-floorProps[1] / 2, sideProps[1] / 2, 0);
leftSide.rotateY(halfPI);
const rightSide = new THREE.Mesh(
new THREE.PlaneGeometry(...sideProps),
material
);
rightSide.position.set(floorProps[1] / 2, sideProps[1] / 2, 0);
rightSide.rotateY(halfPI);
elements.push(
{ element: leftSide, type: "leftSide" },
{ element: rightSide, type: "rightSide" }
);
const back = new THREE.Mesh(
new THREE.PlaneGeometry(floorProps[1], sideProps[1]),
material
);
back.position.set(0, sideProps[1] / 2, -floorProps[0] / 2);
elements.push({ element: back, type: "back" });
//前面墙
const heartShape = new THREE.Shape();
const pointX = floorProps[1] / 2;
const pointY = sideProps[1] / 2;
heartShape.moveTo(-pointX, -pointY);
heartShape.lineTo(-pointX, pointY);
heartShape.lineTo(pointX, pointY);
heartShape.lineTo(pointX, -pointY);
heartShape.lineTo(-pointX, -pointY);
//开一个口 留门
const holeX = pointX / 3;
const holeY = pointY / 3;
const windowHole = new THREE.Path();
windowHole.moveTo(-holeX, holeY);
windowHole.lineTo(0, holeY);
windowHole.lineTo(0, -pointY);
windowHole.lineTo(-holeX, -pointY);
windowHole.lineTo(-holeX, holeY);
heartShape.holes.push(windowHole);
const frontGeometry = new THREE.ShapeGeometry(heartShape);
const front = new THREE.Mesh(frontGeometry, material2);
front.position.set(0, sideProps[1] / 2, floorProps[0] / 2);
elements.push({ element: front, type: "front" });
//门
const box = new THREE.BoxGeometry(holeX, pointY + pointY / 3, 0.1);
const door = new THREE.Mesh(box, door1Mesh);
door.position.set(-holeX / 2, (pointY / 3) * 2, floorProps[0] / 2);
//门的外壳 让门旋转围绕右侧轴 就是一个透明的空间内 宽度是原来的两倍中心轴自然就变成了原来的一侧
const doorWrap = new THREE.Object3D();
doorWrap.position.set(0, (pointY / 3) * 2, floorProps[0] / 2);
door.position.set(-holeX / 2, 0, 0);
doorWrap.add(door);
if (openDoor) {
doorWrap.userData.open = true;
doorWrap.rotateY(Math.PI);
}
//不重复添加door 加入filter 在前面加入让 door的id指向容器
elements.push({ element: door, type: "door", filter: true });
elements.push({ element: doorWrap, type: "door" });
//房盖
const coverHeight = 2;
const cover = new THREE.Mesh(
new THREE.CylinderBufferGeometry(
coverHeight,
coverHeight,
floorProps[1] + coverHeight,
3
),
material
);
cover.position.y = sideProps[1] + coverHeight / 2;
cover.scale.x = floorProps[0] / 3;
cover.rotateX(-halfPI);
cover.rotateZ(halfPI);
elements.push({ element: cover, type: "cover" });
//生成信息
for (const { element, type } of elements) {
house[type] = element.id;
}
//每个元素记录所有相关信息 然后添加到group中
for (const { element, filter } of elements) {
element.userData.type = "house";
element.userData.house = house;
element.castShadow = true;
if (!filter) group.add(element);
}
return group;
}
点击监听
//点击监听
canvas.addEventListener("click", event => {
event.preventDefault();
const intersects = getIntersects(event);
// 获取选中最近的 Mesh 对象
if (intersects.length && intersects[0].object instanceof THREE.Mesh) {
const selectObject = intersects[0].object;
if (selectObject.userData.type === "house") {
handleHouseClick(selectObject.userData.house);
}
// changeMaterial(selectObject);
}
});
function getIntersects(event: MouseEvent) {
// 声明 raycaster 和 mouse 变量
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
// 通过鼠标点击位置,计算出 raycaster 所需点的位置,以屏幕为中心点,范围 -1 到 1
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
//通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
raycaster.setFromCamera(mouse, camera);
// 获取与射线相交的对象数组,其中的元素按照距离排序,越近的越靠前
var intersects = raycaster.intersectObjects(scene.children);
//返回选中的对象
return intersects;
}
function handleHouseClick(house: any) {
const mash = scene.getObjectByProperty("id", house.door);
if (mash) {
console.log("door:", mash);
let c = 0;
const speed = 7;
let angle = Math.PI / speed; //默认关门状态
if (mash.userData.open) {
angle *= -1;
}
const animate = () => {
requestAnimationFrame(() => {
if (c !== speed) {
mash.rotateY(angle);
c++;
requestAnimationFrame(animate);
}
});
};
animate();
mash.userData.open = angle > 0;
}
}
old
建房子
for (let i = 0; i < 5; i++) {
const w = Math.random() * 10 + 4;
const h = House(w, w * 2);
h.position.set(Math.random() * 100 - 50, 0, Math.random() * 100 - 50);
h.rotateY(Math.random());
scene.add(h);
}
function House(width: number = 5, length: number = 10) {
/*
记录该实例的组成元素 将每个元素的id和类型存入每个实例的自定义输入
可以在用户点击任意属于该实例组成部分时控制其他部分
例如:点击房盖 控制房门打开关闭
*/
const house: any = {};
const elements = [];
const group = new THREE.Group();
const material = new THREE.MeshLambertMaterial({
map: texture1,
side: THREE.DoubleSide,
});
texture2.repeat.set(2, 2);
const material2 = new THREE.MeshLambertMaterial({
map: texture2,
side: THREE.DoubleSide,
});
const door1Mesh = new THREE.MeshLambertMaterial({
map: door1Texture,
side: THREE.DoubleSide,
});
//控制房子大小主要数据
const floorProps = [width, length]; //width length
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(...floorProps),
material
);
floor.rotateX(halfPI);
floor.rotateZ(halfPI);
elements.push({ element: floor, type: "floor" });
const sideProps = [floorProps[0], 4]; //width,height
const leftSide = new THREE.Mesh(
new THREE.PlaneGeometry(...sideProps),
material
);
leftSide.position.set(-floorProps[1] / 2, sideProps[1] / 2, 0);
leftSide.rotateY(halfPI);
const rightSide = new THREE.Mesh(
new THREE.PlaneGeometry(...sideProps),
material
);
rightSide.position.set(floorProps[1] / 2, sideProps[1] / 2, 0);
rightSide.rotateY(halfPI);
elements.push(
{ element: leftSide, type: "leftSide" },
{ element: rightSide, type: "rightSide" }
);
const back = new THREE.Mesh(
new THREE.PlaneGeometry(floorProps[1], sideProps[1]),
material
);
back.position.set(0, sideProps[1] / 2, -floorProps[0] / 2);
elements.push({ element: back, type: "back" });
//前面墙
const heartShape = new THREE.Shape();
const pointX = floorProps[1] / 2;
const pointY = sideProps[1] / 2;
heartShape.moveTo(-pointX, -pointY);
heartShape.lineTo(-pointX, pointY);
heartShape.lineTo(pointX, pointY);
heartShape.lineTo(pointX, -pointY);
heartShape.lineTo(-pointX, -pointY);
//开一个口 留门
const holeX = pointX / 3;
const holeY = pointY / 3;
const windowHole = new THREE.Path();
windowHole.moveTo(-holeX, holeY);
windowHole.lineTo(0, holeY);
windowHole.lineTo(0, -pointY);
windowHole.lineTo(-holeX, -pointY);
windowHole.lineTo(-holeX, holeY);
heartShape.holes.push(windowHole);
const frontGeometry = new THREE.ShapeGeometry(heartShape);
const front = new THREE.Mesh(frontGeometry, material2);
front.position.set(0, sideProps[1] / 2, floorProps[0] / 2);
elements.push({ element: front, type: "front" });
//门
const box = new THREE.BoxGeometry(holeX, pointY + pointY / 3, 0.1);
const door = new THREE.Mesh(box, door1Mesh);
door.position.set(-holeX / 2, (pointY / 3) * 2, floorProps[0] / 2);
elements.push({ element: door, type: "door" });
//房盖
const coverHeight = 2;
const cover = new THREE.Mesh(
new THREE.CylinderBufferGeometry(
coverHeight,
coverHeight,
floorProps[1] + coverHeight,
3
),
material
);
cover.position.y = sideProps[1] + coverHeight / 2;
cover.scale.x = floorProps[0] / 3;
cover.rotateX(-halfPI);
cover.rotateZ(halfPI);
elements.push({ element: cover, type: "cover" });
//生成信息
for (const { element, type } of elements) {
house[type] = element.id;
}
//每个元素记录所有相关信息 然后添加到group中
for (const { element } of elements) {
element.userData = { type: "house", house };
group.add(element);
}
return group;
}
点击监听
//点击监听
canvas.addEventListener("click", event => {
event.preventDefault();
const intersects = getIntersects(event);
// 获取选中最近的 Mesh 对象
if (intersects.length && intersects[0].object instanceof THREE.Mesh) {
const selectObject = intersects[0].object;
if (selectObject.userData.type === "house") {
handleHouseClick(selectObject.userData.house);
}
// changeMaterial(selectObject);
}
});
function getIntersects(event: MouseEvent) {
// 声明 raycaster 和 mouse 变量
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
// 通过鼠标点击位置,计算出 raycaster 所需点的位置,以屏幕为中心点,范围 -1 到 1
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
//通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
raycaster.setFromCamera(mouse, camera);
// 获取与射线相交的对象数组,其中的元素按照距离排序,越近的越靠前
var intersects = raycaster.intersectObjects(scene.children);
//返回选中的对象
return intersects;
}
function handleHouseClick(house: any) {
const mash = scene.getObjectByProperty("id", house.door);
if (mash) {
console.log("door:", mash);
mash.position.x *= -1;
}
}