This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 4 years ago.
I'm fairly new to JavaScript though I do have some programming experience with Python.
My problem is, that I do not seem understand the concept of namespace in JS since it appears to be different than in Python. This is my code:
function snake(x, y) {
// x and y coordinate of square (topleft)
this.x = x;
this.y = y;
// reference to div object 'box2'
this.boxid = "#box";
this.box = document.getElementById(this.boxid);
// attempts to move the box by args
this.move = function (speedx, speedy) {
var m = 50;
// check if the box is within the container, moves if true
if ((this.x+(speedx*m))<=150 && (this.y+(speedy*m))<=150 &&
(this.y+(speedy*m))>=0 && (this.x+(speedx*m))>=0) {
this.x = this.x + speedx*m;
this.y = this.y + speedy*m;
}
}
// called every frame to update position of the box
this.update = function () {
$(this.boxid).css({top: this.y, left: this.x});
}
}
var snakeObj = new snake(100, 100);
var t = setInterval(s.update, 100);
When hitting one of the four arrow keys, the move() function is being executed with the correct parameters.
Now the way code is shown up there, JS tells me that the x and y values in the update() function are "undefined". But as soon as I change them from this.x and this.y to snakeObj.x and snakeObj.y, as well as this.boxid to "#box", everything works perfectly.
I would like to understand why the update() function can't "access" the values from the object while the move() function is perfectly fine with it.
Just for clarification, the working code looks like this:
function snake(x, y) {
// x and y coordinate of square (topleft)
this.x = x;
this.y = y;
// reference to div object 'box2'
this.boxid = "#box";
this.box = document.getElementById(this.boxid);
// attempts to move the box by args
this.move = function (speedx, speedy) {
var m = 50;
// check if the box is within the container, moves if true
if ((this.x+(speedx*m))<=150 && (this.y+(speedy*m))<=150 &&
(this.y+(speedy*m))>=0 && (this.x+(speedx*m))>=0) {
this.x = this.x + speedx*m;
this.y = this.y + speedy*m;
}
}
// called every frame to update position of the box
this.update = function () {
$("#box).css({top: snakeObj.y, left: snakeObj.x});
}
}
var snakeObj = new snake(100, 100);
var t = setInterval(s.update, 100);
It's because you've got another this value; the one for the inside function.
In JavaScript, every function gets its own this value. Functions are bound at call-time, which means that if you don't call them via the attribute access they're called without the correct this value. A common workaround is to set var that = this; in your object's constructor so you can use the original value via closure from a function defined in the constructor.
Another workaround, if you don't mind dropping support for IE11, is to use an ES6 arrow function like so:
function snake(x, y) {
// x and y coordinate of square (topleft)
this.x = x;
this.y = y;
// reference to div object 'box2'
this.boxid = "#box";
this.box = document.getElementById(this.boxid);
// attempts to move the box by args
this.move = (speedx, speedy) => (function (speedx, speedy) {
var m = 50;
// check if the box is within the container, moves if true
if ((this.x+(speedx*m))<=150 && (this.y+(speedy*m))<=150 &&
(this.y+(speedy*m))>=0 && (this.x+(speedx*m))>=0) {
this.x = this.x + speedx*m;
this.y = this.y + speedy*m;
}
}).call(this, speedx, speedy);
// called every frame to update position of the box
this.update = () => $("#box").css({top: this.y, left: this.x});
}
var snakeObj = new snake(100, 100);
var t = setInterval(s.update, 100);
Related
This question already has answers here:
Javascript Array of Instances of a class
(5 answers)
Get all instances of class in Javascript
(6 answers)
how to get all objects of a given type in javascript
(2 answers)
Looping through all instances of a javascript object
(5 answers)
Closed 1 year ago.
I am making a platformer game and I am wondering if there is a simpler way to store objects in arrays as I use the arrays to check for collision.
Is there any type of array a class can automatically have?
//This is with making my own array
var obstacleArray = [];
class Obstacle {
constructor(x, y) {
this.x = x,
this.y = y,
this.width = 50,
this.height = 50
}
addToArray() {
obstacleArray.push(this);
}
}
obstacle1 = new Obstacle(0, 0);
obstacle2 = new Obstacle(50, 0);
obstacle1.addToArray();
obstacle2.addToArray();
for (let i = 0; i < obstacleArray.length;i++) {
//check for collision
}
Is there some kind of built-in array for a number of variables a class owns so I can quickly check for collision without having to call the addToArray function for every obstacle?
You can always push to the array in the constructor
Job done :p
Optional but I recommend it: Use a class static to hold the array
class Obstacle {
static obstacleArray = [];
constructor(x, y) {
this.x = x;
this.y = y;
this.width = 50;
this.height = 50;
Obstacle.obstacleArray.push(this);
}
}
obstacle1 = new Obstacle(0, 0);
obstacle2 = new Obstacle(50, 0);
console.log(Obstacle.obstacleArray);
Another interesting option may be to use a Set instead of an array
class Obstacle {
static obstacles = new Set;
constructor(x, y) {
this.x = x;
this.y = y;
this.width = 50;
this.height = 50;
Obstacle.obstacles.add(this);
}
remove() {
Obstacle.obstacles.delete(this);
}
}
obstacle1 = new Obstacle(0, 0);
obstacle2 = new Obstacle(50, 0);
[...Obstacle.obstacles.keys()].forEach(obstacle => {
console.log(obstacle.x);
});
// you can remove an obstacle easily
console.log('removed 1');
obstacle1.remove();
[...Obstacle.obstacles.keys()].forEach(obstacle => {
console.log(obstacle.x);
});
Ok, so I wrote some code for a JS game. The code itself works, but isn't in proper OOP form. In the class "Enemy" I need to reference variables and a method from the "Player" class. Look in the "Collision" method, where the variables are referenced. Notice that I get the data specifically from the new instance of "Player" called "player" at the end of the script. For OOP, how am I suppose to share information between these two classes?
Thanks!
var Player = function() {
this.x = 15;
this.y = 15;
};
Player.prototype.reset = function() {
this.x = 200; // reset to this
this.y = 320; // reset to this
};
var Enemy = function() {
this.x = 25;
this.y = 30;
};
Enemy.prototype.collision = function() {
if (player.x >= this.x - 35 & player.x <= this.x + 35) { // check column
if (player.y >= this.y - 30 & player.y <= this.y + 30) { // check row
player.reset(); // calls player method "reset"
}
}
};
// Start Game
setEnemies();
var player = new Player();
in javascript functions can take arguments
solution to your problem could be passing instance of Player to method collision
as #Álvaro Touzón mentioned, a good practice would be to use inheritance as Enemy and Player in your code are now basically the same
also, you could read about ES6 classes, which make programming a bit easier, however they still rely on prototype inheritance which makes them just a syntactic sugar
If you want to use OOP, then perhaps this will help you.
Add helper extend function
function extend(current, base) {
for (var key in base) {
if (base.hasOwnProperty(key)) {
current[key] = base[key];
}
}
current.prototype = Object.create(base.prototype);
};
Create class Avatar as #Álvaro Touzón suggested
var Avatar = (function () {
function Avatar (x, y) {
this.x = x;
this.y = y;
}
Avatar.prototype.reset = function() {
return this;
};
Avatar.prototype.collision = function(object) {
if (object.x >= this.x - 35 & object.x <= this.x + 35) { // check column
if (object.y >= this.y - 30 & object.y <= this.y + 30) { // check row
object.reset(); // calls object method "reset"
}
}
};
return Avatar;
}());
Class Player and Enemy
var Player = (function (superClass) {
extend(Player, superClass);
function Player (x, y) {
superClass.apply(this, arguments);
}
Player.prototype.reset = function() {
this.x = 200; // reset to this
this.y = 320; // reset to this
return this;
};
return Player;
}(Avatar));
var Enemy = (function (superClass) {
extend(Enemy, superClass);
function Enemy (x, y) {
superClass.apply(this, arguments);
}
return Enemy;
}(Avatar));
Create Game
var Game = (function () {
Game.prototype.player = new Player(15, 15);
Game.prototype.enemys = [
new Enemy(25, 30),
new Enemy(10, 30),
];
function Game () {
// setup
}
Game.prototype.start = function() {
for (var i = 0; i < array.length; i++){
var enemy = this.enemys[i];
this.player.collision(enemy);
}
return this;
};
return Game;
}());
Use:
var game = new Game();
game.start();
How it works
You have a group of objects - enemies and the object - player, all of them have the ability to calculate collision between each other, because common ancestor. Every time you call the start of the game will be calculated collision. I would add setInterval in game.start for calculated collision but this complicate the code.
I cannot say how you want to run your game but in general, to share data I always believe in making singleton classes and use it in different objects. This way we can do better error handling also. In js, there are no singleton classes as such to say. But you can always make simple js modules like below:
var abc = (function(){
var abc = "someval"; //private variable
var setAbc = function() {
//some logic
}
var getAbc = function() {
//some logic
}
var publicMethod = function {
//some logic
}
return {
setAbc: setAbc,
getAbc: getAbc,
publicMethod: publicMethod
}
})();
It's non of the buisness of the Enemy, to mutate/reset the Player. Nor is it the buisness of the Player, to wich Position it is reset to.
These things should be done by the game in the main game loop, and the collision-method should only determine wether this Element has hit the passed Element.
//manages the bounding-Box / Collisions,
//also pretty much everything related to (basic) rendering, like Assets/Image or DOM-Node, ... but since you've not included that in your code, I can't adapt it.
class Element{
constructor(config){
let {x, y, width, height, hitBoxOffsetX, hitBoxOffsetY} = config || {};
this.x = +x || 0;
this.y = +y || 0;
this._hitBoxOffsetX = +hitBoxOffsetX || 0;
this._hitBoxOffsetY = +hitBoxOffsetY || 0;
this.width = +width || 0;
this.height = +height || 0;
}
//bounding box
get left(){ return this.x + this._hitBoxOffsetX }
get right(){ return this.left + this.width }
get top(){ return this.y + this._hitBoxOffsetY }
get bottom(){ return this.top - this.height }
collision(other){
return this.right > other.left && this.left < other.right &&
this.top > other.bottom && this.bottom < other.top;
}
}
//everything that can somehow be hit, extends Element
class Player extends Element {
constructor(){
super({
hitBoxOffsetX: -35, //hitBox starts 35px to the left of this.x
hitBoxOffsetY: -30, //hitBox starts 30px to the top of this.y
width: 70, //width of the hitBox
height: 60 //height of the hitBox
});
}
}
class Enemy extends Element {
constructor(){
//since I have no idea about the dimensions of these Entities
super();
}
}
//and walls, for example
class Wall extends Element {
constructor(x, y, width, height){
super({
x, y,
width: +width || 20,
height: +height || 20
});
}
}
And the mentioned collision-checking and resetting happens in the main game-loop, but since I don't know how your game-loop looks like, here some pseudocode:
update(){
requestAnimationFrame(update);
//move everything
//collision-checks
//if your player has some sort of weapon, maybe you want to first check
//wether it has "killed" some enemies,
//before checking wether an enemy has hit your player.
var hitEnemy = enemies.find(enemy => enemy.collision(player));
//btw. Bullets would also be "enemies"
if(hitEnemy){
//and you probably don't want to
player.x = currentLevel.playerResetPosition.x;
player.y = currentLevel.playerResetPosition.y;
//destroy hitEnemy
//maybe mark the player as blinking/non-hitable for a few seconds?
}
//render everything
}
I have a Javascript question, that might be obvious, but I just can't seem to find the solution for it, and I don't know how to solve it.
(Also, I'm still pretty new to coding)
So I'm writing a patrol function for squares in my game, and for now I started out with just making the square move one way. Later on I will make it patrol back and forth. That's why I put the move function in the draw function.
I want the move function to be reusable for several squares, but I can't seem to make a general move function work. However, I can make a move function specifically for a certain square, work.
Can anyone tell me why this works:
var square = 16;
var posX = 32;
var posY = 32;
function moveSquare() {
for (i = 0; i < 10; i++) {
posX++;
}
}
function draw() {
var redSquare = { x: posX, y: posY, w: square, h: square, color: "red" };
ctx.fillStyle = redSquare.color;
rect(redSquare.x,redSquare.y,redSquare.w,redSquare.h);
moveSquare();
}
And this doesn't:
var square = 16;
var posX = 32;
var posY = 32;
function move(pos) {
for (i = 0; i < 10; i++) {
pos++;
}
}
function draw() {
var redSquare = { x: posX, y: posY, w: square, h: square, color: "red" };
ctx.fillStyle = redSquare.color;
rect(redSquare.x,redSquare.y,redSquare.w,redSquare.h);
move(posX);
}
By the way, I defined the rect function elsewhere, but I figured it wasn't important to include.
Hope you can help
The value passed to the function move is being passed by value, not by reference.
So the pos inside move is private to the move function.
The pos variable will be a copy of posX, so no matter what you do to it in the move function, the global posX will not be affected.
Consider the code:
var x = 5;
function move(x)
{
x++;
console.log("In function x is: " + x);
}
console.log("Outside function, before call x is: " + x);
move(x);
console.log("Outside function, after call x is: " + x);
This outputs:
"Outside function, before call x is: 5"
"In function x is: 6"
"Outside function, after call x is: 5"
The function move has it's own private copy x.
Look into pass by reference, pass by value and variable scope.
I need your help.
my question is how to make the inner class properties take the values of the parent class properties
function drawSvg(element)
{
this.x= 0;
this.y=0;
this.strockeColor="#FFF";
this.fillColor= "#000";
this.darwCircle= function()
{
this.x = parent.x;//here what I have to do to make the x of darwCircle take the vale of drawSvg x
}
}
in my example above I do not know what is the code that I should put to do that ??
A common way of doing this is including:
in the parent scope write
var self = this;
and in the child scope you can write
this.x = self.x; //now you can take the value of the parent
and for more clarification here is the complete example
function drawSvg(element)
{
this.x= 200;
this.y=0;
this.strockeColor="#FFF";
this.fillColor= "#000";
this.lineWidth =10;
this.set = function(x, y, strockColor, fillColor, lineWidth )
{
this.x= x;
this.y= y;
this.strockeColor= strockColor;
this.fillColor= fillColor;
this.lineWidth= lineWidth;
}
self = this;// look here
this.darwCircle= function()
{
this.x = self.x+2000; // look here
}
}
var d = new drawSvg("ddfds");
var dc = new d.darwCircle();
console.log(dc.x);
I'm trying to draw a line using ctx.lineTo in loop.
I have small function in my prototype
this.draw = function(ctx)
{
ctx.beginPath();
trace(trail.join(' '));
for(var i=0;i<trail.length;i++)
{
// ctx.lineTo(trail[i].x,trail[i].y);
}
ctx.stroke();
}
When I run this, I receive some points traced ([389.272, 722.798] [392.583, 25.069]...) but I see nothing (very surprising)
When I remove the comment from ctx.lineTo, it fails and my trace returns [NaN, NaN] [NaN, NaN].... Constants in drawing function works perfectly (and my points doesn't change), but I need value from variables...
What's wrong? Problem occurs only on Firefox
edit:
trace is simple text assignment to html object
trail is an array of points which are simple objects
function point(x,y)
{
this.x = x;this.y = y;
this.toString = function()
{
var xs=this.x.toFixed(3);
var ys=this.y.toFixed(3);
var xs=" ".substring(0,8-xs.length)+xs;
var ys=" ".substring(0,8-ys.length)+ys;
return "["+xs+","+ys+"]";
}
}
There's still not enough information to really answer this, so I'm going to just hazard a guess: change the "point" constructor as follows:
function point(x,y)
{
this.x = x - 0; this.y = y - 0;
this.toString = function()
{
var xs=this.x.toFixed(3);
var ys=this.y.toFixed(3);
var xs=" ".substring(0,8-xs.length)+xs;
var ys=" ".substring(0,8-ys.length)+ys;
return "["+xs+","+ys+"]";
}
}
The idea is to make sure that "x" and "y" are actually numbers and not strings. You could also do this:
this.x = x - 0; this.y = y - 0;
if (isNaN(this.x) || isNaN(this.y)) {
alert("NaN! NaN! x is " + x + " y is " + y);