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);
});
Related
In a class constructor, why are const and let not used ?
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
this.area = width * height;
this.widthScaled;
this.heightScaled;
}
scale(x) {
this.widthScaled = this.width * x;
this.heightScaled = this.height * x;
}
}
Why shouldn't it be :
const this.area = width * height;
let this.widthScaled;
?
That's because you're not creating a new variable, you're just adding/changing a property to the current instance (this).
To elaborate with an example, Imagine this:
const x = {};
x.prop = 10;
as you can see, we added prop as a property to x object. Same thing happens when you use this;
This.width = width simply adds a property named width to current Rectangle instance (with the value inside width parameter), so no new variable is created.
As previous speakers said, you are not creating new variable but modify alredy existing one. Your constructor is 'function' that creates new object and inside it you modify it's(this keyword) properties:
constructor(width, height) {
this.width = width;
this.height = height;
this.area = width * height;
this.widthScaled;
this.heightScaled;
}
The equivalent in the javascript without using classes would look like this:
var myObject = {};
myObject.width = width;
myObject.height = height;
myObject.area = width * height;
myObject.widthScaled;
myObject.heightScaled;
But because you are using class you can create it like this and create many rectangles:
var rectangle1 = new Rectangle(3,4);
var rectangle2 = new Rectangle(5,10);
const rectangle3 = new Rectangle(22,11);
This question already has answers here:
JavaScript: Collision detection
(10 answers)
Closed 2 years ago.
What a good practice to detect collision of 2 objects(walls). Yes, not just detection, but further displacement so that objects do not enter each other. That is, so that when they collide, they rest against each other, but do not enter.
CODE
class WallObj {
constructor(obj) {//x, y, w, h, bern ,thru) {
this.x = obj.x
this.y = obj.y
this.w = obj.w
this.h = obj.h
this.bern = obj.bern
this.thru = obj.thru
this.hide = obj.hide
this.id = obj.id
}
collusionWall(startPosition, endPosition) {
var xS = startPosition[0]
var x = endPosition[0]
if (xS - x > 0)
if (x)
// if wall behind point
if (this.x < startPosition[0])
return endPosition
else if (this.x + this.w < x)
return endPosition
return endPosition
// return [this.x, endPosition[1]]
}
}
a naive approach would be to check x and y :
let ctx = mycan.getContext("2d");
let objects = [];
class player
{
constructor()
{
this.position = {x:50,y:50};
this.color = "blue";
this.size = 32;
this.stop= false;
this.prevpos=this.position;
window.addEventListener("keydown",(e)=>{
if(this.stop) this.position=this.prevpos;
else this.displacement(e);
});
}
displacement(e)
{
this.prevpos = this.position;
this.position.x+=(e.key=="ArrowRight");
this.position.x-=(e.key=="ArrowLeft");
this.position.y+=(e.key=="ArrowDown");
this.position.y-=(e.key=="ArrowUp");
}
draw()
{
ctx.fillStyle = this.color; ctx.fillRect(this.position.x,this.position.y,this.size,this.size);
}
};
class wall
{
constructor(posx,posy)
{
this.position = {x:posx,y:posy};
this.color = "red";
this.size = 32;
}
draw(){
ctx.fillStyle = this.color;
ctx.fillRect(this.position.x,this.position.y,this.size,this.size);
}
};
for(let i = 0; i<mycan.width;i+=32)
{
objects.push(new wall(i,0));
objects.push(new wall(i,mycan.height-32));
}
for(let j = 0; j<mycan.height;j+=32)
{
objects.push(new wall(0,j));
objects.push(new wall(mycan.width-32,j));
}
let playr=new player;
let collision = (colider)=>{
let colx = false;
let coly = false;
/*******************************************************
here we check if the top left point from our current
wall object is inferior to the top left point of our
player and if the top rignt point of the wall object is
superior to the player top left point.
we need to repeat this for the player top right point
(so we compare the player top right point is superior
to the wall top left point and inferior to the wall
top right point)
then we repeat this for y
*******************************************************/
for(let object of objects)
{
colx = (
(
(object.position.x<=colider.position.x) &&
(
(object.position.x+object.size)>=
(colider.position.x)
)
)||(
(
(colider.position.x+colider.size)>=object.position.x) &&
(
(colider.position.x+object.size)<=(object.position.x+object.size)
)
)
)
coly = (
(
(object.position.y<=colider.position.y) &&
(
(object.position.y+object.size)>=
(colider.position.y)
)
)||(
(
(colider.position.y+colider.size)>=object.position.y) &&
(
(colider.position.y+object.size)<=(object.position.y+object.size)
)
)
)
if(colx&&coly) return true;
}
return false;
};
setInterval(()=>{
ctx.clearRect(0,0,mycan.width,mycan.height);
playr.stop = collision(playr);
playr.draw();
for(let obj of objects)
obj.draw();
},1000/30);
<canvas id="mycan" width=400 height=250></canvas>
à better approach would be to cut your 2d world into zones where the density of objects that can collide is more or less important (a quadtree).
like so :
and an easier way would be to look if the colided object is in a sphere (this would mean that walls have a spheric collision and could hardly be represented as a square) but we could just say that the player is a sphere and check if something has entered his radius.
https://studiofreya.com/3d-math-and-physics/sphere-vs-aabb-collision-detection-test/
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);
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 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);