FPS sprint code

how would I add a running function to this with shift key?

var FirstPersonMovement = pc.createScript('firstpersonmovement');
FirstPersonMovement.attributes.add('camera', {
    type: 'entity',
    description: 'Optional, assign a camera entity, otherwise one is created'
});

FirstPersonMovement.attributes.add('power', {
    type: 'number',
    default: 2500,
    description: 'Adjusts the speed of player movement'
});

FirstPersonMovement.attributes.add('lookSpeed', {
    type: 'number',
    default: 0.25,
    description: 'Adjusts the sensitivity of looking'
});

// initialize code called once per entity
FirstPersonMovement.prototype.initialize = function() {
    this.force = new pc.Vec3();
    this.eulers = new pc.Vec3();

    var app = this.app;

    // Listen for mouse move events
    app.mouse.on("mousemove", this._onMouseMove, this);

    // when the mouse is clicked hide the cursor
    app.mouse.on("mousedown", function () {
        app.mouse.enablePointerLock();
    }, this);

    // Check for required components
    if (!this.entity.collision) {
        console.error("First Person Movement script needs to have a 'collision' component");
    }

    if (!this.entity.rigidbody || this.entity.rigidbody.type !== pc.BODYTYPE_DYNAMIC) {
        console.error("First Person Movement script needs to have a DYNAMIC 'rigidbody' component");
    }
};

// update code called every frame
FirstPersonMovement.prototype.update = function(dt) {
    // If a camera isn't assigned from the Editor, create one
    if (!this.camera) {
        this._createCamera();
    }

    var force = this.force;
    var app = this.app;

    // Get camera directions to determine movement directions
    var forward = this.camera.forward;
    var right = this.camera.right;


    // movement
    var x = 0;
    var z = 0;

    // Use W-A-S-D keys to move player
    // Check for key presses
    if (app.keyboard.isPressed(pc.KEY_A) || app.keyboard.isPressed(pc.KEY_Q)) {
        x -= right.x;
        z -= right.z;
    }

    if (app.keyboard.isPressed(pc.KEY_D)) {
        x += right.x;
        z += right.z;
    }

    if (app.keyboard.isPressed(pc.KEY_W)) {
        x += forward.x;
        z += forward.z;
    }

    if (app.keyboard.isPressed(pc.KEY_S)) {
        x -= forward.x;
        z -= forward.z;
    }
        if (app.keyboard.isPressed(pc.KEY_SPACE)) {
        this.entity.rigidbody.applyForce(0, 25, 0);
    }



    // use direction from keypresses to apply a force to the character
    if (x !== 0 && z !== 0) {
        force.set(x, 0, z).normalize().scale(this.power);
        this.entity.rigidbody.applyForce(force);
    }

    // update camera angle from mouse events
    this.camera.setLocalEulerAngles(this.eulers.y, this.eulers.x, 0);
};

FirstPersonMovement.prototype._onMouseMove = function (e) {
    // If pointer is disabled
    // If the left mouse button is down update the camera from mouse movement
    if (pc.Mouse.isPointerLocked() || e.buttons[0]) {
        this.eulers.x -= this.lookSpeed * e.dx;
        this.eulers.y -= this.lookSpeed * e.dy;
    }
};

FirstPersonMovement.prototype._createCamera = function () {
    // If user hasn't assigned a camera, create a new one
    this.camera = new pc.Entity();
    this.camera.setName("First Person Camera");
    this.camera.addComponent("camera");
    this.entity.addChild(this.camera);
    this.camera.translateLocal(0, 0.5, 0);
};

Hello

most simple way to do this would be to add 2 variables inside your update function like this: first variable gets your global this.power speed and the second is your sprint speed * by 2

var movementSpeed = this.power;
var sprintSpeed = this.power * 2;

then add a keyboard press for shift and add: movementSpeed = sprintSpeed;
so now when you press shift your movement speed turns into your sprint speed

then change the this.power to movementSpeed here:
force.set(x, 0, z).normalize().scale(movementSpeed);

so force.set is accessing the movement speed variable instead of the this.power as we are making computation based on movementSpeed variable and not this.power anymore.

3 Likes

How would you write that? Can you share the completed code?

I thought I should share this with you.

It’s a code that I modified and edited to my game needs. It includes walking, running, and crouching.

Note: it does not have jumping

here you go.

var FirstPersonMovementV2 = pc.createScript('firstPersonMovementV2');


FirstPersonMovementV2.attributes.add('camera', {
    type: 'entity',
    description: 'Optional, assign a camera entity, otherwise one is created'
});

FirstPersonMovementV2.attributes.add('power', {
    type: 'number',
    default: 2500,
    description: 'Adjusts the speed of player movement'
});

FirstPersonMovementV2.attributes.add('powerRun', {
    type: 'number',
    default: 2500,
    description: 'Adjusts the speed of player running'
});

FirstPersonMovementV2.attributes.add('powerCrouching', {
    type: 'number',
    default: 2000,
    description: 'Adjusts the speed of player movement when crouching'
});

FirstPersonMovementV2.attributes.add('lookSpeed', {
    type: 'number',
    default: 0.25,
    description: 'Adjusts the sensitivity of looking'
});

FirstPersonMovementV2.attributes.add('cameraHeight', {
    type: 'number',
    default: 0.5,
    description: 'Adjusts the Y position of the camera'
});
FirstPersonMovementV2.attributes.add('cameraSmoothingUp', {
    type: 'number',
    default: 0.25,
    description: 'Camera smooth amount for crouching'
});
FirstPersonMovementV2.attributes.add('cameraSmoothingDown', {
    type: 'number',
    default: 0.15,
    description: 'Camera smooth amount for crouching'
});

FirstPersonMovementV2.attributes.add('playerHeight', {
    type: 'number',
    default: 2.0,
    description: 'Adjusts the Y position of the camera'
});

FirstPersonMovementV2.attributes.add('debug', {
    type: 'boolean',
    default: false,
    description: 'Debug player movement'
});


// initialize code called once per entity
FirstPersonMovementV2.prototype.initialize = function() {
    
    
    // player child entities
    this.playerCollisionUpper = this.entity.findByName('player-collision-upper');
    this.playerCollisionLower = this.entity.findByName('player-collision-lower');
    this.playerModel = this.entity.findByName('player-model');
    
    // physics
    this.force = new pc.Vec3();
    this.eulers = new pc.Vec3();
    this.velocity = new pc.Vec3();
    this.crouchRaycastStart = new pc.Vec3();
    this.crouchRaycastEnd = new pc.Vec3();
    this.crouchRaycastLastAngle = 0;
    this.playerHeightCrouching = this.playerCollisionLower.collision.height;
    
    // player data
    this.playerSpawnPosition = this.entity.getPosition().clone();
    this.playerHeightCurrent = this.playerHeight;
    
    // states
    this.playerMoving = false;
    this.playerCrouching = false;
    this.playerJumping = false;
    this.playerRunning = false;

    // (optional) enable ccd, prevents rigidbody clipping through at high speeds
    const body = this.entity.rigidbody.body;
    body.setCcdMotionThreshold(1);
    body.setCcdSweptSphereRadius(0.1);
    body.setContactProcessingThreshold(0.1); 

    // camera
    this.cameraHeightCurrent = this.playerHeight * this.cameraHeight;
    
    // reparent camera if not a child of this entity
    if (this.entity.children.indexOf(this.camera) < 0) {
        this.camera.reparent( this.entity );
    }
    
    // debug
    this.debugThirdPerson = false; // toggle with 'X' key
    this.debugCamLocalPosition = new pc.Vec3();
    
    // events
    var app = this.app;
    
    // Listen for mouse move events
    app.mouse.on("mousemove", this._onMouseMove, this);

    // when the mouse is clicked hide the cursor
    app.mouse.on("mousedown", function () {
        app.mouse.enablePointerLock();
    }, this);

    // Check for required components
    if (!this.entity.collision) {
        console.error("First Person Movement script needs to have a 'collision' component");
    }

    if (!this.entity.rigidbody || this.entity.rigidbody.type !== pc.BODYTYPE_DYNAMIC) {
        console.error("First Person Movement script needs to have a DYNAMIC 'rigidbody' component");
    }
};

FirstPersonMovementV2.prototype.shoot = function() {
    var bullet = this.entity.findByName('Bullet').clone();
    this.app.root.addChild(bullet);
    bullet.setLocalScale(0.055,0.055,0.055);
    bullet.setPosition(this.entity.findByName('Bullet').getPosition());
    bullet.setRotation(this.entity.findByName('Bullet').getRotation());
    bullet.enabled = true;
};



// update code called every frame
FirstPersonMovementV2.prototype.update = function(dt) {
    // If a camera isn't assigned from the Editor, create one
    if (!this.camera) {
        this._createCamera();
    }

    const force = this.force;
    const app = this.app;

    // Get camera directions to determine movement directions
    const forward = this.camera.forward;
    const right = this.camera.right;

    // movement
    let x = 0;
    let z = 0;

  
  
  
  


   






    // attributes
    let playerHeight = this.playerHeight;
    let power = this.power;
    let powerRun = this.power*3;

    // Movement mechanic: input
    // Use W-A-S-D keys to move player
    // Check for key presses
    
    // start shooting
    if (this.app.mouse.isPressed(pc.KEY_E)) {
        this.timer += dt;

        if (this.timer > 0.5) {
            this.timer = 0;
            this.shoot();
        }
    }
    
    
    if (app.keyboard.isPressed(pc.KEY_A) || app.keyboard.isPressed(pc.KEY_Q)) {
        x -= right.x;
        z -= right.z;
    }

    if (app.keyboard.isPressed(pc.KEY_D)) {
        x += right.x;
        z += right.z;
    }

    if (app.keyboard.isPressed(pc.KEY_W)) {
        x += forward.x;
        z += forward.z;
    }

    if (app.keyboard.isPressed(pc.KEY_S)) {
        x -= forward.x;
        z -= forward.z;
    }
  
    if (app.keyboard.isPressed(pc.KEY_SHIFT) && app.keyboard.isPressed(pc.KEY_W)) {
        
        x += forward.x;
        z += forward.z;

        power = powerRun;

        playerRunning = true;

       
  
    }
    else( 
        
        

     (playerRunning = false)

    );
   





    // Crouching mechanic: input
    if (app.keyboard.isPressed(pc.KEY_C)) {
        this.playerCrouching = true;
    }
    else if (this.playerCrouching) {
        // verify that there is room to stand up
        this.playerCrouching = this.raycastCrouch();   
    }
    
    // Crouching mechanic: apply
    if (this.playerCrouching) {
        playerHeight = this.playerHeightCrouching;
        power = this.powerCrouching;
    }
    this._updateCrouchMechanic( playerHeight, dt );
    
    // Movement mechanic: use direction from keypresses to apply a force to the character
    this.playerMoving = x !== 0 && z !== 0;
    if (this.playerMoving) {
        force.set(x, 0, z).normalize().scale(power);
        this.entity.rigidbody.applyForce(force);

       
    }

    // Running mechanic: run like mad
    this.playerRunning = x !== 0 && z !== 0;
    if (this.playerRunning) {
        force.set(x, 0, z).normalize().scale(powerRun);
        this.entity.rigidbody.applyForce(force);

       
    }
      

    // respawn when falling off map
    if (this.entity.getPosition().y < -60) {
        this.entity.rigidbody.teleport(0, -45, 0);
        this.entity.rigidbody.linearVelocity = this.velocity.set(0, 0, 0);
        this.entity.rigidbody.angularVelocity = new pc.Vec3(0, 0, 0);
    }

    // update camera
    this._updateCamera();
    
};
 

//
// Utilities
//

FirstPersonMovementV2.prototype._updateCrouchMechanic = function (heightTarget, dt) {

    // on height change
    let height = this.playerHeightCurrent;
    if (height !== heightTarget) {
        
        this.playerHeightCurrent = heightTarget;
        
        // (optional) instantly scale player model
        //this.playerModel.setLocalScale(1, heightTarget * 0.5, 1);
        
        // (optional) instantly offset player model. usefull for a crouching skeletal animations.
        //this.playerModel.setLocalPosition(0, (this.playerHeight - heightTarget) * 0.5, 0);
        
        // disable the upper collision body when crouching
        const upperCollisionEnabled = this.playerCollisionUpper.collision.enabled;
        if (this.playerCrouching) {
            if (upperCollisionEnabled) {
                this.playerCollisionUpper.collision.enabled = false; 
                this.playerCollisionUpper.enabled = false;
            }
        }
        else {
            if (!upperCollisionEnabled) {
                this.playerCollisionUpper.collision.enabled = true; 
                this.playerCollisionUpper.enabled = true;
            }
        }
    }
    
    // smoothing //
    
    // setup smoothing speed based on going up or down
    const smoothing = this.playerCrouching ? this.cameraSmoothingDown : this.cameraSmoothingUp;
    const t = Math.min(dt / smoothing, 1.0);
    
    /// update camera height smooth
    let cameraHeightTarget = heightTarget * this.cameraHeight * 0.5; // - this.playerHeight * 0.5;
    this.cameraHeightCurrent = pc.math.lerp(this.cameraHeightCurrent, cameraHeightTarget, t);
    
    /// (optional) update player model smooth, not ideal for skeletal animations.
    // scale
    let playerModelScaleY = this.playerModel.getLocalScale().y;
    playerModelScaleY = pc.math.lerp( playerModelScaleY, heightTarget * 0.5, t);
    this.playerModel.setLocalScale(1, playerModelScaleY, 1);
    // pos
    let playerModelPosY = this.playerModel.getLocalPosition().y;
    const scaleDelta = - playerModelScaleY;
    this.playerModel.setLocalPosition(0, -scaleDelta, 0);
    
};

// Raycast above player when in Crouching State
FirstPersonMovementV2.prototype.raycastCrouch = function() {
    
    const height = this.playerHeightCurrent;
    const radius = this.playerCollisionLower.collision.radius;
    const positionStart = this.playerCollisionLower.getPosition();
    const positionEntity = this.entity.getPosition();
    const padding = -0.15;  // add a little buffer to radius to cast from inside
    const precision = 9;    // amount of raycasters to cast
    
    // raycaster is colliding with rigidbody
    let colliding = false;
    
    // nifty optimization (starts angle at last hit angle)
    let angleOffset = this.crouchRaycastLastAngle;
    
    // make a circle of raycasters to cast above player
    for (let i = 0; i < precision; i ++) {
        
        // setup raycaster positions
        this.crouchRaycastStart.copy(positionStart);
        this.crouchRaycastEnd.copy(positionEntity);
        this.crouchRaycastEnd.y += this.playerHeight;
        
        // offset raycaster positions in a circle, index 0 reserved for center raycast
        let phi = 0;
        if (i !== 0) {
            const len = precision - 1;
            const slice = ((i - 1) / len);
            const pizza = 360 * pc.math.DEG_TO_RAD;
            phi = (pizza * slice) + angleOffset;
            const x = Math.cos( phi ) * (radius + padding);
            const z = Math.sin( phi ) * (radius + padding);
            this.crouchRaycastStart.x += x;
            this.crouchRaycastStart.z += z;
            this.crouchRaycastEnd.x += x;
            this.crouchRaycastEnd.z += z;
        }
        
        // raycast from center to to player height
        const result = this.app.systems.rigidbody.raycastFirst(this.crouchRaycastStart, this.crouchRaycastEnd);

        // is raycaster colliding with a rigidbody?
        colliding = result && result.entity.rigidbody;

        // debug: render line
        if (this.debugThirdPerson && this.debug) {
            this.app.renderLine(this.crouchRaycastStart, this.crouchRaycastEnd, colliding ? pc.Color.RED : pc.Color.GREEN);
        }
        
        // exit loop when collision success
        if (colliding) {
            this.crouchRaycastLastAngle = phi;
            break;   
        }
    }

    return colliding;
};

FirstPersonMovementV2.prototype._createCamera = function () {
    // If user hasn't assigned a camera, create a new one
    this.camera = new pc.Entity();
    this.camera.setName("First Person Camera");
    this.camera.addComponent("camera");
    this.entity.addChild(this.camera);
    this.camera.translateLocal(0, this.cameraHeight, 0);
};

FirstPersonMovementV2.prototype._updateCamera = function () {
    
    // update camera angle from mouse events
    this.camera.setLocalEulerAngles(this.eulers.y, this.eulers.x, 0);
    
    // update position
    this.camera.setLocalPosition(0, this.cameraHeightCurrent, 0);
    
    // debug: third person camera
    this.debugThirdPersonCamera();
};

//
// Input
//

FirstPersonMovementV2.prototype._onMouseMove = function (e) {
    // If pointer is disabled
    // If the left mouse button is down update the camera from mouse movement
    if (pc.Mouse.isPointerLocked() || e.buttons[0]) {
        this.eulers.x -= this.lookSpeed * e.dx;
        this.eulers.y -= this.lookSpeed * e.dy;
    }
};


//
// Debug
//

// simple third person camera
FirstPersonMovementV2.prototype.debugThirdPersonCamera = function() {
    
    const camForward = this.camera.forward;
    const camDistance = 6;
    
    // toggle camera
    if (this.app.keyboard.wasPressed(pc.KEY_X)) {
        this.debugThirdPerson = !this.debugThirdPerson;
        
        // on toggle state change
        if (this.debugThirdPerson) {
            // setup cam for TPV
            const camLocalPosition = this.camera.getLocalPosition();
            this.debugCamLocalPosition.copy( camLocalPosition );
        }
        else {
            // reset cam to FPV
            this.camera.setLocalPosition( 0, this.cameraHeightCurrent, 0);   
        }
    }
    
    // update camera position
    if (this.debugThirdPerson) {
        const cameraHeight = this.cameraHeightCurrent;
        this.camera.setLocalPosition( 
            -camForward.x * camDistance, 
            -camForward.y * camDistance + cameraHeight, 
            -camForward.z * camDistance);
    }
};



1 Like

To set up the player just look over here.

https://playcanvas.com/editor/scene/1192176

The set up is quite different from just a simple capsule but its still easy to do.

2 Likes

how does the player run?

hold shift and w at the same time

2 Likes