Calling function bad argument - javascript

I'm trying to write a game and I got a problem with function showing player:
function Furry() {
this.board=document.querySelectorAll('#board div');
this.x = 0;
this.y = 0;
this.direction = "right";
}
and:
showFurry=function(){
this.x = Math.floor(Math.random() * 10);
this.y = Math.floor(Math.random() * 10);
this.board[ this.position(this.furry.x , this.furry.y) ].classList.add('furry');
}
and in consol when I want to call function I got this:
Uncaught TypeError: Cannot read property 'x' of undefined
https://jsfiddle.net/mdx3w24c/
At this state after calling function showFurry and showCoin I should receive this:

Try removing the functions showFurry and showCoin. Then, make a function in the Game class that will do those things.
Game.prototype.start = function() {
this.board[this.position(this.coin.x, this.coin.y)].classList.add('coin');
this.board[this.position(this.furry.x, this.furry.y)].classList.add('furry');
};
Then when you start your game, instead of calling showFurry and showCoin you can call Game.start();
var game = new Game();
game.start();
Also your Coin constructor set random values to x & y, but the furry construct set them both to 0, then set them to random values in the showFurry function. In this fiddle, I've moved that to the constructor.

Related

Implementing Circle constructor in JavaScript, without ES6 classes

Eric Faust typed up a Circle constructor function in the following article about ES6 classes: https://hacks.mozilla.org/2015/07/es6-in-depth-classes/
I was wondering:
Why does he use defineProperty? Can't we just implement the behavior straight into the constructor. For example: Circle.draw = function draw() {..}
Why use get / set instead of just having the state in normal properties: Circle.circleCount?
Which properties should be implemented directly on new instance objects, via this in constructor, vs on Constructor.prototype (given how both make the properties available to new instances)?
Eric's code:
function Circle(radius) {
this.radius = radius;
Circle.circlesMade++;
}
Circle.draw = function draw(circle, canvas) { /* Canvas drawing code */ }
Object.defineProperty(Circle, "circlesMade", {
get: function() {
return !this._count ? 0 : this._count;
},
set: function(val) {
this._count = val;
}
});
Circle.prototype = {
area: function area() {
return Math.pow(this.radius, 2) * Math.PI;
}
};
Object.defineProperty(Circle.prototype, "radius", {
get: function() {
return this._radius;
},
set: function(radius) {
if (!Number.isInteger(radius))
throw new Error("Circle radius must be an integer.");
this._radius = radius;
}
});
let c1 = new Circle(10);
console.log(c1.area());
console.log(Circle.circlesMade);
My version:
function Circle(_radius) {
this.radius = _radius;
Circle.draw = function draw(circle, canvas) {
/* Canvas drawing code */
};
!Circle.circleCount ?
(Circle.circleCount = 1) //First construction
:
(Circle.circleCount = Circle.circleCount + 1);
this.area = function area() {
return Math.pow(this.radius, 2) * Math.PI;
};
}
let c1 = new Circle(10);
console.log(Circle.circleCount);
console.log(c1.area());
let c2 = new Circle(20);
console.log(Circle.circleCount);
console.log(c2.area());
I prefer using the constructor's prototype object for functions that will be shared with instances rather than defining a new function on each instance. This can save memory because you only have one instance of the function rather than a new copy for each instance you create.
You can also define circleCount on the prototype since all instances need the same number. You just need to be a little careful changing it to make sure you don't create a shadowed property on each instance. Then each instance can directly provide the count via the prototype chain.
Doing that complicates the function, but simplifies the rest of you code:
function Circle(_radius) {
this.radius = _radius;
// creating an instance increments the count for everyone
Circle.prototype.circleCount++ // not this.circleCount++ which will create a new property on instance
}
Circle.prototype.draw = function draw(circle, canvas) {
/* Canvas drawing code */
};
Circle.prototype.area = function() {
return Math.pow(this.radius, 2) * Math.PI;
}
Circle.prototype.circleCount = 0
let c1 = new Circle(10);
console.log(c1.circleCount);
console.log(c1.area());
let c2 = new Circle(20);
console.log(c2.circleCount);
console.log(c2.area());
Also, regarding the question about Object.defineProperty. It looks like he's using that so he can set getter and setter functions rather than just returning the property. You could do that with area with something like:
Object.defineProperty(Circle.prototype, "area", {
get: function() {
return Math.pow(this.radius, 2) * Math.PI;
}
});
Which would allow you to access area as if ti were a property on each instance:
c2.area // instead of a function c2.area()
You could set area as a property directly, but then if you change the radius you need to also change the area. I guess which is best depends on whether radius will ever change.

GreenSock, tween function arguments

I got two functions called on mouse events:
function menuBtnOver(e){
var b = e.data;
b.setPosition(b.x, b.y+5);
}
function menuBtnOut(e){
var b = e.data;
b.setPosition(b.x, b.y-5);
}
and:
setPosition:function(x, y) {
if(!x) x = 0;
if(!y) y = 0;
this.element.css("left", x);
this.element.css("top", y);
}
element property is a jQuery object.
It is working ok but i want to animate this. How can i do this with TweenLite?
I've tried following code:
function menuBtnOver(e){
TweenLite.to(e.data, 1, {top:500});
}
As well as this:
function menuBtnOver(e){
TweenLite.to(e.data.getElement(), 1, {top:500});
}
and many other combinations but none of them worked.
Only on method which partially work is this:
function menuBtnOver(e){
TweenLite.to(e.data, 1, {y:400, onUpdate:e.data.setPosition, onUpdateParams:[e.data.x, e.data.y]});
}
But it work only on fist button when I roll over and (after any time) roll out, it moves directly to given position (without tween) and then the tween goes forever giving me error each time(at least - I couldn't get any errors or anything with other attempts).
Uncaught TypeError: Cannot read property 'css' of undefined
at: this.element.css("left", x);
Update
I figured out what is going on.
I've changed code as so:
function menuBtnOver(e){
TweenLite.to(e.data, 1, {y:400, onUpdate:e.data.setPosition, onUpdateParams:[e.data.x, e.data.y], onUpdateScope:e.data});
}
But the problem with this is that arguments to update function which I set to e.data.y/x aren't dynamic references and always stay as those exact values from menuBtnOver state. So the tween works if i change setPosition function to:
setPosition:function(x, y) {
if(!x) x = 0;
if(!y) y = 0;
this.element.css("left", this.x);
this.element.css("top", this.y);
}
But obviously this is not what I want to do.
So I have option to make something like this:
MenuButton.prototype = {
setPosition:function(x, y) {
if(!x) x = 0;
if(!y) y = 0;
this.x = x; this.y = y;
this.element.css("left", x);
this.element.css("top", y);
},
updatePosition:function(){
this.element.css("left", this.x);
this.element.css("top", this.y);
}
}
function menuBtnOver(e){
TweenLite.to(e.data, 1, {y:400, onUpdate:e.data.updatePosition, onUpdateScope:e.data});
}
Or define external update function in similar manner. The question still stays the same so is there a simple way to do this simpler. Does GS tween has any mechanic which automate this process?
Thanks to everyone for attention :)
this in setPosition is referring to that function and not the this of the onClick event.
you need to do pass this to setPosition. As in the example below, where I passed it as self in the function setPosition.
function menuBtnOver(e){
var b = e.data;
b.setPosition(this, b.x, b.y+5);
}
function menuBtnOut(e){
var b = e.data;
b.setPosition(this, b.x, b.y-5);
}
and:
setPosition:function(self, x, y) {
if(!x) x = 0;
if(!y) y = 0;
self.element.css("left", x);
self.element.css("top", y);
}
this always refernces the function in which is was called. as you can read about her. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
So you can pass this in a function as a variable.

Getting object fields and calling object functions in JavaScript

I just started fiddling around with JavaScript. Coming from Java and OO PHP things are getting weirder with every step :)
This is my introduction project to javascript in which I've set out to program multiplayer working version of Settlers of Catan. Code below is an attempt to store cube coordinates of N sized hexagonal map tiles in an array.
I've read you declare object in javascript by assigning functions to variables.
var Tile = function (x, y, z) {
this.x = x;
this.y = y;
this.z = z;
};
var Map = function () {
var grid = [];
function generate_map(radius) {
for (width = -radius; width <= radius; width++) {
var r1 = Math.max(-radius, -width - radius);
var r2 = Math.min(radius, -width + radius);
for (r = r1; r <= r2; r++) {
grid.push(new Tile(width, r, -width - r));
}
}
}
};
I've tried instantiating new Map object, calling its only function and outprinting the resulting values stores in grid[] array. But for each loop is not playing nice :( I get the unexpected identifier.
var main = function () {
var basic_map = new Map();
basic_map.generate_map(3);
for each (var tile in basic_map.grid) {
console.log(tile.x, tile.y, tile.z);
}
};
main();
I am fully aware this is one of those face palm errors, but help would nevertheless be appreciated, cheers!
Change this:
function generate_map(radius) {
...to this:
this.generate_map = function(radius) {
Edit: there are actually more issues than I at first realized.... :)
A few other tips:
First, I would recommend changing:
var Tile = function (x, y, z) {
...to simply be:
function Tile(x, y, z) {
(the same goes for Map). Your current solution works fine, but it's not very idiomatic, and until ES6 there was nothing in the spec that would cause var Tile = function to cause the resulting function's 'name' property to be set to "Tile", which is useful when it comes to debugging. I recently wrote another answer that delves a bit more into the differences between, e.g., function Foo() {} and var Foo = function() {}.
Second, you probably want to rename Map to something else. Map is a core part of ES6 now (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map).
Third, even though you can create your generate_map function using this.generate_map, you may want to move it to the Map's prototype. Also, since you need to expose the grid value, you want to store it as a property, rather than a local variable scoped to the NewMapName constructor. E.g.,:
function NewMapName() {
this.grid = [];
}
NewMapName.prototype.generateMap = function(radius) {
// you can access the grid here via `this.grid`
...
};
By moving it to the prototype, that means all instances of NewMapName will share the same function reference, rather than it being created over-and-over-and-over (although maybe you really only create it once? Either way, it's more idiomatic, at a minimum). Note that I took some liberties with the "camelCasing" here (see the last point).
Fourth, your generateMap implementation is leaking some global variables (width and r, since you don't declare them with var). I would change that to this:
NewMapName.prototype.generateMap = function(radius) {
for (var width = -radius; width <= radius; width++) {
var r1 = Math.max(-radius, -width - radius);
var r2 = Math.min(radius, -width + radius);
for (var r = r1; r <= r2; r++) {
grid.push(new Tile(width, r, -width - r));
}
}
};
Fifth, your loop is kind of broken. I would refactor that as follows:
var main = function () {
var basicMap = new NewMapName();
basicMap.generateMap(3);
basicMap.grid.forEach(function(tile) {
console.log(tile.x, tile.y, tile.z);
});
};
main();
Lastly, and probably most minor, is that in JavaScript-land, camelCase is far more dominant that snake_case, so generate_map might be "better" as generateMap.

Html5 canvas how to generate multiple images

Im making a simple zombie game in html5 canvas and wanted to know how to create a zombie every x seconds in a random place? so far i have
var zombies = new Array();
function SummonZombies (){
TotalZombies++;
zombies[TotalZombies] = new Image();
zombies[TotalZombies].src = 'images/monster.png';
ctx.drawImage(zombies[TotalZombies], zombie_x, zombie_y);
}
Only one zombie is being created with this? how would i get it to generate more.
First of all, where are you declaring the variable TotalZombies?
Try something like this :
var zombies = new Array();
for (var i = 0; i < 100; i++) {
var zombie = new Image();
zombie.src = 'images/monster.png';
ctx.drawImage(zombie, Math.floor((Math.random()*100)+1), Math.floor((Math.random()*100)+1));
zombies.push(zombie);
}
This will create 100 zombies, with random x and y positions between 1 and 100. It will add each zombie to the zombies array after they have been instantiated.
You should iterate through zombies array, and invoke drawImage() on everyone.
Extra tip: remember to change x and y after all iteration.
You must separate a Zombi from your zombies :
create a class that will describe what a Zombi is, and only after you will define a collection of such lovely guys and girls :
// This Class defines what a Zombi is.
function Zombi(x,y) {
this.x = x;
this.y = y;
}
var ZombiImage = new Image();
ZombiImage.src = "images/monster.png";
// image of a zombi is shared amongst all zombies, so it is
// defined on the prototype
Zombi.prototype.image = ZombiImage;
// draw the zombi on provided context
Zombi.prototype.draw = function(ctx) {
ctx.drawImage(this.image, this.x, this.y);
}
Now for the collection :
// This class defines a collection of Zombies.
function Zombies() {
this.zombies = [];
}
// summons a zombi at a random place. returns the summoned zombi.
myZombies.prototype.summon() {
var randX = Math.random()*100;
var randY = Math.random()*100;
return this.summonAt(randX, randY);
}
// summons a zombi at x,y. returns the summoned zombi.
myZombies.prototype.summonAt = function (x,y) {
var newZombi = new Zombi(x,y);
this.zombies.push();
return newZombi;
}
// draws all zombies on provided context.
myZombies.prototype.drawAll = function (ctx) {
var i=0;
var __zombies = this.zombies;
for (;i<__zombies.length; i++) {
__zombies[i].draw(ctx);
}
}
// collection of all zombies for your game.
var zombies = new Zombies();
// here you can call zombies.summon(); or zombies.drawAll();
// and even zombies.summonAt(x,y);
In fact the code above is simplified : you must handle the onload event of the image to start the game only after the image was loaded.
But you should get the idea : separate the issues (handle ONE zombi vs a collection of zombies) will get you faster to your goal.
With this -simple- design, you'll be able to easily add-up behaviour to your zombies.
Just one more example in which i will add the seekBrain and walk behaviour :
// This Class defines what a Zombi is.
function Zombi(x,y) {
this.x = x;
this.y = y;
this.dirX = 0 ; // direction X
this.dirY = 0; // direction Y
this.speed = 0.1; // common speed for all zombies
}
// have the zombi seek the brain located at (x,y)
Zombi.prototype.seekBrain = function (x,y) {
this.dirX = (x - this.x );
this.dirY = (y - this.y );
// normalize direction
var norm = Math.sqrt( this.dirX*this.dirX + this.dirY*this.dirY );
this.dirX/=norm;
this.dirY/=norm;
}
// Have the zombi walk in its current direction
Zombi.prototype.walk = function() {
this.x += this.dirX * this.speed;
this.y += this.dirY * this.speed;
}
// image and draw remains the same
And now you might want for your collection :
// makes all zombies walk.
Zombies.walkAll = function() {
var i=0;
var __zombies = this.zombies;
for (;i<__zombies.length; i++) {
__zombies[i].walk();
}
}
// constructor, summon, summonAt, and drawAll remains the same.
So to summon a zombi at random place every xxx ms, do something like :
// summons a zombi at a random place every 2 seconds (==2000 ms)
setTimeInterval(2000, function() { zombies.summon(); } );
now, if hero.x and hero.y are what we guess, you can do :
// Have a random zombi hunt for hero's brain every 2 seconds
setTimeInterval(2000, function() {
var which = Math.floor(zombies.zombies.length * Math.random());
zombies.zombies[which].seekBrain(hero.x, hero.y);
} );
provided you call to zombies.walkAll(); and zombies.drawAll(); on a regular basis, you've got the start of a game ! (i love so much zombies :-) )

jQuery - setInterval issue

I am using jQuery to generate and add a random amount of Clouds to the Header of the page and move them left on the specified interval. Everything is working fine, execpt the interval only runs once for each Cloud and not again. Here is my code:
if(enableClouds) {
var cloudCount = Math.floor(Math.random() * 11); // Random Number between 1 & 10
for(cnt = 0; cnt < cloudCount; cnt++) {
var cloudNumber = Math.floor(Math.random() * 4);
var headerHeight = $('header').height() / 2;
var cloudLeft = Math.floor(Math.random() * docWidth);
var cloudTop = 0;
var thisHeight = 0;
var cloudType = "one";
if(cloudNumber == 2) {
cloudType = "two";
}else if(cloudNumber == 3) {
cloudType = "three";
}
$('header').append('<div id="cloud' + cnt + '" class="cloud ' + cloudType + '"></div>');
thisHeight = $('#cloud' + cnt).height();
headerHeight -= thisHeight;
cloudTop = Math.floor(Math.random() * headerHeight);
$('#cloud' + cnt).css({
'left' : cloudLeft,
'top' : cloudTop
});
setInterval(moveCloud(cnt), 100);
}
function moveCloud(cloud) {
var thisLeft = $('#cloud' + cloud).css('left');
alert(thisLeft);
}
}
Any help is appreciated!
This is the way to go:
setInterval((function(i){
return function(){
moveCloud(i);
};
})(cnt), 100);
Engineer gave you the code you need. Here's what's happening.
The setInterval function takes a Function object and an interval. A Function object is simply an object that you can call, like so:
/* Create it */
var func = function() { /* ... blah ... */};
/* Call it */
var returnVal = func(parameters)
The object here is func. If you call it, what you get back is the return value.
So, in your code:
setInterval(moveCloud(cnt), 100);
you're feeding setInterval the return value of the call moveCloud(cnt), instead of the the function object moveCloud. So that bit is broken.
An incorrect implementation would be:
for(cnt = 0; cnt < cloudCount; cnt++) {
/* ... other stuff ... */
var interval = setInterval(function() {
moveCloud(cnt);
}, 100);
}
Now, you're feeding it a function object, which is correct. When this function object is called, it's going to call moveCloud. The problem here is the cnt.
What you create here is a closure. You capture a reference to the variable cnt. When the function object that you passed to setInterval is called, it sees the reference to cnt and tries to resolve it. When it does this, it gets to the variable that you iterated over, looks at its value and discovers that it is equal to cloudCount. Problem is, does not map on to a Cloud that you created (you have clouds 0 to (cloudCount -1)), so at best, nothing happens, at worst, you get an error.
The right way to go is:
setInterval((function(i){
return function(){
moveCloud(i);
};
})(cnt), 100);
This uses an 'immediate function' that returns a function. You create a function:
function(i){
return function(){
moveCloud(i);
};
}
that returns another function (let's call it outer) which, when called with a value i, calls moveCloud with that value.
Then, we immediately call outer with our value cnt. What this gives us is a function which, when called, calls moveCloud with whatever the value of cnt is at this point in time. This is exactly what we want!
And that's why we do it that way.

Categories

Resources