[SOLVED] Stopping Player Mesh rotation

chrome-capture-2024-3-2

Hey hey!

I’m working on this project that has a ArcRotate camera following a player, with the ability to look around and move with WASD keys and jump with space. Just like in a third person shooter. Physics and everything seem to work perfectly.

However as you can see the sphere starts spinning after collision. I have been trying to stop it with no success. Is there some simple way of doing this? ChatGPT is suggesting to use a Quartion but it doesn’t seem to work. Here is how I handle the playerphysics:

import { Ray, Mesh, Scene, Vector3, PhysicsImpostor, ArcRotateCamera } from "@babylonjs/core";

export class PlayerPhysics {
    private mesh: Mesh;
    private scene: Scene;
    private camera: ArcRotateCamera;
    private lastInputVector: Vector3 | null = null; // Add this line
    private mass: number;


    constructor(mesh: Mesh, scene: Scene, camera: ArcRotateCamera) {
        this.mesh = mesh;
        this.scene = scene;
        this.camera = camera;
        this.mass = mesh.physicsImpostor.mass; // Cache the mass if it doesn't change

    }

    public move(inputVector: Vector3) {
        if (!this.camera) {
            console.warn("Camera not set for PlayerPhysics instance.");
            return;
        }
    
        let forward = this.camera.getForwardRay().direction;
        forward.y = 0; // Maintain horizontal movement
        forward.normalize();
    
        const right = Vector3.Cross(Vector3.Up(), forward).normalize();
        const mass = this.mesh.physicsImpostor.mass; // Assuming the mass is set correctly on the physics impostor
        
        const additionalGravity = new Vector3(0, -9.81, 0); // This value represents the extra gravity force you want to apply
    
        let moveDirection = forward.scale(inputVector.z).add(right.scale(inputVector.x));
        let currentVelocity = this.mesh.physicsImpostor.getLinearVelocity();
        let verticalVelocity = currentVelocity ? currentVelocity.y : 0;
    
        if (this.lastInputVector && !inputVector.equalsWithEpsilon(this.lastInputVector, 0.1)) {
            this.mesh.physicsImpostor.setLinearVelocity(new Vector3(0, verticalVelocity, 0));
        } else {
            moveDirection = moveDirection.normalize().scale(15); // Adjust speed as needed
            this.mesh.physicsImpostor.setLinearVelocity(new Vector3(moveDirection.x, verticalVelocity, moveDirection.z));
        }
    
        this.lastInputVector = inputVector.clone();
        
    }

    public update() {
        // Calculate additional gravity force
        const additionalGravity = new Vector3(0, -15.81, 0); // This value represents the extra gravity force you want to apply
        const mass = this.mesh.physicsImpostor.mass; // Assuming the mass is set correctly on the physics impostor
    
        // Apply the additional gravity force to the player
        // Note: This force is applied in addition to the scene's global gravity
        this.mesh.physicsImpostor.applyForce(additionalGravity.scale(mass), this.mesh.getAbsolutePosition());
    }

    public jump() {
        // Simple ground check before jumping
        if (this.isOnGround()) {
            const jumpForce = new Vector3(0, 30, 0); // Adjust the Y value as needed for jump height
            this.mesh.physicsImpostor.applyImpulse(jumpForce, this.mesh.getAbsolutePosition());
        }
    }

   
private isOnGround(): boolean {
    // Define the start and end points of the ray
    const rayStart = this.mesh.position;
    const rayEnd = new Vector3(rayStart.x, rayStart.y - 1.1, rayStart.z); // 1.1 units below the player, adjust as needed

    // Create the ray
    const ray = new Ray(rayStart, rayEnd.subtract(rayStart), 1.1); // The third parameter is the length of the ray

    // Cast the ray and check for intersections
    const pickInfo = this.scene.pickWithRay(ray, (mesh) => {
        return mesh !== this.mesh && mesh.isPickable && mesh.isVisible; // Ignore the player's own mesh
    });

    // If the ray intersects a mesh, the player is considered to be on the ground
    return !!pickInfo.hit;
}
}    

And this is how I create the player:

// Import necessary Babylon.js classes and custom modules for player setup
import { ArcRotateCamera, Scene, MeshBuilder, Vector3, PhysicsImpostor, Mesh, Color3, StandardMaterial, Texture, Quaternion } from "@babylonjs/core";
import { InputControls } from "./inputControls"; // Manages player input
import { createCamera } from "./cameraSetup"; // Configures the player's camera
import { PlayerPhysics } from "./playerPhysics"; // Handles physics interactions for the player

// Defines the Player class
export class Player {
    private scene: Scene; // Reference to the Babylon.js scene
    private inputControls: InputControls; // Input controller for managing player actions
    private mesh: Mesh; // The player's mesh in the scene
    private playerPhysics: PlayerPhysics; // Physics logic specific to the player
    public camera: ArcRotateCamera; // The camera attached to the player, made public for external access

    // Constructor initializes the player within the provided scene and attaches a camera
    constructor(scene: Scene, canvas: HTMLCanvasElement) {
        this.scene = scene; // Store reference to the scene
        this.mesh = this.createPlayerMesh(); // Create the player's mesh
        this.camera = createCamera(scene, canvas, this.mesh); // Initialize the camera and attach it to the player
        this.inputControls = new InputControls(scene); // Setup input controls for the player
        this.playerPhysics = new PlayerPhysics(this.mesh, this.scene, this.camera); // Initialize player-specific physics handling
    }

    // Creates the player's mesh, setting up its appearance and physics properties
    private createPlayerMesh(): Mesh {
    // Create a sphere to represent the player
    const sphere = MeshBuilder.CreateSphere("player", {diameter: 2, segments: 32}, this.scene);
    sphere.position = new Vector3(0, 1, 0); // Adjust position as needed

    const material = new StandardMaterial("playerMaterial", this.scene);
    material.diffuseColor = new Color3(1, 1, 1); // Set to white to not affect the texture's colors

    // Apply the striped texture
    const texture = new Texture("path/to/your/stripedTexture.png", this.scene);
    material.diffuseTexture = texture;

    sphere.material = material;

    // Add physics to the sphere
    sphere.physicsImpostor = new PhysicsImpostor(sphere, PhysicsImpostor.SphereImpostor, { mass: 2, restitution: 0.5 }, this.scene);

    return sphere;
}

    // Returns the player's mesh. Useful for external classes that need to interact with the player
    public getMesh(): Mesh {
        return this.mesh;
    }

    // The main update loop for the player, called every frame to handle input and physics updates
    public update() {
        const inputVector = this.inputControls.getInputVector(); // Get the current input vector
        this.playerPhysics.move(inputVector); // Move the player based on input
        
        if (this.inputControls.isJumping()) { // Check if the jump action was triggered
            this.playerPhysics.jump(); // Make the player jump
        }
        
        this.playerPhysics.update(); // Perform any additional physics updates, such as applying custom gravity
        
    }
}

Any help on this would super appreciated :slight_smile:

cc @Cedric

I think this would be helpful : Havok, bodies without rotation, angular velocity - #8 by eoin

2 Likes

Updating this comment to say I solved my issue using @Cedric 's method. I’ve applied both the setAngluarVelocity to 0 and using Ammo Js to setAngularFactor to 0. It work’s great! Thanks for the help :slight_smile: