Созданные объекты проваливаются сквозь плоскость пола (CANNON.js + Three.js)

У меня есть такой код:

let scene = new THREE.Scene();
let redAsyk, yellowAsyk;
let bitka1, bitka2, bitka3;
let isRedAsykLoaded = false;
let isYellowAsykLoaded = false;
let camera, renderer, controls;
let redAsykBody, yellowAsykBody, bitka1Body, bitka2Body, bitka3Body, floorBody;
let redAsykShape, yellowAsykShape, bitka1Shape, bitka2Shape, bitka3Shape, floorShape;
let currentAsyk = null;
const world = new CANNON.World();

let isStoped = false;

let redAsykThrows = 0;
let yellowAsykThrows = 0;
const maxThrows = 3;


const modelsToLoad = [
    { name: 'red_asyk', objPath: 'models/red_asyk/red_asyk.obj', mtlPath: 'models/red_asyk/red_asyk.mtl', position: new THREE.Vector3(0, 0, 25) },
    { name: 'yellow_asyk', objPath: 'models/yellow_asyk/yellow_asyk.obj', mtlPath: 'models/yellow_asyk/yellow_asyk.mtl', position: new THREE.Vector3(0, 0, 20) },
    { name: 'bitka1', objPath: 'models/bitka/bitka.obj', mtlPath: 'models/bitka/bitka.mtl', position: new THREE.Vector3(10, 0, 0) },
    { name: 'bitka2', objPath: 'models/bitka/bitka.obj', mtlPath: 'models/bitka/bitka.mtl', position: new THREE.Vector3(0, 0, 0) },
    { name: 'bitka3', objPath: 'models/bitka/bitka.obj', mtlPath: 'models/bitka/bitka.mtl', position: new THREE.Vector3(-10, 0, 0) },
];

function init() {
    scene = new THREE.Scene();
    scene.fog = new THREE.FogExp2(0xb3e8ff, 0.003);
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.set(0, 12, 38);
    camera.lookAt(scene.position);
    renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    world.gravity.set(0, -9.82, 0);


    createLights();

    createSky();
    createFloor();
    addCircleTexture();

    loadModels(modelsToLoad);


    animate();
}

function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);

    world.step(1 / 60);

    if (currentAsyk) {
        currentAsyk.position.copy(currentAsyk.userData.physicsBody.position);
        currentAsyk.quaternion.copy(currentAsyk.userData.physicsBody.quaternion);
    }
    
}

function loadModels(models) {

    let loadedCount = 0;

    models.forEach(model => {
        let mtlLoader = new THREE.MTLLoader();
        mtlLoader.load(model.mtlPath, function(materials) {
            materials.preload();
            let objLoader = new THREE.OBJLoader();
            objLoader.setMaterials(materials);
            objLoader.load(model.objPath, function(object) {
                object.position.copy(model.position);
                
                if (model.name === 'red_asyk') {
                    redAsyk = object;
                    redAsyk.scale.set(0.05, 0.05, 0.05);
                    scene.add(redAsyk);
                    isRedAsykLoaded = true;

                    redAsykShape = new CANNON.Box(new CANNON.Vec3(1, 1, 1));
                    redAsykBody = new CANNON.Body({ mass: 1, redAsykShape });
                    world.addBody(redAsykBody);

                    redAsyk.userData.physicsBody = redAsykBody;
                    redAsykBody.threeObject = redAsyk;

                    currentAsyk = redAsyk;
                } else if (model.name === 'yellow_asyk') {
                    yellowAsyk = object;
                    yellowAsyk.scale.set(0.05, 0.05, 0.05);
                    yellowAsyk.visible = false;
                    scene.add(yellowAsyk);
                    isYellowAsykLoaded = true;

                    yellowAsykShape = new CANNON.Box(new CANNON.Vec3(1, 1, 1));
                    yellowAsykBody = new CANNON.Body({ mass: 1, yellowAsykShape });
                    world.addBody(yellowAsykBody);

                    yellowAsyk.userData.physicsBody = yellowAsykBody;
                    yellowAsykBody.threeObject = yellowAsyk;
                } else if (model.name === 'bitka1') {
                    bitka1 = object;
                    bitka1.scale.set(0.05, 0.05, 0.05);
                    scene.add(bitka1);

                    let bitka1Shape = new CANNON.Box(new CANNON.Vec3(1, 1, 1));
                    let bitka1Body = new CANNON.Body({ mass: 1, bitka1Shape });
                    world.addBody(bitka1Body);

                    bitka1.userData.physicsBody = bitka1Body;
                    bitka1Body.threeObject = bitka1;
                } else if (model.name === 'bitka2') {
                    bitka2 = object;
                    bitka2.scale.set(0.05, 0.05, 0.05);
                    scene.add(bitka2);

                    bitka2Shape = new CANNON.Box(new CANNON.Vec3(1, 1, 1));
                    bitka2Body = new CANNON.Body({ mass: 1, bitka2Shape });
                    world.addBody(bitka2Body);

                    bitka2.userData.physicsBody = bitka2Body;
                    bitka2Body.threeObject = bitka2;
                } else if (model.name === 'bitka3') {
                    bitka3 = object;
                    bitka3.scale.set(0.05, 0.05, 0.05);
                    scene.add(bitka3);

                    bitka3Shape = new CANNON.Box(new CANNON.Vec3(1, 1, 1));
                    bitka3Body = new CANNON.Body({ mass: 1, bitka3Shape });
                    world.addBody(bitka3Body);

                    bitka3.userData.physicsBody = bitka3Body;
                    bitka3Body.threeObject = bitka3;
                }
                loadedCount++;
                if (loadedCount === models.length) {
                    $('canvas').css('display', 'block');
                    $('.loading-screen').css('display', 'none');
                }
            });
        });
    });
}

function createLights() {
    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    directionalLight.position.set(0, 100, 50);
    scene.add(directionalLight);

    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
    scene.add(ambientLight);
}

function createSky() {
    let sphereGeometry = new THREE.SphereBufferGeometry(1000, 32, 32);
    
    let texture = new THREE.TextureLoader().load('images/sky.jpg');
    let skyMaterial = new THREE.MeshStandardMaterial({ map: texture, side: THREE.BackSide });
    
    let sky = new THREE.Mesh(sphereGeometry, skyMaterial);
    scene.add(sky);
}

function createFloor() {
    const textureLoader = new THREE.TextureLoader();
    const floorTexture = textureLoader.load('images/floor.jpg');
    floorTexture.wrapS = THREE.RepeatWrapping;
    floorTexture.wrapT = THREE.RepeatWrapping;
    floorTexture.repeat.set(25, 25);

    const floorMaterial = new THREE.MeshBasicMaterial({ map: floorTexture, side: THREE.DoubleSide });
    const floorGeometry = new THREE.PlaneGeometry(1000, 1000);

    const floor = new THREE.Mesh(floorGeometry, floorMaterial);
    floor.rotation.x = -Math.PI / 2;
    floor.position.y = 0;
    scene.add(floor);

    // Физическое тело пола в Cannon.js
    const floorShape = new CANNON.Plane();
    const floorBody = new CANNON.Body({
        mass: 0,
        shape: floorShape,
        position: new CANNON.Vec3(0, 0, 0),
        quaternion: new CANNON.Quaternion().setFromAxisAngle(new CANNON.Vec3(-1, 0, 0), Math.PI / 2)
    });

    world.addBody(floorBody);
}

function addCircleTexture() {
    let circleTexture = new THREE.TextureLoader().load('images/arena_background.png');
    let circleMaterial = new THREE.MeshBasicMaterial({ map: circleTexture, transparent: true });

    let circleGeometry = new THREE.PlaneGeometry(40, 40);

    let circle = new THREE.Mesh(circleGeometry, circleMaterial);
    circle.rotation.x = -Math.PI / 2;
    circle.position.set(0, 0, 0);
    scene.add(circle);
}

function resetAsykThrow() {
    const physicsBody = currentAsyk.userData.physicsBody;
    physicsBody.velocity.set(0, 0, 0);
    physicsBody.angularVelocity.set(0, 0, 0);
    physicsBody.position.set(0, 0, 0);
    currentAsyk.quaternion.set(0, 0, 0, 1);
    currentAsyk.position.set(0, 0, 0);
}

function switchAsyk() {
    if (currentAsyk === redAsyk) {
        redAsyk.visible = false;
        yellowAsyk.visible = true;
        currentAsyk = yellowAsyk;
    } else if (currentAsyk === yellowAsyk) {
        yellowAsyk.visible = false;
        redAsyk.visible = true;
        currentAsyk = redAsyk;
    }
    resetAsykThrow();
}

function initControls() {
    controls = new THREE.OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.25;
    controls.enableZoom = true;
}

window.addEventListener('keydown', function (event) {
    if (event.key === 'Enter') {
        switchAsyk();
    }
});

window.addEventListener('load', function () {
    init();
    initControls();
});
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- <link rel="stylesheet" href="src/css/main.css"> -->
    <script src="scripts/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/FBXLoader.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/MTLLoader.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/OBJLoader.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/libs/fflate.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/21.0.0/tween.umd.js" integrity="sha512-iPPs+A0ew4z+jybQ1r6HPj5b8zV8zaw6TpfGn/qqntA480gp6HDfUJGp1ni46LXrItwnTvYlg80h1NIkC4pf6g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.min.js" integrity="sha512-avLcnGxl5mqAX/wIKERdb1gFNkOLHh2W5JNCfJm5OugpEPBz7LNXJJ3BDjjwO00AxEY1MqdNjtEmiYhKC0ld7g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

    <title>Asyk 3D Game</title>

    <style>
        body {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        canvas {
            width: 100%;
            height: 100%;
            display: none;
        }

        .loading-screen {
            width: 100%;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            position: absolute;
            background: url(images/background.jpg);
            background-size: cover;
            background-position: 50% 50%;
        }
    </style>

</head>
<body>
    
    <div class="loading-screen">
        <h1>Загрузка...</h1>
    </div>

    <script src="main.js"></script>

</body>
</html>

Я создаю плоскость пола, на ней есть круг, небо, на котором будет происходить некоторые действия, подсветку, создаю объекты redAsyk, yellowAsyk, bitka1, bitka2 и bitka3. Почему-то эти 5 объектов проваливаются сквозь плоскость пола. Физические тела начинают падать, хотя вроде как я устанавливаю физику для пола. Уже много вариация создания пола перепробовал, но всё равно объекты продолжают падать.


Ответы (0 шт):