I've created a simple scene using cannon.js and three.js with a few meshes, but for some reason, when I set the mass of a mesh to any positive number, the position is set to Nan, and the mass is set to undefined.
I've tried to add an if statement to world.step(delta) so it only runs if delta > 0, but the objects appear only sometimes, and they don't have gravity. This is my code:
import * as THREE from 'three'
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; // Import the GLTFLoader
const scene = new THREE.Scene()
//scene.add(new THREE.AxesHelper(5))
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
)
camera.position.set(0, 0, 0)
const cameraDirection = new THREE.Vector3();
window.onload = function() {
renderer.domElement.requestPointerLock();
renderer.domElement.onclick = function() {
renderer.domElement.requestPointerLock();
}
};
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
})
renderer.shadowMap.enabled = true
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
const controls = new PointerLockControls(camera, renderer.domElement)
controls.sensitivity = 0.1
const world = new CANNON.World();
world.gravity.set(0, -5, 0);
const normalMaterial = new THREE.MeshNormalMaterial()
const phongMaterial = new THREE.MeshPhongMaterial()
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1)
const cubeMesh = new THREE.Mesh(cubeGeometry, normalMaterial)
cubeMesh.position.x = -4
cubeMesh.position.y = 3
cubeMesh.castShadow = true
scene.add(cubeMesh)
const cubeShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5))
const cubeBody = new CANNON.Body({ mass: 1 })
cubeBody.addShape(cubeShape)
cubeBody.position.x = cubeMesh.position.x
cubeBody.position.y = cubeMesh.position.y
cubeBody.position.z = cubeMesh.position.z
world.addBody(cubeBody)
const sphereGeometry = new THREE.SphereGeometry()
const sphereMesh = new THREE.Mesh(sphereGeometry, normalMaterial)
sphereMesh.position.x = -2
sphereMesh.position.y = 3
sphereMesh.castShadow = true
scene.add(sphereMesh)
const sphereShape = new CANNON.Sphere(1)
const sphereBody = new CANNON.Body({ mass: 1 })
sphereBody.addShape(sphereShape)
sphereBody.position.x = sphereMesh.position.x
sphereBody.position.y = sphereMesh.position.y
sphereBody.position.z = sphereMesh.position.z
world.addBody(sphereBody)
const floorGeometry = new THREE.BoxGeometry(10, 1, 10)
const floorMesh = new THREE.Mesh(floorGeometry, normalMaterial)
floorMesh.position.x = -4
floorMesh.position.y = -2
floorMesh.castShadow = true
scene.add(floorMesh)
const floorShape = new CANNON.Box(new CANNON.Vec3(5, 0.5, 5))
const floorBody = new CANNON.Body({ mass: 0 })
floorBody.addShape(floorShape)
floorBody.position.x = floorMesh.position.x
floorBody.position.y = floorMesh.position.y
floorBody.position.z = floorMesh.position.z
world.addBody(floorBody)
const wall1Geometry = new THREE.BoxGeometry(10, 10, 1)
const wall1Mesh = new THREE.Mesh(wall1Geometry, normalMaterial)
wall1Mesh.position.x = -4
wall1Mesh.position.y = 3.5
wall1Mesh.position.z = 5.5
wall1Mesh.castShadow = true
scene.add(wall1Mesh)
const wall1Shape = new CANNON.Box(new CANNON.Vec3(5, 5, 0.5))
const wall1Body = new CANNON.Body({ mass: 0 })
wall1Body.addShape(wall1Shape)
wall1Body.position.x = wall1Mesh.position.x
wall1Body.position.y = wall1Mesh.position.y
wall1Body.position.z = wall1Mesh.position.z
world.addBody(wall1Body)
const wall2Geometry = new THREE.BoxGeometry(10, 10, 1)
const wall2Mesh = new THREE.Mesh(wall2Geometry, normalMaterial)
wall2Mesh.position.x = -4
wall2Mesh.position.y = 3.5
wall2Mesh.position.z = -5.5
wall2Mesh.castShadow = true
scene.add(wall2Mesh)
const wall2Shape = new CANNON.Box(new CANNON.Vec3(5, 5, 0.5))
const wall2Body = new CANNON.Body({ mass: 0 })
wall2Body.addShape(wall2Shape)
wall2Body.position.x = wall2Mesh.position.x
wall2Body.position.y = wall2Mesh.position.y
wall2Body.position.z = wall2Mesh.position.z
world.addBody(wall2Body)
const wall3Geometry = new THREE.BoxGeometry(1, 10, 10)
const wall3Mesh = new THREE.Mesh(wall3Geometry, normalMaterial)
wall3Mesh.position.x = 1.5
wall3Mesh.position.y = 3.5
wall3Mesh.castShadow = true
scene.add(wall3Mesh)
const wall3Shape = new CANNON.Box(new CANNON.Vec3(5, 5, 0.5))
const wall3Body = new CANNON.Body({ mass: 0 })
wall3Body.addShape(wall3Shape)
wall3Body.position.x = wall3Mesh.position.x
wall3Body.position.y = wall3Mesh.position.y
wall3Body.position.z = wall3Mesh.position.z
world.addBody(wall3Body)
const wall4Geometry = new THREE.BoxGeometry(1, 10, 10)
const wall4Mesh = new THREE.Mesh(wall4Geometry, normalMaterial)
wall4Mesh.position.x = -9.5
wall4Mesh.position.y = 3.5
wall4Mesh.castShadow = true
scene.add(wall4Mesh)
const wall4Shape = new CANNON.Box(new CANNON.Vec3(5, 5, 0.5))
const wall4Body = new CANNON.Body({ mass: 0 })
wall4Body.addShape(wall4Shape)
wall4Body.position.x = wall4Mesh.position.x
wall4Body.position.y = wall4Mesh.position.y
wall4Body.position.z = wall4Mesh.position.z
world.addBody(wall4Body)
const loader = new GLTFLoader();
loader.load('untitled.glb', (gltf) => {
const levelModel = gltf.scene;
const scaleFactor = 0.1; // Adjust this value as needed
levelModel.scale.set(scaleFactor, scaleFactor, scaleFactor);
levelModel.position.set(-5, -1.9, -6);
scene.add(levelModel);
});
const clock = new THREE.Clock()
let delta
class Box extends THREE.Mesh {
constructor({
width,
height,
depth,
color = '#00ff00',
velocity = {
x: 0,
y: 0,
z: 0
},
position = {
x: 0,
y: 0,
z: 0
},
zAcceleration = false
}) {
super(
new THREE.BoxGeometry(width, height, depth),
new THREE.MeshStandardMaterial({ color })
)
this.width = width
this.height = height
this.depth = depth
this.position.set(position.x, position.y, position.z)
this.right = this.position.x + this.width / 2
this.left = this.position.x - this.width / 2
this.bottom = this.position.y - this.height / 2
this.top = this.position.y + this.height / 2
this.front = this.position.z + this.depth / 2
this.back = this.position.z - this.depth / 2
this.velocity = velocity
this.gravity = -0.002
this.zAcceleration = zAcceleration
}
updateSides() {
this.right = this.position.x + this.width / 2
this.left = this.position.x - this.width / 2
this.bottom = this.position.y - this.height / 2
this.top = this.position.y + this.height / 2
this.front = this.position.z + this.depth / 2
this.back = this.position.z - this.depth / 2
camera.position.copy(cube.position)
}
update(ground) {
this.updateSides()
if (this.zAcceleration) this.velocity.z += 0.0003
this.position.x += this.velocity.x
this.position.z += this.velocity.z
this.applyGravity(ground)
}
applyGravity(ground) {
this.velocity.y += this.gravity
// this is where we hit the ground
if (
boxCollision({
box1: this,
box2: ground
})
) {
const friction = 0.5
this.velocity.y *= friction
this.velocity.x *= friction
//this.velocity.y = -this.velocity.y
} else this.position.y += this.velocity.y
}
}
function boxCollision({ box1, box2 }) {
const xCollision = box1.right >= box2.left && box1.left <= box2.right
const yCollision =
box1.bottom + box1.velocity.y <= box2.top && box1.top >= box2.bottom
const zCollision = box1.front >= box2.back && box1.back <= box2.front
return xCollision && yCollision && zCollision
}
const cube = new Box({
width: 1,
height: 1,
depth: 1,
velocity: {
x: 0,
y: -0.01,
z: 0
}
})
cube.castShadow = false
scene.add(cube)
const ground = new Box({
width: 10,
height: 0.5,
depth: 50,
color: '#0369a1',
position: {
x: 0,
y: -2,
z: 0
}
})
ground.receiveShadow = true
scene.add(ground)
const light = new THREE.DirectionalLight(0xffffff, 1)
light.position.y = 3
light.position.z = 1
light.castShadow = true
scene.add(light)
scene.add(new THREE.AmbientLight(0xffffff, 0.5))
camera.position.z = 5
const keys = {
a: {
pressed: false
},
d: {
pressed: false
},
s: {
pressed: false
},
w: {
pressed: false
}
}
window.addEventListener('keydown', (event) => {
switch (event.code) {
case 'KeyA':
keys.a.pressed = true
break
case 'KeyD':
keys.d.pressed = true
break
case 'KeyS':
keys.s.pressed = true
break
case 'KeyW':
keys.w.pressed = true
break
case 'Space':
cube.velocity.y = 0.08
break
}
})
window.addEventListener('keyup', (event) => {
switch (event.code) {
case 'KeyA':
keys.a.pressed = false
break
case 'KeyD':
keys.d.pressed = false
break
case 'KeyS':
keys.s.pressed = false
break
case 'KeyW':
keys.w.pressed = false
break
}
})
let frames = 0
function animate() {
const animationId = requestAnimationFrame(animate)
renderer.render(scene, camera)
delta = Math.min(clock.getDelta(), 0.1)
world.step(delta);
camera.getWorldDirection(cameraDirection);
// Copy coordinates from Cannon to Three.js
cubeMesh.position.set(cubeBody.position.x, cubeBody.position.y, cubeBody.position.z)
cubeMesh.quaternion.set(
cubeBody.quaternion.x,
cubeBody.quaternion.y,
cubeBody.quaternion.z,
cubeBody.quaternion.w
)
sphereMesh.position.set(sphereBody.position.x, sphereBody.position.y, sphereBody.position.z)
sphereMesh.quaternion.set(
sphereBody.quaternion.x,
sphereBody.quaternion.y,
sphereBody.quaternion.z,
sphereBody.quaternion.w
)
console.log(cubeMesh.position, cubeMesh.mass, sphereMesh.position, sphereMesh.mass)
// movement code
const movement = new THREE.Vector3();
// Check for key presses and update movement vector
if (keys.d.pressed) {
movement.x -= cameraDirection.z;
movement.z += cameraDirection.x;
}
if (keys.a.pressed) {
movement.x += cameraDirection.z;
movement.z -= cameraDirection.x;
}
if (keys.s.pressed) {
movement.x -= cameraDirection.x;
movement.z -= cameraDirection.z;
}
if (keys.w.pressed) {
movement.x += cameraDirection.x;
movement.z += cameraDirection.z;
}
const slideForce = 0.2; // Adjust this value to control the sliding force
if (keys.d.pressed) {
const force = new CANNON.Vec3(slideForce, 0, 0);
cubeBody.applyImpulse(force, cubeBody.position);
}
if (keys.a.pressed) {
const force = new CANNON.Vec3(-slideForce, 0, 0);
cubeBody.applyImpulse(force, cubeBody.position);
}
if (keys.s.pressed) {
const force = new CANNON.Vec3(0, 0, slideForce);
cubeBody.applyImpulse(force, cubeBody.position);
}
if (keys.w.pressed) {
const force = new CANNON.Vec3(0, 0, -slideForce);
cubeBody.applyImpulse(force, cubeBody.position);
}
// Normalize the movement vector if needed
if (movement.length() > 0) {
movement.normalize();
}
// Apply movement vector to the cube's velocity
cube.velocity.x = movement.x * 0.05;
cube.velocity.z = movement.z * 0.05;
cube.update(ground);
frames++
}
animate()
window.addEventListener('resize', () => {
const width = window.innerWidth
const height = window.innerHeight
camera.aspect = width / height
camera.updateProjectionMatrix()
renderer.setSize(width, height)
})