Search code examples
javascriptcraftyjs

Creating a class of Crafty JS entity (class of a class?)


I am trying to create a class which creates a Crafty entity with specific properties. So far, the functions within the class do not run because 'this' refers to the window object

$(document).ready(function () {

Crafty.init(window.innerWidth, window.innerHeight);

var player = new controller(37,38,39,40);

player.d.color("red").attr({
    w: 50,
    h: 50,
    x: 0,
    y: 0
}); 

// Jump Height = velocity ^ 2 / gravity * 2
// Terminal Velocity = push * (1 / viscosity)
var gravity = 1; 
var viscosity = 0.5; 
var frame = (1 / 20);
var distanceMultiplier = 10; //pixels per meter
var timeMultiplier = 20; //relative to actual time
var keystart = [];
var keyboard = [];

function controller (controls) {
    this.d = Crafty.e();
    this.d.addComponent("2D, Canvas, Color, Collision");
    this.d.collision();
    this.d.mass = 1;
    this.d.a = {
    extradistance : 0, 
    velocity : 0,
    acceleration : 0, 
    force : 0, 
    resistance : 0 

    };
    this.d.a.push = 0;
    this.d.v = {
    extradistance : 0, 
    velocity : 0, 
    acceleration : 0, 
    force : 0 
    };
    this.d.jumping = true;
    this.d.onHit("Collision", function () {
        var a = this.d.hit("Collision");
        if (a) {
            for (var b in a) {
                this.d.x = this.d.x - a[b].normal.x * a[b].overlap;
                this.d.y = this.d.y - a[b].normal.y * a[b].overlap;
                if (a[b].normal.y < -0.5) {
                    this.d.jumping = false;
                }
                if (Math.abs(a[b].normal.x) < 0.2) {
                    this.d.v.velocity = this.d.v.velocity * a[b].normal.y * 0.2;
                }
                if (Math.abs(a[b].normal.y) < 0.2) {
                    this.d.a.velocity = this.d.a.velocity * a[b].normal.x * 0.2;
                }
            }
            return;
        }
    });
    this.d.physics = function () {
        if (keyboard[arguments[1]] && !this.jumping) {
            this.v.velocity = 5;
            this.jumping = true;
        }

        if (keyboard[arguments[1]] && this.jumping) {
            var now = new Date();
            if (now.getTime() - keystart[arguments[1]].getTime() < 500) {
                this.v.velocity = 5;
            }
        }

        if (keyboard[arguments[0]] && keyboard[arguments[2]]) {
            this.a.velocity = 0;
        } else {
            if (keyboard[arguments[0]]) {
                this.a.velocity = -3;
            }
            if (keyboard[arguments[2]]) {
                this.a.velocity = 3;
            }
        }

        if (keyboard[arguments[3]]) {
            this.v.velocity = -5;
        }

        this.a.force = this.a.push - this.a.resistance;
        this.a.acceleration = this.a.force / this.mass;
        this.a.velocity = this.a.velocity + (this.a.acceleration * frame);
        this.a.extradistance = (this.a.velocity * frame);
        this.a.resistance = this.a.velocity * viscosity;
        this.attr({
            x: (this.x + (this.a.extradistance * distanceMultiplier))
        });

        this.v.force = gravity * this.mass;
        this.v.acceleration = this.v.force / this.mass;
        this.v.velocity = this.v.velocity - (this.v.acceleration * frame);

        this.v.extradistance = (this.v.velocity * frame);
        this.attr({
            y: (this.y - (this.v.extradistance * distanceMultiplier))
        });
        setTimeout(this.physics, (frame * 1000) / timeMultiplier);
    };
    this.d.listen = function(){ document.body.addEventListener("keydown", function (code) {
        var then = new Date();
        if (!keyboard[code.keyCode] && !this.jumping && code.keyCode == arguments[1]) { //only if not yet pressed it will ignore everything until keyup
            keyboard[code.keyCode] = true; //start movement
            keystart[code.keyCode] = then; //set time
        }
        if (!keyboard[code.keyCode] && code.keyCode != arguments[1]) { //only if not yet pressed it will ignore everything until keyup
            keyboard[code.keyCode] = true; //start movement
            keystart[code.keyCode] = then; //set time
        }
    });
    };
}
player.d.physics();
player.d.listen();
document.body.addEventListener("keyup", function (code) {
    keyboard[code.keyCode] = false;
});

});

In trying to put the functions as prototypes of the class, I run into a problem.

Crafty.init(500,500);
function block () {
block.d = Crafty.e("2D, Color, Canvas");
block.d.color("red");
block.d.attr({x:0,y:0,h:50,w:50});
}
block.d.prototype.green = function() {
this.color("green");
}
var block1 = new block();
block1.d.color();

If an object is defined in the constructor, I cannot use it to add a prototype to.


Solution

  • Generally in Crafty, we favor composition. That is, you extend an entity by adding more components to it. You can have kind of a hierarchy by having one component automatically add others during init.

    I haven't looked through all of your example code, because there's a lot! But consider the second block:

    function block () {
      block.d = Crafty.e("2D, Color, Canvas");
      block.d.color("red");
      block.d.attr({x:0,y:0,h:50,w:50});
    }
    block.d.prototype.green = function() {
       this.color("green");
    }
    var block1 = new block();
    block1.d.color();
    

    You're trying to combine Crafty's way of doing things (an entity component system) with classes in a way that's not very idiomatic. Better to do this:

    // Define a new component with Crafty.c(), rather than creating a class
    Crafty.c("Block", {
       // On init, add the correct components and setup the color and dimensions
       init: function() {
           this.requires("2D, Color, Canvas")
               .color("red")
               .attr({x:0,y:0,h:50,w:50});
       },
       // method for changing color
       green: function() {
           this.color("green");
       }
    });
    
    // Create an entity with Crafty.e()
    block1 = Crafty.e("Block");
    // It's not easy being green!
    block1.green();