Server side projectile collision detection

I am currently trying to implement a server authoritative tank game and i have issues with server side hit detection

in the code below i register an action with action manager so when a projectile intersects with one of the tanks from my tanks dictionary it should (for now) print “hit” to console
but i never get the hit in console but i know the projectile should hit since i run the exact same (same code and also babylon with cannon) physics simulation on the client and there the projectile goes through the other tank

if you had a similar issue or have any ideas on how i can solve this please help

import {tanks} from "../MyRoom";

export class Projectile {
    constructor(scene, position, direction, velocity, damage) {
        this.scene = scene;
        this.velocity = velocity;

        // Create a box instead of a sphere and set its size to create a long rectangle
        this.mesh = BABYLON.MeshBuilder.CreateBox("projectile", {width: 0.5, height: 0.5, depth: 8}, this.scene);
        this.mesh.position = position;

        // Create a yellow material and assign it to the box
        var material = new BABYLON.StandardMaterial("mat", this.scene);
        material.diffuseColor = new BABYLON.Color3(1, 1, 0); // RGB values of yellow
        this.mesh.material = material;

        // Align the box with the direction vector
        var forward = new BABYLON.Vector3(0,0,1);
        var directionToLookAt = direction;
        var angle = Math.acos(BABYLON.Vector3.Dot(forward, directionToLookAt));

        // Correct the angle
        if (directionToLookAt.x < 0) angle *= -1;

        this.mesh.rotation.y = angle;

        // Physics settings
        this.mesh.physicsImpostor = new BABYLON.PhysicsImpostor(
            this.mesh,
            BABYLON.PhysicsImpostor.BoxImpostor,
            { mass: 1, restitution: 0.9 },
            this.scene
        );

        // Apply the impulse in the given direction
        this.mesh.physicsImpostor.applyImpulse(
            direction.scale(this.velocity),
            this.mesh.getAbsolutePosition()
        );


        this.mesh.actionManager = new BABYLON.ActionManager(this.scene);

        for (let tankId in tanks) {
            let tank = tanks[tankId];
            if(tank){
                [tank.hullMesh, tank.turretMesh].forEach(mesh => {
                    this.mesh.actionManager.registerAction(
                        new BABYLON.ExecuteCodeAction(
                            {
                                trigger: BABYLON.ActionManager.OnIntersectionEnterTrigger,
                                parameter: mesh
                            },
                            () => {
                                console.log("hit")
                                this.destroy();
                            }
                        )
                    );
                });
            }
        }



        setTimeout(() => {
            console.log("destroy")
            this.destroy();
        }, 1000);


    }

    destroy() {
        // Remove the mesh from the scene
        this.mesh.dispose();
    }
}
1 Like

Hello and welcome to the Babylon forum!

It’s hard to know what’s going on without more of the server-side code to look at. Can you show more of it? Maybe point us to a working git repository we can build and test with?

I think you need to use deterministic locksteps to be sure that your server and local code generate exactly the same thing:

1 Like

this is the part of server side code where i receive the fire message

    this.onMessage("fire", (client, data) => {
      tanks[client.sessionId].fireProjectile(BABYLON.Vector3.FromArray(data.muzzlePosition), BABYLON.Vector3.FromArray(data.cannonForwardGlobal));
    })

this is my simulation interval and onUpdate function
i only handle movement in onupdate function but maybe this will be informative

    this.setSimulationInterval(() => this.onUpdate(), 16);

  onUpdate () {
    // update physics engine
    scene.getPhysicsEngine().getPhysicsPlugin().world.step(1/24);
    for (let key in tanks){
      let tank = tanks[key];
      if(tank){
        // normalize the quaternion
        let hullRotation = tank.getState().hullRotation;
        hullRotation.normalize();
        let turretRotation = tank.getState().turretRotation;
        turretRotation.normalize();
        this.broadcast("state_update", {
          id:key,
          position: tank.getState().position,
          hullRotation: hullRotation.asArray(),
          turretRotation: turretRotation.asArray()
        })
      }
    }
  }

and here is my fireprojectile method from the tank class

    fireProjectile(muzzlePosition, vector3) {
        if(!this.isAlive){
            return null;
        }

        const currentTime = new Date().getTime();
        const timeSinceLastFired = currentTime - this.turret.lastFiredTime;
        if (timeSinceLastFired < this.turret.reloadTime * 1000) {
            return null;  // Not enough time has passed since the last shot
        }

        // If the tank has finished reloading, fire a projectile and update lastFiredTime
        // Transform the local forward vector to the global space
        let cannonForwardGlobal = vector3

        // Create a new projectile
        let projectile;
        projectile = new Projectile(this.scene, muzzlePosition, cannonForwardGlobal, this.projectileSpeed, this.projectileDamage);

        this.turret.lastFiredTime = currentTime;  // Update the time the turret last fired


        return projectile;
    }

Hi literallyme and welcome to the forums!

There are a couple potential things I noticed with your code that you could try examining to see if it fixes your issue.

First, in the function that is ran when the OnIntersectionTrigger Code Action fires, you appear to be destroying the mesh as part of the callback. This could cause problem, so I would wrap the destroy call in a setImmediate or setTimeout with 0 delay. Similar recco for the other call to destroy since it is cleaning itself up as part of the loop - you can do that from your main update though alternately.

Second, I like how you are manually running the physics simulation step by step and deterministically in your onUpdate handler, but instead of doing that have you looked at enabling deterministic lockstep (as mentioned earlier) and registering your update logic to run on the appropriate physics step observer? Could help simplify your code and ensure that client and server are doing the same things, because remember on the server you’re not getting some render-related events…

HTH!

3 Likes