It sure does work in my game, I run nullengine in the server with havok wasm using bun. I also have a dynamic system to create physics objects, though I just basically use box collider for now.
I have a small wrapper class in my game to communicate between ecs and babylon:
import {
HavokPlugin,
InstancedMesh,
Mesh,
PhysicsAggregate,
PhysicsAggregateParameters,
PhysicsShapeBox,
PhysicsShapeCapsule,
PhysicsShapeType,
Scene,
TransformNode,
Vector3,
} from '@babylonjs/core'
import HavokPhysics from '@babylonjs/havok'
export class PhysicsManager {
physicsObjects: Map<number, PhysicsAggregate>
groundAggregates: PhysicsAggregate[] = []
scene: Scene | undefined
constructor() {
this.physicsObjects = new Map<number, PhysicsAggregate>()
}
async setup(scene: Scene, wasmBinary?: ArrayBuffer) {
try {
const havokInstance = await HavokPhysics(wasmBinary ? { wasmBinary } : undefined)
this.scene = scene
scene.enablePhysics(new Vector3(0, -9.81, 0), new HavokPlugin(true, havokInstance))
scene.getPhysicsEngine()?.setTimeStep(1 / 30)
console.log('physics-manager: physics are enabled', scene.physicsEnabled)
console.log('physics-manager: physics havok instance available', !!havokInstance)
} catch (error) {
console.error('physics-manager: Failed to load Havok Physics:', error)
throw error
}
}
addPhysicsObject(
id: number,
node: TransformNode,
shape: PhysicsShapeCapsule | PhysicsShapeBox,
options?: PhysicsAggregateParameters
) {
const physicsAggregate = new PhysicsAggregate(node, shape, options)
this.physicsObjects.set(id, physicsAggregate)
return physicsAggregate
}
addImpulse(id: number, force: Vector3) {
const physicsObject = this.getPhysicsObject(id)
if (physicsObject) {
const impulseOrigin = physicsObject.transformNode.position
physicsObject.body.applyImpulse(force, impulseOrigin)
}
}
getPhysicsObject(id: number) {
return this.physicsObjects.get(id)
}
removePhysicsObject(id: number) {
const physicsAggregate = this.getPhysicsObject(id)
if (!physicsAggregate) {
throw new Error('Physics object not found')
}
physicsAggregate.dispose()
}
createGroundAggregates(meshes: (Mesh | InstancedMesh)[]) {
if (this.groundAggregates.length > 0) {
this.groundAggregates.forEach((groundAggregate) => groundAggregate.dispose())
}
this.groundAggregates = meshes.map(
(mesh) =>
new PhysicsAggregate(
mesh,
PhysicsShapeType.MESH,
{ mass: 0, friction: 0.2, restitution: 0.3 },
this.scene
)
)
return this.groundAggregates
}
}
I create a physics object like:
world.physicsManager.addPhysicsObject(
entity,
meshPackage.mesh,
new PhysicsShapeBox(
new Vector3(0, meshPackage.meshTopPoint.position.y / 2, 0),
new Quaternion(0, 0, 0, 1),
meshPackage.meshExtent,
world.scene
),
{ mass: 1 }
)