[SOLVED] Character becomes grounded while falling through the air

We have a character capsule set up with a raycasting object to detect ground collisions. However when it falls for more than a second, it will randomly become grounded in midair. This is the script. Any ideas?

var Jump = pc.createScript('jump');

Jump.attributes.add('raycastTargetBelow', { type: 'entity', description: 'Entity to point downward raycasts at for collision checks.' });
Jump.attributes.add('raycastBelowOffset', { type: 'number', default: 0.1, description: 'Offsets raycast downward to be closer/further from character.' });

// initialize code called once per entity
Jump.prototype.initialize = function() {       
    this._initRaycastBelowTarget(this.raycastBelowOffset);
};

// update code called every frame
Jump.prototype.update = function(deltaTime) {
    if(this._isGrounded() !== null) {
        console.log(`Grounded`); // <---- Happens randomly in midair
    } else {
        console.log(`Airborne. Velocity: ${this.entity.rigidbody.linearVelocity}`);
    }
};

Jump.prototype._isGrounded = function() {
    return this.app.systems.rigidbody.raycastFirst(this.entity.getPosition(), this.raycastTargetBelow.getPosition());
};

Jump.prototype._initRaycastBelowTarget = function (offset) {
    // Automatically creates a raycast target below the character's
    // collider if none was provided. Accepts an offset to modify distance.
    if(this.raycastTargetBelow === null) {
        this.raycastTargetBelow = new pc.Entity('RaycastBelow');
        this.raycastTargetBelow.setPosition(new pc.Vec3(0, -this.entity.collision.halfExtents.y + offset, 0));
        this.entity.addChild(this.raycastTargetBelow);
                
        console.log(`RaycastTargetBelow added to ${this.entity.name} at ${this.raycastTargetBelow.getLocalPosition()}`);
    } else {
        console.log(`Using manually assigned RaycastTargetBelow.`);
    }
};

The character has a rigid body and collision on it.

I feel that your script is set up unnecessarily complicated. Perhaps for a reason, but it’s impossible to see what’s going wrong.

Do you have any suggestions as to what seems to make it complicated? Or what we could change that would be more efficient?

I assume that you want to put the character on the ground and / or let it fall when there is no ground?

We are trying to perform a jump when grounded. So we are doing a grounded check. which while falling through the air at some point returns true. We were trying to figure out if the ray cast is not keeping up when falling or something like that.

To make it easy I would create two empty entities as a child of your entity. These entities are the start and end point for the raycast. Place the start point above the entity and the end point below the entity.

var ground = this.app.systems.rigidbody.raycastFirst(start, end);
if (ground) {
    // player is grounded
} 
else {
    // player is in the air
}

I don’t get why raycast is needed, if you set a variable to the player that’s set to true when the collision with the ground is detected and it’s set to false when there’s no collision you can set the status of player as falling with the proper animation and when it touch ground you can play the grounded animation when it reach ground if (status===‘falling’ and ground===true) then play grounded animation). That’s if i have understood right what your problem is.

As far as I know there is no event that constantly checks for collision. There are only events that are triggered when entering and exiting a collision. I know from experience that this can cause problems if, for example, your ground consists of different parts and the character is moving from one part to another part.

SOLVED

We simplified it, and seem to have fixed the issue. Here is the code

var Jump = pc.createScript('jump');

// initialize code called once per entity
Jump.prototype.initialize = function() {        
    this.entity.collision.on('collisionstart', this.onCollisionStart, this);
};

// update code called every frame
Jump.prototype.update = function(deltaTime) {
   
};

Jump.prototype.onCollisionStart = function(collision) {
    if(collision.other.rigidbody && collision.other.tags.has('solid')) {
        if(collision.other.getPosition().y <= (this.entity.getPosition().y - this.entity.collision.halfExtents.y)) {
            console.log("Landed");
        } else {
            console.log("Bumped");
        }
    }
};
2 Likes