0

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();

1
  • if you are using OOP in javascript, an idea is to extends Enemy from Player, or make one base for two , like Avatar, or any name Commented Dec 22, 2016 at 6:39

4 Answers 4

1

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

Sign up to request clarification or add additional context in comments.

2 Comments

So I would pass the "Player", not the "player" to the collision method?
no, you should pass instance of Player, which in your case is player
0

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.

Comments

0

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
 }
})();

Comments

0

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
}

1 Comment

Normally that would be true. I am using a game engine to build the game, it uses HTML5 Canvas and JS. The engine only looks to new functions to update the game, that wasn't specifically mentioned in this post though. The engine looks for "player.prototype.update" and "enemy.prototype.update," and it continually updates the game based on those two functions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.