I’m currently building an auto-shooter / zombie game on the web ( WIP )
And I came across a few perfs issues regarding the kinematic controller / setNextKinematicPosition
Context :

I’ve got zombies / which are capsuled kinematic entities. They spawn randomly and move across the terrain to attack us.
I’m using a single kinematic player controller for all the zombies and applying kinematic forces so each of them does not pass through actual collided objects + does not pass through other zombies
// set the velocity of the zombie toward the actual direction, and applying gravity in Y position
tempVec.set ( -v31.x * dt * this.targets[i].speed, -9.81 * dt * 2, - v31.z * dt * this.targets[i].speed )
// this is the part that calculate the new position of the zombie
this.characterController.computeColliderMovement(
this.targets[i].collider._collider, // The collider we would like to move.
tempVec, // The movement we would like to apply if there wasn’t any obstacle.
null,
null,
( collider )=>{
// this is filter from the collision solver :
// if the colliding object is a sensor => no collision
const isSensor = collider.isSensor()
return isSensor == false
}
)
let correctedMovement = this.characterController.computedMovement();
const rpos = this.targets[i].collider.rigidBody.translation()
this.targets[i].position.set(
rpos.x + correctedMovement.x,
rpos.y + correctedMovement.y,
rpos.z + correctedMovement.z
)
this.targets[i].collider.rigidBody.setNextKinematicTranslation(this.targets[i].position)
Problem :
Getting 20 zombies into kinematic forces leads a big drop of performances. I analysed the situation here and did a few check and realised that each zombie performs 10 to a 100 times a collision in the filter by logging the zombieCollisions value in the filter :

let zombieCollisions = 0
this.characterController.computeColliderMovement(
this.targets[i].collider._collider, // The collider we would like to move.
tempVec, // The movement we would like to apply if there wasn’t any obstacle.
null,
null,
( collider )=>{
// this is filter from the collision solver :
// if the colliding object is a sensor => no collision
const isSensor = collider.isSensor()
console.log( zombieCollisions )
return isSensor == false
}
)
Solution ?
Well, I’ve tried so many things, my best bet was to skip the collision test when there are more than a X value using but it ultimately leads to weird behavior and break the simulation kind of :
let zombieCollisions = 0
this.characterController.computeColliderMovement(
this.targets[i].collider._collider, // The collider we would like to move.
tempVec, // The movement we would like to apply if there wasn’t any obstacle.
null,
null,
( collider )=>{
// this is filter from the collision solver :
// if the colliding object is a sensor => no collision
// if the current zombie colliders with other zombie more than 6 times => stop colliding
const isSensor = collider.isSensor()
const c = (collider.parent().userData).mesh.componentType
if( c == 'avatar'){
zombieCollisions++
}
return isSensor == false && (c != 'avatar' || zombieCollisions < 6 )
}
)
Questions :
Is having from 10 to a 100 collision tests per zombie using a kinematic control a standard behavior ?
Is there another way than using the kinematic controller to get the zombie to collide between each other ?
Has there been some benchmarks and tests that I’m not aware of about a kind of “crowd simulation” using rapier.js ? I’ve been crawling the web to get a solution on this but no chances yet.
Is there a way to spread the collision tests across multiples frame and somehow accepts than sometimes the capsules are merging / passing through for a few frames, without breaking the physics / the perfs ?
It is a long a long post I’m sorry.
Thank you very much for your time