game_actor_component_physics_component.js

import ActorComponent from "./actor_component.js";
import Vec2 from "../../vector/vec2.js";

/**
 * @module PhysicsComponent
 * @fileoverview Contains PhysicsComponent class.
 */

/**
 * @class
 * @extends ActorComponent
 * Handles physics attributes like velocity, friction, etc.
 */
class PhysicsComponent extends ActorComponent {
    /**
     * Create a new physics component.
     * @param {number|Vec2} vel Initial velocity
     * @param {number} angularVel Initial angular velocity
     * @param {number} friction Friction to be applied
     * @param {number} mass Mass of object
     * @param {number} bounce Bounce percentage on collision
     * @constructor
     */
    constructor({
        vel = 0,
        angularVel = 0,

        friction = 0,
        mass = 1,
        bounce = 0.5
    } = {}) {
        super();

        this.vel = Vec2.from(vel);
        this.angularVel = angularVel;

        this.friction = friction;
        this.mass = mass;
        this.bounce = bounce;
    }

    update(elapsed) {
        super.update(elapsed);

        const dir = this.vel.norm();
        const friction = dir.mulv(this.mass * this.friction * elapsed);

        const vel = this.vel.sub(friction);

        if (Math.sign(vel.x) !== Math.sign(this.vel.x))
            vel.x = 0;
        if (Math.sign(vel.y) !== Math.sign(this.vel.y))
            vel.y = 0;

        this.vel = vel;

        this.doMove(elapsed);

        const angularFriction = this.mass / 100 * this.friction * elapsed;
        let angularVel = (Math.abs(this.angularVel) - angularFriction) * Math.sign(this.angularVel);

        if (Math.sign(angularVel) !== Math.sign(this.angularVel))
            angularVel = 0;

        this.angularVel = angularVel;

        this.actor.rotation += this.angularVel * elapsed;
    }

    /**
     * Perform movement and check collisions as necessary.
     * @param {number} elapsed Time since last update cycle in seconds
     */
    doMove(elapsed) {
        if (this.vel.isZero())
            return;

        const to = this.actor.pos.add(this.vel.mulv(elapsed));

        let collision = {
            result: false
        };

        let result = this.actor.space.checkUnitCollision(this.actor, to);

        if (!result.result) {
            for (const chunk of this.actor.overlappingChunks.values()) {
                result = chunk.checkCollision(this.actor, to);
                if (result.result)
                    collision = result;
            }
        } else {
            collision = result;
        }

        if (collision.result) {
            const pos = this.actor.pos;

            const correction = collision.axes.mulv(collision.overlap + 1);

            if (collision.axes.x !== 0)
                pos.x = to.x - Math.sign(this.vel.x) * Math.abs(correction.x);
            else
                pos.x = to.x;

            if (collision.axes.y !== 0)
                pos.y = to.y - Math.sign(this.vel.y) * Math.abs(correction.y);
            else
                pos.y = to.y;

            // if (collision.actor.components.physicsCount === 0)
            this.vel = this.vel.mirrorOver(collision.axes).mulv(-1 * this.bounce);

            // for (const comp of collision.actor.components.getPhysics()) {
            //     const e = (this.bounce + comp.bounce) / 2;
            //     const div = this.mass + comp.mass;
            //     const diff = this.vel.sub(comp.vel);
            //     const impulse = diff.mulv(-1 * e);
            //
            //     this.vel = this.vel.sub(impulse.mulv(comp.mass / div));
            //     comp.vel = comp.vel.sub(impulse.mulv(this.mass / div));
            // }

            this.actor.pos = pos;
        } else {
            this.actor.pos = to.copy();
        }
    }
}

export default PhysicsComponent;