best design practice to separate common code? - javascript

I have a card class:
function Card() {
this.image = new Image();
this.x = 0;
this.y = 400;
this.initialX = 0;
this.initialY = 0;
this.scaleFactor = 4;
this.setImage = function(ii){
this.image.src = ii;
};
this.getWidth = function(){
if (this.image == null){
return 0;
}
return this.image.width / this.scaleFactor;
}
this.getHeight = function(){
if (this.image == null){
return 0;
}
return this.image.height / this.scaleFactor;
}
this.pointIsInCard = function(mx, my){
if (mx >= this.x && mx <= (this.x + this.getWidth()) && my >= this.y && my <= (this.y + this.getHeight()))
{
return true;
}
else{
return false;
}
};
};
I then have a deck class:
function Deck(x, y, w, h){
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.cards = [];
}
I need to add a method in Deck class similar to pointIsInCard instead it will be called pointIsInDeck. The logic will be same i.e to check whether the passed in point falls in the boundary of the object. So seeing this duplication of code I wanted to know what is a good design practice to avoid this duplication? One option I thought of was to extract the method out and create a function for generic object with x, y, width, height but again from OOP principles I thought this method should belong to the class/object. I appreciate any help! Thanks!

A common approach for what you're doing is to attach a Rectangle or similar instance with that functionality to both of your objects, that is:
class Rectangle {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
containsPoint(x, y) {
return x >= this.x && x =< this.width &&
y >= this.y && y =< this.height;
}
}
Then just add it to Card and Deck:
function Card() {
this.rect = new Rectangle(/* Your card shape */);
// ...
}
function Deck() {
this.rect = new Rectangle(/* Your deck shape */);
// ...
}
And you can do:
card.rect.containsPoint();
deck.rect.containsPoint();

If these are classes related to drawing, they would both inherit from something like Rectangle, which they would both inherit this behaviour from.
If they are gameplay-related, I would prefer them each referencing a Rectangle (or its subclass) that they would delegate all UI-related tasks to; then reduce this to the previous paragraph's solution.

You can use Function.prototype.call() to set this at a function call
function Card() {
this.x = 1; this.y = 2;
};
function Deck() {
this.x = 10; this.y = 20;
}
function points(x, y) {
// do stuff
console.log(x, this.x, y, this.y); // `this`: `c` or `d`
}
var c = new Card();
var d = new Deck();
points.call(c, 3, 4); // `this`: `c` within `points` call
points.call(d, 100, 200); // `this`: `d` within `points` call

Related

I cant figure out how to make my loop work in my space invader like game in Javascript

For my space invader like game I want to make bullets that get shot by the enemies but the problem is that the bullets dont appear in this loop that I made. I tried several things like making only one bullet appear while that works it is not the result that I want. My idea behind the code is that a new bullet gets shot from the position of the enemies when the enemybullet.position == 1 and if it exceeds the height of the canvas I want the bullet to return to the enemies and be shot again.
The code that I used for this result is here:
sketch.js
var enemies = [];
var enemybullet = [];
function setup() {
createCanvas(800, 600);
for (var i = 0; i < 2; i++) {
enemies[i] = new Enemy(i*300+300, 100);
}
// I tried enemybullet = new Enemybullet(120, 200); and that drew the bullet but it wasnt assigned to the enemy
rectMode(CENTER);
}
function draw() {
background(0);
// enemybullet.show(); this is wat I used to draw the single projectile
// enemybullet.move();
var edge = false;
for (var i = 0; i < enemies.length; i++) {
enemies[i].show();
enemies[i].move();
if(enemies[i].x > width || enemies[i].x < 0) {
edge = true;
}
}
if (edge) {
for (var i = 0; i < enemies.length; i++) {
enemies[i].shiftDown();
}
}
for (var i = 0; i < enemybullet.length; i++) {
enemybullet[i].show();
enemybullet[i].move();
for (var j = 0; j < enemies.length; j++) {
if(enemybullet[i].position == 1){
var enemybullets = new Enemybullet(enemies[j].x(), enemies[j].y());
enemybullet.push(enemybullets);
enemybullet[i].x = enemybullet[i].x;
enemybullet[i].y = enemybullet[i].y + enemybullet[i].speed;
if(enemybullet[i].y >= height){
enemybullet[i].position = 2;
}
}
else{
enemybullet[i].y = enemies[i].y;
enemybullet[i].x = enemies[i].x;
}
if(enemybullet[i].position == 2 ){
enemybullet[i].y = enemies[i].y;
enemybullet[i].x = enemies[i].x;
enemybullet[i].position = 1;
}
}
}
enemybullet.js
function Enemybullet(x, y) {
this.x = x;
this.y = y;
this.width = 10;
this.height = 20;
this.position = 1;
this.speed = 2;
this.show = function() {
fill('#ADD8E6');
rect(this.x, this.y, this.width, this.height);
}
this.move = function() {
this.x = this.x;
this.y = this.y + this.speed;
}
}
enemy.js
function Enemy(x, y) {
this.x = x;
this.y = y;
this.r = 100;
this.xdir = 1;
this.shot = function() {
this.r = this.r * 0;
this.xdir = this.xdir * 0;
}
this.shiftDown = function() {
this.xdir *= -1;
this.y += this.r/2;
}
this.show = function() {
fill('#0000FF');
rect(this.x, this.y, this.r, this.r);
}
this.move = function() {
this.x = this.x + this.xdir;
}
}
Rather than having the bullets be in their own global array, try having the bullets be a variable in the Enemy class.
this.bullet = new Enemybullet(this.x, this.y);
Then you can give the enemies functions such as the following to update the bullets.
this.updateBullet = function() {
this.bullet.move();
this.bullet.show();
}
this.resetBullet = function() {
this.bullet = new Enemybullet(this.x, this.y);
}
Where you were looping through each bullet before, you can instead call enemies[i].updateBullet(); when you move and show the enemies.
And when you want the enemies to shoot another shot, call enemies[i].resetBullet();
You can see my implementation here.

JavaScript Cannot Set Property '0' of undefined (2-dimensional array)

I know a bunch of people have already asked this, but nothing I have tried has worked. I have been stuck for days.
I suspect it may just be because the thing I am using (code.org) is a pile of flaming garbage, and it uses ES5. If you notice anything wrong, though, please let me know!
function Vector2(x, y)
{
this.x = x;
this.y = y;
return this;
}
function Pixel(x, y, darkness)
{
this.position = Vector2(x, y);
this.darkness = darkness;
this.rgbDarkness = this.darkness * 255;
return this;
}
function DataTexture2D(offsetX, offsetY, width, height)
{
this.offset = Vector2(offsetX, offsetY);
this.size = Vector2(width, height);
this.pixels = [];
for (var x = 0; x < width; x++)
{
this.pixels[x] = [];
for (var y = 0; y < height; y++)
{
this.pixels[x][y] = Pixel(x + this.offset.x, y + this.offset.y, 0.5);
}
}
return this;
}
var test = DataTexture2D(100, 50, 200, 300);
console.log(test.offset.x);

Question Regarding some Haverbeke "Eloquent JavaScript" Constructor Code

I am having a bit of difficulty understanding a few lines of code from page 108 of Marijn Haverbeke's "Eloquent JavaScript" book.
Namely, in the example below, I don't understand what "element = (x, y) => undefined" in the constructor parameter list is doing, when the Matrix object is instantiated with "let matrix = new Matrix(3, 3, (x, y) => value ${x}, ${y});"
Can some step-by-step it for me? It seems like we are passing a function to the constructor, but I don't get why the constructor parameter list is setup the way it is with another arrow function.
var Matrix = class Matrix {
constructor(width, height, element = (x, y) => undefined) {
this.width = width;
this.height = height;
this.content = [];
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
this.content[y * width + x] = element(x, y);
}
}
}
get(x, y) {
return this.content[y * this.width + x];
}
set(x, y, value) {
this.content[y * this.width + x] = value;
}
}
var MatrixIterator = class MatrixIterator {
constructor(matrix) {
this.x = 0;
this.y = 0;
this.matrix = matrix;
}
next() {
if (this.y == this.matrix.height) return {done: true};
let value = {x: this.x,
y: this.y,
value: this.matrix.get(this.x, this.y)};
this.x++;
if (this.x == this.matrix.width) {
this.x = 0;
this.y++;
}
return {value, done: false};
}
}
Matrix.prototype[Symbol.iterator] = function() {
return new MatrixIterator(this);
};
let matrix = new Matrix(3, 3, (x, y) => `value ${x}, ${y}`);
for (let {x, y, value} of matrix) {
console.log(x, y, value);
}
Ok, I forgot in ES6+ an equal sign after a parameter is a default value if the parameter is missing or undefined. The way he did this is just a little awkward.

p5.js repetition of the same function

I am learning p5.js and I don't quite understand how to repeat my function on the y-axis so that the lines appeared on top of the other. I understand that I would need to make a class object but all that I succeeded to do was to freeze the editor XD. Could you help me figure out how to make my function repeat itself with different Y starting point?
let walkers = []; // creation of an array
this.xoff = 0; //changed to go outside of the walker class
this.yoff = 0; //changed to go outside of the walker class
this.x = 0;
y = 200;
function setup() {
createCanvas(600, 600);
background(250);
for (let i = 0; i < 10; i++) { //mix array and class
walkers[i] = new walker(y);
}
}
function draw() {
for (i = 0; i < walkers.length; i++) { // consider the array lenght
walker[i].acceleration(); // call the class and it's function
walker[i].velocity();
walker[i].update();
walker[i].display();
}
}
class walker {
constructor(y) { //divide the class in multiple function
this.y = y
}
acceleration() {
this.accX = 0.1;
this.accY = 0.1;
this.px = this.x;
this.py = this.y;
}
velocity() {
this.velocityY = random(-20, 20);
this.velocityX = 5;
}
update() {
this.x = this.x + this.accX + this.velocityX * noise(this.xoff);
this.y = this.y + this.accY + this.velocityY * noise(this.yoff);
}
display() {
for (this.y < 200; this.y > 400; this.y + 20) {
line(this.x, this.y, this.px, this.py);
}
this.xoff = this.xoff + 1;
this.yoff = this.yoff + 100;
this.px = this.x;
this.py = this.y;
}
}
There are quite a few things wrong with how your code behaves.
Here are a few issues:
walkers it the array used,: e.g.walkers[i].acceleration();, not walker[i].acceleration(); (same hold true for the rest of the calls)
initialize variables if you plan to use them (otherwise using math operators update() will end up with NaN: e.g. this.x, this.xoff, this.yoff, etc.
it's unclear what motion behaviour you're after with position, velocity, acceleration, perlin noise, etc. (which btw are updated with strange increments ( this.yoff = this.yoff + 100;))
The code mostly freezes because of this:
for (this.y < 200; this.y > 400; this.y + 20)
It's unclear what you're trying to do there: this.y < 200; this.y > 400 makes me think you were going for an if condition to only draw lines between 200-400 px on Y axis, however this.y + 20 makes me think you want to increment y for some reason ?
It's also unclear why x,xoff,yoff moved out the walker class ?
There may be some understanding around classes, instances and the this keyword.
As per JS naming convention, class names should be title case.
I can tell you've put a bit of effort trying to build a more complex sketch, however it will won't be helpful for you to add complexity unless you understand all the parts of your code. This is where mastering the fundamentals pays off.
I recommend:
going back to pen/paper sketching to work out your idea until it's clearer
thinking of how you will achieve that by breaking complex tasks into smaller steps
writing basic test sketches for each step/component
Finally, bring one component at a time into a main program, testing again as components interact with another. Be sure to check out Kevin Workman's How to Program guide.
FWIW, here's a modified version of your code, with a tweak to set the initial y position of each Walker on top of each other:
let walkers = []; // creation of an array
y = 200;
function setup() {
createCanvas(600, 600);
background(250);
for (let i = 0; i < 10; i++) { //mix array and class
// incrementally add 10 pixels to y so the initially lines start on top of each other
walkers[i] = new Walker(y + (i * 10));
}
}
function draw() {
for (let i = 0; i < walkers.length; i++) { // consider the array length
// walkers, not walker
walkers[i].acceleration(); // call the class and it's function
walkers[i].velocity();
walkers[i].update();
walkers[i].display();
}
}
class Walker {
constructor(y) { //divide the class in multiple function
this.y = y;
// remember to init all variables you plan to use
this.xoff = 0; //changed to go back inside of the walker class
this.yoff = 0; //changed to go back inside of the walker class
this.x = 0;
}
acceleration() {
this.accX = 0.1;
this.accY = 0.1;
this.px = this.x;
this.py = this.y;
}
velocity() {
this.velocityY = random(-20, 20);
this.velocityX = 5;
}
update() {
this.x = this.x + this.accX + this.velocityX * noise(this.xoff);
this.y = this.y + this.accY + this.velocityY * noise(this.yoff);
}
display() {
// what were you trying ?
//for (this.y < 200; this.y > 400; this.y + 20) {
line(this.x, this.y, this.px, this.py);
//}
this.xoff = this.xoff + 1;
this.yoff = this.yoff + 1;
this.px = this.x;
this.py = this.y;
// reset x, y to 0
if(this.x > width){
this.x = 0;
}
if(this.y > height){
this.y = 0;
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
It will definitely be easier to do it using a class. Make different functions inside the class which are responsible for update, movement, etc. You can make a display function too in which you can set the y co-ordinate using a For loop. In that way, it will become very easy to keep changing the y co-ordinate.
If you want to display multiple lines at once, do all of the above and also use an array to store the y co-ordinates and then display them in the For loop.
Do let me know if you need help with the actual code.

Hit detection algorithm not working, unsure why not. (Javascript/Processing.js)

I'm new to programming games (and programming in general). I have previously made a "Flappy Bird" clone and a few others and I used the hit detection algorithm as provided by the Mozilla Developer Network here.
I am now trying to recreate "Pong" but, for whatever reason, its not working in my current code and I'm completely clueless as to why not. I want the ball to hit the "paddle" and then go back the way it came, but right now it ghosts through the paddle.
I'm using the Processing.js library but it should be apparent to anyone (familiar with it or not) what my code is trying to achieve. The draw() function gets called frequently by processing.js.
The code in action (but not working as expected) can be found here
var PADDLE_WIDTH = 10;
var PADDLE_HEIGHT = 75;
var PADDLE_X = 10;
var Player = function(y) {
this.x = PADDLE_X;
this.y = mouseY;
this.score = 0;
this.width = PADDLE_WIDTH;
this.height = PADDLE_HEIGHT;
};
Player.prototype.drawPlayer = function() {
rect(10,mouseY, this.width, this.height);
};
var Ball = function(x,y) {
this.x = x;
this.y = y;
this.speed = 4;
this.width = 10;
this.height = 10;
};
Ball.prototype.drawBall = function() {
rect(this.x, this.y, this.width, this.height);
};
Ball.prototype.onHit = function() {
if(this.y <= 0 || this.y >= height) {
this.speed *= -1;
} else if(this.x <= 0 || this.x >= width){
this.speed *= -1;
// HIT DETECTION UNDERNEATH
} else if (player.x < this.x + this.width &&
player.x + player.width > this.x &&
player.y < this.y + this.height &&
player.height + player.y > this.y){
this.speed *= -1;
}
};
var player = new Player();
var ball = new Ball(width/2, height/2);
draw = function() {
background(0);
fill(250, 250, 250);
ball.x -= ball.speed;
player.drawPlayer();
ball.drawBall();
ball.onHit();
};
In the drawPlayer method you draw player in the (10, mouseY) point, but never update y property of player. It always remains equal to 0. I would recommend you to add update method, which will change player's state and change draw method to render player purely on its state. Something like
Player.prototype.updatePlayer = function() {
this.y = mouseY;
};
Player.prototype.drawPlayer = function() {
rect(this.x , this.y, this.width, this.height);
};
draw = function() {
// ...
player.updatePlayer();
player.drawPlayer();
ball.drawBall();
// ...
};
In drawPlayer you can add the line this.y = mouseY
Player.prototype.drawPlayer = function() {
rect(10,mouseY, this.width, this.height);
this.y = mouseY;
};
Fiddle: http://jsfiddle.net/z0acb8gy/

Categories