我的 Three.js 子组中有五个元素,但我无法访问网格。如何访问 three.js 组中的网格?

There are five elements in my Three.js Group children but I can't access the meshes. How to access the meshes in a three.js group?

我试图访问这个数组中的元素,但由于某种原因它说“未定义”并返回长度为 0,但显然长度为 5。上图是这段代码的结果.

console.log(scene.children[1].children);

下面这段代码的结果是0

console.log(scene.children[1].children.length);

下面这段代码的结果是undefined

console.log(scene.children[1].children[0]);

如果对上下文有帮助,这是以前的路径:

console.log(scene);

console.log(scene.children[1]);

根据评论:这里是完整代码:

import "./styles.css";
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import gsap from "gsap";

import blueHouseBaked from "./static/Bluehouse.png";
import blueHouse from "./static/Bluehouse.glb";
import greenHouseBaked from "./static/Greenhouse.png";
import greenHouse from "./static/Greenhouse.glb";
import redHouseBaked from "./static/Redhouse.png";
import redHouse from "./static/Redhouse.glb";
import purpleHouseBaked from "./static/Purplehouse.png";
import purpleHouse from "./static/Purplehouse.glb";
import groundBaked from "./static/Ground.png";
import ground from "./static/Ground.glb";

import "regenerator-runtime/runtime";

const canvas = document.querySelector("canvas.experience");

// Scene
const scene = new THREE.Scene();

/**
 * Sizes
 */
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight,
};

window.addEventListener("resize", () => {
    // Update sizes
    sizes.width = window.innerWidth;
    sizes.height = window.innerHeight;

    // Update camera
    camera.aspect = sizes.width / sizes.height;
    camera.updateProjectionMatrix();

    // Update renderer
    renderer.setSize(sizes.width, sizes.height);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});

/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(
    75,
    sizes.width / sizes.height,
    0.1,
    100
);
camera.position.x = 0;
camera.position.y = 17;
camera.position.z = 22;
camera.rotation.x = -Math.PI / 8;

scene.add(camera);

//Controls -------------------
//Three.js vector lerping
let cameraTargetPosition = new THREE.Vector3(0, 17, 22);

window.addEventListener("mousedown", () => {
    window.addEventListener("mousemove", updateCamera);
});

window.addEventListener("mouseup", () => {
    window.removeEventListener("mousemove", updateCamera);
});

// Controls
function updateCamera(ev) {
    ev.preventDefault();
    if (ev.movementX > 0) {
        cameraTargetPosition.x -= ev.movementX * 0.015;
    } else {
        cameraTargetPosition.x -= ev.movementX * 0.015;
    }
    if (ev.movementY > 0) {
        cameraTargetPosition.z -= ev.movementY * 0.015;
    } else {
        cameraTargetPosition.z -= ev.movementY * 0.015;
    }
}

/**
 * Everything load
 */
let group = new THREE.Group();
async function loadModels(model, texture) {
    const gltfLoader = new GLTFLoader();
    const textureLoader = new THREE.TextureLoader();

    const mesh = await gltfLoader.loadAsync(model);

    const bakedTexture = textureLoader.load(texture);
    const bakedMaterial = new THREE.MeshBasicMaterial({
        map: bakedTexture,
    });

    bakedTexture.flipY = false;
    bakedTexture.encoding = THREE.sRGBEncoding;
    mesh.scene.children[0].material = bakedMaterial;
    group.add(mesh.scene.children[0]);
}

loadModels(blueHouse, blueHouseBaked);
loadModels(greenHouse, greenHouseBaked);
loadModels(redHouse, redHouseBaked);
loadModels(purpleHouse, purpleHouseBaked);
loadModels(ground, groundBaked);

scene.add(group);

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias: true,
});
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

/**
 * Animate
 */
const clock = new THREE.Clock();
let lastElapsedTime = 0;

// Ray Casting stuff
let raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
let currentIntersect = null;

window.addEventListener("mousemove", (event) => {
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
});

console.log(scene.children[1].children[0]);


const tick = () => {
    camera.position.lerp(cameraTargetPosition, 0.1);
    const elapsedTime = clock.getElapsedTime();
    const deltaTime = elapsedTime - lastElapsedTime;
    lastElapsedTime = elapsedTime;

    // Raycaster stuff
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects([scene.children[1].children]);
    if (intersects.length) {
        if (!currentIntersect) {
            console.log("mouse enter");
        }
        currentIntersect = intersects[0];
    } else {
        if (currentIntersect) {
            console.log("mouse leave");
        }
        currentIntersect = null;
    }
    // Render
    renderer.render(scene, camera);

    // Call tick again on the next frame
    window.requestAnimationFrame(tick);
};

tick();

所以我找到了解决办法,但我仍然不知道为什么根本问题仍然存在。如果我把 console.log(scene.children[1].children[0]); 放在勾号的里面它输出这个:

undefined
mesh
mesh
mesh

所以第一个循环是未定义的。因此,解决方案只是检查它是否未定义,现在我没有收到任何错误并且一切正常:

// Raycaster stuff
raycaster.setFromCamera(mouse, camera);
if (
    typeof scene.children[1].children[0] !== "undefined" &&
    typeof scene.children[1].children[1] !== "undefined" &&
    typeof scene.children[1].children[2] !== "undefined" &&
    typeof scene.children[1].children[3] !== "undefined"
) {
    const intersects = raycaster.intersectObjects([
        scene.children[1].children[0],
        scene.children[1].children[1],
        scene.children[1].children[2],
        scene.children[1].children[3],
    ]);
    if (intersects.length) {
        if (!currentIntersect) {
            console.log("mouse enter");
        }
        currentIntersect = intersects[0];
    } else {
        if (currentIntersect) {
            console.log("mouse leave");
        }
        currentIntersect = null;
    }
}

但是,我还是不明白为什么它在前几个循环中是未定义的。

您遇到的问题是由异步代码引起的。 loadModels 函数异步加载模型,这意味着其余代码先完成,然后异步方法完成其所有调用。

这是一个关于事件循环及其工作原理的精彩视频,可以更好地理解为什么会发生这种情况 - https://www.youtube.com/watch?v=8aGhZQkoFbQ

加载 GLTF 模型也可能需要一些时间,具体取决于它们的大小,因此它们可能需要几毫秒才能在组中可用。

您可以尝试记录每个报价,因此将您的报价方法更改为 -

const tick = () => {
    camera.position.lerp(cameraTargetPosition, 0.1);
    const elapsedTime = clock.getElapsedTime();
    const deltaTime = elapsedTime - lastElapsedTime;
    lastElapsedTime = elapsedTime;

    // Raycaster stuff
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects([scene.children[1].children]);
    if (intersects.length) {
        if (!currentIntersect) {
            console.log("mouse enter");
        }
        currentIntersect = intersects[0];
    } else {
        if (currentIntersect) {
            console.log("mouse leave");
        }
        currentIntersect = null;
    }
    // Render
    renderer.render(scene, camera);

    console.log(scene.children[0].children[1])

    // Call tick again on the next frame
    window.requestAnimationFrame(tick);
};

然而,当我们使用 threejs 时,将场景分配到全局 window 以便于在 chrome 中访问要容易得多。

创建场景后只需在代码中添加以下几行

window.scene = scene

import "./styles.css";
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import gsap from "gsap";

import blueHouseBaked from "./static/Bluehouse.png";
import blueHouse from "./static/Bluehouse.glb";
import greenHouseBaked from "./static/Greenhouse.png";
import greenHouse from "./static/Greenhouse.glb";
import redHouseBaked from "./static/Redhouse.png";
import redHouse from "./static/Redhouse.glb";
import purpleHouseBaked from "./static/Purplehouse.png";
import purpleHouse from "./static/Purplehouse.glb";
import groundBaked from "./static/Ground.png";
import ground from "./static/Ground.glb";

import "regenerator-runtime/runtime";

const canvas = document.querySelector("canvas.experience");

// Scene
const scene = new THREE.Scene();

window.scene = scene

.......

一旦加载了 GLTF 模型,您就可以从 chrome 控制台访问该组,因为现在可以从开发工具访问场景。你可以在那里写场景然后你就可以访问它,或者你可以写scene.children[0].children[1]然后你会看到你的组。

编辑:这只是为了调试。