Object in Javascript undefined when calling a method - javascript

I'm building a game similar to the Chrome dinosaur in Vanilla JS. To animate the obstacles I have created a class Obstacle, which stores their position and size, and defines a method that changes the position.
var Obstacle = function (type, w, h, sprite) {
this.h = h; // Obstacle height
this.w = w; // Obstacle width
this.x = 600; // Starting horizontal position
this.y = GROUND - this.h; // Starting vertical position
this.type = type;
this.sprite = sprite;
this.speed = -4;
this.move = function () {
this.x += this.speed;
}
}
These are stored inside an array, defined as a property of a different class:
var ObstacleBuffer = function () {
this.bufferFront = [];
this.createObstacle = function () {
this.bufferFront.push(this.createBox());
}
// Obstacle creators
this.createBox = function () {
if (Math.random() < 0.5) return new Obstacle ("box1", OBSTACLES.box1.w, OBSTACLES.box1.h, OBSTACLES.box1.sprite);
return new Obstacle ("box2", OBSTACLES.box2.w, OBSTACLES.box2.h, OBSTACLES.box2.sprite);
}
//Obstacle animation
this.animateObstacle = function () {
this.bufferFront[0].move();
}
}
When running this an error pops up:
TypeError: Cannot read property 'move' of undefined.
I have logger the content of this.bufferFront[0] and it correctly show the Obstacle stored inside it.
I have also tried assigning this.bufferFront[0] locally to a variable and then tried to call the method from there. The data stored is correct but the error pops up whenever trying to access Obstacles methods or properties.
Any ideas ? Thank you very much.
EDIT - I have reviewed the code as per your suggestions and the problem seems to be at the point where I'm calling the function. Before generating the obstacles I am preloading a series of images and only generating obstacles once these have load:
this.loadWhenReady = function () {
if (self.resources.isLoadComplete()) {
self.sound.load(self.resources.list.sfx);
drawGround();
this.obstacles.createObstacle(); // <--
self.startGame();
return;
} else {
setTimeout(self.loadWhenReady, 300);
}
}
And this is called in:
setTimeout(self.loadWhenReady, 300);
Of course const self = this has been defined before.
Everything seems to move forward when the method is called outside the SetTimeout.
Why is this happening though ? And is there a way of solving this while calling the method in there ?
SOLVED - As #Bergi and #Jaime-Blandon mention it was a context problem. Calling the method from outside the setTimeout loop or using self.obstacle.createObstacle() instead of this.obstacle.createObstacle() did the trick and solved the issue.

Try this change on the ObstacleBuffer Class:
var ObstacleBuffer = function () {
this.bufferFront = [];
this.createObstacle = function () {
//this.bufferFront.push(this.createBox());
this.createBox();
}
// Obstacle creators
this.createBox = function () {
if (Math.random() < 0.5) this.bufferFront.push(new Obstacle ("box1", OBSTACLES.box1.w, OBSTACLES.box1.h, OBSTACLES.box1.sprite));
this.bufferFront.push(new Obstacle ("box2", OBSTACLES.box2.w, OBSTACLES.box2.h, OBSTACLES.box2.sprite));
}
//Obstacle animation
this.animateObstacle = function () {
this.bufferFront[0].move();
}
}
Best Regards!

Related

Changing stroke colour using recursion

I am struggling with the error of which throw me once I am trying to recursively change a colour. The error is: “Uncaught TypeError Cannot read property ‘map’ of undefined (sketch: line 18)” and reference to this piece of code: this.color.levels.map(x => x * 0.9) .
I supposed that’s because of a recursive and problem with “this” context. The “right” function which I’ve created executing just one time until above error is thrown.
Any ideas how to make this work or how to recursively change the colour referencing to the same object which I’ve been created?
My code: https://editor.p5js.org/grzegorz.kuguar#gmail.com/sketches/Syc1qQmnQ
<code> class Branch {
constructor(begin, end, strokeW, color, angle) {
this.begin = begin;
this.end = end;
this.angle = angle;
this.strokeW = strokeW;
this.color = color;
}
display() {
stroke(this.color);
strokeWeight(this.strokeW);
line(this.begin.x, this.begin.y, this.end.x, this.end.y);
}
right(angle) {
let direction = p5.Vector.sub(this.end, this.begin);
direction.rotate(angle);
let nextPoint = p5.Vector.add(direction, this.end);
let right = new Branch(this.end, nextPoint, this.strokeW*0.7, this.color.levels.map(x => x * 0.9)); //this line of code throw an error once I am trying to manipulate on the array
return right;
}
}
let tree = [];
let trunk;
let something; //just for check how looks like a p5.color object
function setup() {
createCanvas(400, 400);
background(20);
something = color(100, 230, 100);
console.log(something);
let x = createVector(width/2, height);
let y = createVector(width/2, height-100);
trunk = new Branch(x,y, 7, color(255,100, 100));
tree[0] = trunk;
tree.push(trunk);
}
function draw() {
for(let i = 0; i < tree.length; i++) {
tree[i].display();
}
}
function mousePressed() {
for(let i = tree.length-1; i >= 0; i--) {
tree.push(tree[i].right(Math.PI/4, 0.66));
}
}
The problem is in your use of map. You are doing:
this.col.levels.map(x => x * 0.9)
which returns an array, not a p5.Color object.
To create the new color object, instead use:
color.apply(this, this.col.levels.map(x => x * 0.9))
You can see the full sketch here

Javascript constructor not working

I posted this on gamedev.stackexchange but was referred here so I'll try. I've got this simple menu that is a function, with a mainmenu.prototype.Render to draw it to the screen. Inside the mainmenu function i would like to make an array of objects containing the buttons x, y positions and the .src.
This is my current code that works, so no problem with the function itself:
this.Mainmenu = function() {
}
this.Mainmenu.prototype.Render = function() {
imgPause = new Image();
imgPause.src = 'img/pause.png';
c.drawImage(imgPause, canvas.width - 42, 10);
}
var mainmenu = new self.Mainmenu();
What I would like the final result to look like, but can't get to work (I've included the error in a comment):
this.Mainmenu = function() {
this.button = function(src, X, Y) {
this = new Image(); // Gives error "Invalid left-hand side in assignement"
this.src = src;
this.X = X;
this.Y = Y;
}
this.buttons = [pause = new this.button(src, X, Y)];
}
this.Mainmenu.prototype.Render = function() {
for (i = 0; i < this.buttons.length; i++) {
c.drawImage(this.src, this.X, this.Y);
}
}
var mainmenu = new self.Mainmenu();
But it doesn't work, if anyone can identify where my mistake is it would be appreciated, my patience is about to run out.
Well, your mistake is exactly what your js interpreter says it is - the left side of your assignment is invalid. Namely, you cannot assign this to anything, that's a rule of thumb in all languages that have the this word. The reasoning behind that is obvious - this denotes the current context of the function, the hidden argument of its. If you could overwrite it dynamically, you could alter the behaviour of every single function that is using yours thus the whole program.
How not to use this in this broken way:
this.MainMenu = function() {
this.Button = function(src, X, Y) {
var image = new Image();
image.src = src;
image.X = X;
image.Y = Y;
return image;
}
this.buttons = [pause = new this.Button(src, X, Y)];
}
Also, name your classes with PascalCase (Button, not button) and your variables with camelCase EVERYWHERE (x, not X).
You cannot do this
this.button = function(src, X, Y) {
this = new Image(); // Gives error "Invalid left-hand side in assignement"
}
this represents the current instance of Mainmenu. You cannot override an instance by another
instance.
No sense.

Douglas Crockfords - how to call base method into an inherited class

I'm trying to construct a base class Shape using Crockford's inheritance pattern. Using this base Shape, I'm trying to draw a circle, a rectangle and a triangle. I'm kinda stuck. I didn't know how to call/modify the base method
function points(x,y) {
x = this.x;
y = this.y;
}
function Shape() {
return {
this.points: [ ],
init : function(){
if(typeof this.context === ‘undefined’){
var canvas = document.getElementById(‘canvas’);
var context = canvas.getContext(‘2d’);
}
},
draw: function(){
var context = this.context;
context.beginPath();
context.moveTo(this.points[0].x, this.points[0].y);
for(var i=1; i< this.parameter.length; i++){
context.lineTo(this.parameter[i].x, this.parameter[i].y);
}
context.closePath();
context.stroke();
}
};
}
function Circle(x, y, r){
var points = Shape();
point.x = x;
points.y = y;
points.r = r;
var baseMethod = that.draw;
that.draw = function(){
/*how to modify the base method to draw circle*/
};
}
function Rectangle(a, b, c, d){
var points = Shape();
point.a = a;
points.b = b;
points.c = c;
points.d = d
var baseMethod = that.draw;
that.draw = function(){
/*how to call base method to draw rectangle*/
};
}
You've got quite a few problems going on with your code. Firstly you need to make sure you've got your basic drawing code working before moving on to more complicated shapes such as circles and rectangles. Start with drawing lines. I've tidied up your code and got it working with drawing straight lines:
//returns basic point object which has
//two properties x & y
function point(x, y) {
return {
x: x,
y: y
}
}
//function that returns a shape object with all the
//mechanisms for drawing lines between points
function Shape(canvasID) {
return {
points: [], //not 'this.points' (which would most likely be window.points)
addPoint: function(x, y) {//adding a point to a shape is an operation of shape
this.points.push(point(x, y))
},
init: function() {
if (typeof this.context === 'undefined') {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
this.context = ctx; //add the context reference to the current shape object
}
},
draw: function() {
this.init();
var context = this.context;
context.beginPath();
var that = this; //create a local reference to the current 'this' object.
//insures us against any possible 'this' scope problems
context.moveTo(that.points[0].x, that.points[0].y);
for (var i = 1; i < that.points.length; i++) {
context.lineTo(that.points[i].x, this.points[i].y);
}
context.closePath();
context.stroke();
}
};
}
//Simple Line object - good for testing your
//basic drawing functionality
function Line(canvasID, x, y, x2, y2) {
var shape = Shape(canvasID);
shape.addPoint(x, y);
shape.addPoint(x2, y2);
shape.draw();
}
//Execute your drawing functionality after the
//window has loaded to make sure all your objects exist before
//trying to use them
window.onload = function() {
Line('canvas', 100, 100, 200, 200);
}
I'm not necessarily sold on whether this is the best way to approach what you are doing - but DC's basic approach is to create objects without having to use the "new" keyword. So he returns an object from a function call using the JavaScript object notation.
Now that you can draw a line, the next step is to draw a series of connected lines one after the other (a path). After that, create your rectangle. You need some code to tell your code where to start drawing the rectangle (the start x/y coordinate) and then you can have parameters denoting the height and width of the rectangle which will be used to calculate the coordinates of the rectangle's corners and passed to the shape object to be drawn in the same way the series of connected lines were drawn. One caveat, though, is to check if there is some sort of 'createRectangle' function on the context object (and same for circle). I don't actually know myself as I've not done this sort of work in HTML5/canvas - although I have in other environments.
Edit
Forgot to mention that you will need to make sure the doctype declaration of your html is html5. A lot of IDE's will automatically declare your html as html4. Html5 just needs: <!DOCTYPE html>
Also, make sure you declare a canvas element in the html body, something like this:
<canvas id="canvas" width="300" height="150">
</canvas>

Updating an object's definition in javascript

I'm fairly new to object orientated stuff so this may very well be the wrong way to be going about getting this done.
This is a very slimmed down version of what I currently have, but the concept is essentially the same. When the user clicks on a canvas element on my page, I create 20 instances of the particle object below, append them to an array whilst at the same time updating the canvas at 30FPS and drawing circles based on the x property of the instances of each object. Once a particle is off the screen, it's removed from the array.
var particle = function()
{
var _this = this;
this.velocity = 1;
this.x = 0;
this.updateVelocity = function(newVelocity)
{
_this.multiplier = newVelocity;
}
var updateObject = function()
{
_this.x += velocity;
}
}
I would like the user to be able to control the velocity of new particles that are created using an input element on the page. When this is updated I have an event listener call
particle.updateVelocity(whateverTheUserEntered);
However I get the error "particle has no method updateVelocity". After a bit of reading up on the subject I understand that to call that function I need to create an instance of the object, but this will only update the velocity value of that instance which isn't going to work for me.
My question is, is there a way to achieve what I'm doing or have I approached this in completely the wrong way? As I said, I'm still getting to grips with OOP principles so I may have just answered my own question...
Try this:
var particle = new (function()
{
var _this = this;
this.velocity = 1;
this.x = 0;
this.updateVelocity = function(newVelocity)
{
_this.multiplier = newVelocity;
}
var updateObject = function()
{
_this.x += velocity;
}
})();
Your's is creating a function and then setting the variable particle to that value. particle will not have any special properties because of this. My example above, however, by using new and the function as a constructor assigns particle an instance of a (now anonymous) class.
I think what you want is:
// define a particle "class"
function Particle() {
var _this = {};
_this.velocity = 1;
_this.x = 0;
_this.multiplier = 1;
_this.updateVelocity = function(newVelocity)
{
_this.multiplier = newVelocity;
}
_this.updateObject = function()
{
_this.x += velocity;
}
return _this;
}
// make 1 particle
var myParticle = new Particle();
myParticle.updateVelocity(100);
// make a bunch of particles
var myParticles = [];
for (var i=0; i < 100; i++) {
var p = new Particle();
p.updateVelocity(Math.random * 100);
myParticles.push(p);
}
If you change it to
var particle = new function () {
}
The 'new' will cause creation of an instance.
So create a function that builds new particle instances for you.
Make velocity static and have a static method to update it. This way, you can still make instances of particle and update the velocity for all of them.
var particle = function() {
// particle stuff
}
particle.velocity = 1;
particle.updateVelocity = function(newVelocity) {
this.velocity = newVelocity
}

Initiation of an object inside another

I have a piece of js software that is structured like so:
obj = new object[id]();
function wrapperFunction (e) {
var pos = findPos(this);
e._x = e.pageX - pos.x;
e._y = e.pageY - pos.y;
var func = obj[e.type];
if (func) {
func(e);
}
}
__
obj.line = function () {
this.started = false;
this.mousedown = function (e) {
}
this.mousemove = function (e) {
if (this.started) {
}
}
this.mouseup = function (e) {
if (this.started) {
}
}
}
The above code block is duplicated for multiple shapes so there is also a obj.square obj.circle etc...
I also have a shape object that is as follows.
function Shape (type, color, height, width, radius, x, y) {
this.type = type;
this.color = color;
this.h = height;
this.w = width;
this.r = radius;
this.points = ["x","y"];
this.points["x"] = [x];
this.points["y"] = [y];
};
I would like to initiate the shape object on a mousedown for each obj.* and populate the shape object with the propper info.
Now for the issue.
The radius is calcuated on every mousemove as well as height and width but when I add shapes = new Shape(circle, black, 10, 10, null, e._x, e._y) to the mousemove so it looks like...
this.mousemove = function (e) {
if (this.started) {
shapes = new Shape(circle, black, 10, 10, null, e._x, e._y);
}
}
The shape object does not create.
If I create the shape object inside the wrapper function instead of the mousemove then the object initiates but I cannot use radius or height/width.
How can I create an object inside another object inside a wrapper function so I can use calculated terms inside the created object? Is there an alternate route to take besides what I am doing?
Aside from wonkiness in the obj = new object[this.id](); line, I think you're just missing a this keyword:
this.mousemove = function (e) {
if (this.started) {
this.shapes = new Shape(circle, black, 10, 10, null, e._x, e._y);
}
}
Edit just noticed more wonkiness in your code (yes, that's a technical term :). I think you want to change these lines in the constructor:
this.points = ["x","y"]; // creates an array, which is indexed by numbers
this.points["x"] = [x]; // tacks on some ad-hoc properties to the array, which
this.points["y"] = [y]; // doesn't really make sense
to this:
this.points = {x: x, // I think this is what you actually mean to do.
y: y};

Categories