Three.js盖房子 点击开关门

Source

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;
      }
    }