This has probably been asked in some form and I tried to check this resource: https://developer.mozilla.org/en/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
My gameobject.js looks like this:
function GameObject(program)
{
this.program = program;
this.graphics = null;
}
GameObject.prototype.update = function(dt)
{
}
GameObject.prototype.draw = function()
{
// Here graphics would be the graphics of the child class,
// because the graphics would be replaced with the child class' graphics
if(this.graphics !== null)
this.program.renderer.render(this.graphics);
}
And I want to have another class called, for example, box.js:
function Box(program, width, height, x, y)
{
// Call base constructor here?
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
And Now I want to basically inherit the update and draw methods from GameObject as well as call the GameObject constructor with the program parameter, so that in the end the thing should work like this:
var box = new Box(program, 10, 10, 100, 100);
// In game class update method
box.update(this.td);
// In game class draw method
box.draw();
So basically like how it would be done in C#. It would already help a lot, if only I could get the Box to inherit the update and draw methods from the GameObject.
Edit 1: Jsfiddle here: https://jsfiddle.net/0df9rfu5/1/
Edit 2: I tried a workaround like this:
function Box(program, width, height, x, y)
{
var self = this;
this.base = new GameObject(program);
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.update = function(dt) { self.base.update(dt); };
this.draw = this.base.draw();
}
So I would be creating a new instance of the base class GameObject every time a Box is created and then I set the box' update and draw methods to those of GameObject.
This isn't doing the trick though and I think there is something deeply wrong with this method any way.
Edit 3: Maybe I should just do this like I've always done... everything that inherits from GameObject still has to override the update and draw methods. It is just that I guess I can't be sure that every object in gameobject list has draw and update methods and I will just have to assume they do.
Constructors in JavaScript are just plain functions. To call GameObject from inside the Box constructor you would use:
GameObject.call(this, program);
To have Box objects inherit form GameObject use
Object.setPrototypeOf(Box.prototype, GameObject.prototype);
Be sure to put the line above before the first invocation of new Box.
For best compatibility with older browsers (tanks #Bergi), you can overwrite Box.prototype with a new object instead of using Object.setPrototypeOf.
Box.prototype = Object.create(GameObject.prototype, { configurable: true, writable: true, value: Box });
Then you should be able to use the methods draw and update on Box objects.
The example below should get you on the right track
function GameObject(program)
{
this.program = program;
this.graphics = null;
}
GameObject.prototype.update = function(dt)
{
alert('updating');
}
GameObject.prototype.draw = function()
{
// Here graphics would be the graphics of the child class,
// because the graphics would be replaced with the child class' graphics
if(this.graphics !== null)
this.program.renderer.render(this.graphics);
}
function Box(program, width, height, x, y)
{
GameObject.call(this, program);
this.width = width;
this.height = height;
this.x = x;
this.y = y;
}
Object.setPrototypeOf(Box.prototype, GameObject.prototype);
let b = new Box();
b.update();
Related
Im trying to code pacman at the moment, and a came across a problem:
Since all the Ghosts use the same pathfinding and are generally pretty simular, I want to use a prototype for them. The only property in which they really differ, is the way, they choose their target position. I'd like to give a function to the prototype and use it as a getter. Is that possible?
function Ghost(color,x,y,getterFunction){
this.color = color;
this.x = x;
this.y = y;
this.direction = "up";
this.move = function(){
//Pathfind towards this.target
}
this.target = getterFunction; //or something like this...
}
Thanks for your help :^)
#Bergi is right. You don't want to use it as a getter. If you tried to add it to the prototype it would be overwritten by every new ghost you create, since the prototype is a shared object.
The prototype is for shared functionality. Instance functionality belongs in the instance, i.e. In your constructor function.
Your move function should be on the prototype. But target should be an instance method. You could set a default method for target on the prototype. Any instance method would be called before looking to the prototype.
Example
function Ghost(color, x, y, target){
// everything in here is instance specific
this.color = color;
this.x = x;
this.y = y;
this.direction = "up";
if (typeof target === 'function') {
this.target = target;
}
}
// this is the prototype
Ghost.prototype.move = function() {
//Pathfind towards this.target
this.target()
}
Ghost.prototype.target = function() {
// default target to use if no target provided at creation
}
So now, when you do this:
var redGhost = new Ghost('red', 0, 0, function() {
//do something unique
})
You'll have a ghost that's red and has a custom target function. But if you do this:
var yellowGhost = new Ghost('yellow', 0, 0)
You'll have a ghost that uses the default target function you added to the prototype.
I don't understand this behavior in javascript for inheritance I've always seen it defined like so :
function GameObject(oImg, x, y) {
this.x = x;
this.y = y;
this.img = oImg;
this.hit = new Object();
this.hitBox.x = x;
this.hitBox.y = y;
this.hitBox.width = oImg.width;
this.hitBox.height = oImg.height;
}
Spaceship.prototype = new GameObject();
Spaceship.prototype.constructor = Spaceship;
function Spaceship(){
console.log("instantiate ship");
GameObject.apply(this, arguments);
this.vx = 0;
this.vy = 0;
this.speed = 3;
this.friction = 0.94;
}
But in my case, these lines :
this.hitBox.width = oImg.width;
this.hitBox.height = oImg.height;
When I do a console.log(this) in my Spaceship constructor, I can see that the proto property is set to Spaceship instead of GameObject, if I remove them, it is set to GameObject.
And if I use :
Spaceship.prototype = GameObject.prototype;
I have no more problems with that. The reason that this blocks me is that I have another object with an add() method and it checks that the object inerhits of GameObject with this code :
if(object instanceof GameObject)
I don't understand what those two lines can probably change so that inheritance is broken when they are present and I'm not sure doing inheritance the second way is good. Could someone enlighten me about this please ? :)
If you do
Spaceship.prototype = GameObject.prototype;
Then they both refer to the same object, so you might as well have everything in GameObject, if you add something to Spaceship.prototype, it will be added to GameObject.prototype as well. You can easily test it by adding something to Spaceship.prototype after the assignment. For example, in your case you can see that GameObject.prototype.constructor is actually Spaceship.
As for
Spaceship.prototype = new GameObject();
This invokes the constructor which might have undesired side effects, you rather want to use:
Spaceship.prototype = Object.create(GameObject.prototype);
Where the used Object.create functionality here comes down to:
Object.create = function( proto ) {
function f(){}
f.prototype = proto;
return new f;
};
Modern browsers already have the function though.
It was never properly explained why you were getting weird behavior with this.hitBox (I think that's what you were trying to say).
If you do inheritance by invoking the parent's constructor to create a prototype, that parent's constructor is executed once to create an instance of the parent type, and then all instances of the child type will share that one instance as their prototype.
The problem with this is that if that constructor has any lines that assign mutable objects to this, then those objects will be properties on that prototype and any modifications to those objects will be reflected across all instances of the child type:
Spaceship.prototype = new GameObject();
Spaceship.prototype.constructor = Spaceship;
var sps1 = new Spaceship();
var sps2 = new Spaceship();
sps1.hitBox.x = 9;
sps2.hitBox.x = 12;
console.log(sps1.hitBox.x); // 12 (oh noes! what happened)
console.log(sps2.hitBox.x); // 12
(there are other, similar problems with the "call a constructor to make a prototype" approach, but I'll just leave it here on that point)
#Esailija's suggestion to use Object.create(baseObject) is the first step to solving this problem. It creates a new object whose prototype is baseObject, but without the stuff that is set up in the constructor (This is a good thing, but it needs to be accounted for. Read on...).
As I just said, this will create an object where the initialization logic in the parent's constructor has never run, but in most cases that logic is relevant to the object's functionality. So there is one more thing you need to do, which is to have the child constructor call the parent constructor:
function Spaceship(oImg, x, y) {
// call parent constructor on this object and pass in arguments.
// you could also use default values for the arguments when applicable
GameObject.call(this, oImg, x, y);
// remainder of Spaceship constructor...
}
This will ensure that the parent constructor logic runs separately for every new Spaceship, and carries out the necessary initialization tasks.
function GameObject(oImg, x, y) {
this.x = x;
this.y = y;
this.img = oImg || {width:null, height: null};
this.hitBox = new Object();
this.hitBox.x = x;
this.hitBox.y = y;
this.hitBox.width = this.img.width;
this.hitBox.height = this.img.height;
}
function Spaceship(){
GameObject.apply(this, arguments);
this.vx = 0;
this.vy = 0;
this.speed = 3;
this.friction = 0.94;
}
Spaceship.prototype = new GameObject();
var sps1 = new Spaceship();
var sps2 = new Spaceship();
sps1.hitBox.x = 9;
sps2.hitBox.x = 12;
console.log(sps1.hitBox.x); // 9
console.log(sps2.hitBox.x); // 12
I don't understand this behavior in javascript for inheritance I've always seen it defined like so :
function GameObject(oImg, x, y) {
this.x = x;
this.y = y;
this.img = oImg;
this.hit = new Object();
this.hitBox.x = x;
this.hitBox.y = y;
this.hitBox.width = oImg.width;
this.hitBox.height = oImg.height;
}
Spaceship.prototype = new GameObject();
Spaceship.prototype.constructor = Spaceship;
function Spaceship(){
console.log("instantiate ship");
GameObject.apply(this, arguments);
this.vx = 0;
this.vy = 0;
this.speed = 3;
this.friction = 0.94;
}
But in my case, these lines :
this.hitBox.width = oImg.width;
this.hitBox.height = oImg.height;
When I do a console.log(this) in my Spaceship constructor, I can see that the proto property is set to Spaceship instead of GameObject, if I remove them, it is set to GameObject.
And if I use :
Spaceship.prototype = GameObject.prototype;
I have no more problems with that. The reason that this blocks me is that I have another object with an add() method and it checks that the object inerhits of GameObject with this code :
if(object instanceof GameObject)
I don't understand what those two lines can probably change so that inheritance is broken when they are present and I'm not sure doing inheritance the second way is good. Could someone enlighten me about this please ? :)
If you do
Spaceship.prototype = GameObject.prototype;
Then they both refer to the same object, so you might as well have everything in GameObject, if you add something to Spaceship.prototype, it will be added to GameObject.prototype as well. You can easily test it by adding something to Spaceship.prototype after the assignment. For example, in your case you can see that GameObject.prototype.constructor is actually Spaceship.
As for
Spaceship.prototype = new GameObject();
This invokes the constructor which might have undesired side effects, you rather want to use:
Spaceship.prototype = Object.create(GameObject.prototype);
Where the used Object.create functionality here comes down to:
Object.create = function( proto ) {
function f(){}
f.prototype = proto;
return new f;
};
Modern browsers already have the function though.
It was never properly explained why you were getting weird behavior with this.hitBox (I think that's what you were trying to say).
If you do inheritance by invoking the parent's constructor to create a prototype, that parent's constructor is executed once to create an instance of the parent type, and then all instances of the child type will share that one instance as their prototype.
The problem with this is that if that constructor has any lines that assign mutable objects to this, then those objects will be properties on that prototype and any modifications to those objects will be reflected across all instances of the child type:
Spaceship.prototype = new GameObject();
Spaceship.prototype.constructor = Spaceship;
var sps1 = new Spaceship();
var sps2 = new Spaceship();
sps1.hitBox.x = 9;
sps2.hitBox.x = 12;
console.log(sps1.hitBox.x); // 12 (oh noes! what happened)
console.log(sps2.hitBox.x); // 12
(there are other, similar problems with the "call a constructor to make a prototype" approach, but I'll just leave it here on that point)
#Esailija's suggestion to use Object.create(baseObject) is the first step to solving this problem. It creates a new object whose prototype is baseObject, but without the stuff that is set up in the constructor (This is a good thing, but it needs to be accounted for. Read on...).
As I just said, this will create an object where the initialization logic in the parent's constructor has never run, but in most cases that logic is relevant to the object's functionality. So there is one more thing you need to do, which is to have the child constructor call the parent constructor:
function Spaceship(oImg, x, y) {
// call parent constructor on this object and pass in arguments.
// you could also use default values for the arguments when applicable
GameObject.call(this, oImg, x, y);
// remainder of Spaceship constructor...
}
This will ensure that the parent constructor logic runs separately for every new Spaceship, and carries out the necessary initialization tasks.
function GameObject(oImg, x, y) {
this.x = x;
this.y = y;
this.img = oImg || {width:null, height: null};
this.hitBox = new Object();
this.hitBox.x = x;
this.hitBox.y = y;
this.hitBox.width = this.img.width;
this.hitBox.height = this.img.height;
}
function Spaceship(){
GameObject.apply(this, arguments);
this.vx = 0;
this.vy = 0;
this.speed = 3;
this.friction = 0.94;
}
Spaceship.prototype = new GameObject();
var sps1 = new Spaceship();
var sps2 = new Spaceship();
sps1.hitBox.x = 9;
sps2.hitBox.x = 12;
console.log(sps1.hitBox.x); // 9
console.log(sps2.hitBox.x); // 12
I have the following code:
function Vector(X,Y) //Constructor
{
this.X = X;
this.Y = Y;
}
function Box(Size /*Vector*/, Position /*Vector*/) //Constructor
{
this.Size = Size;
this.Position = Position;
this.Velocity = new Vector(0,0);
this.Anchored = true;
this.CanCollide = false;
this.Colour = "rgb(50,50,50)";
this.draw = function()
{
ctx.fillStyle = this.Colour;
ctx.fillRect(this.Position.X-(this.Size.X*0.5),this.Position.Y-(this.Size.Y*0.5),this.Size.X,this.Size.Y);
}
}
function Player(Size,Position)
{
Box(Size,Position);
this.Anchored = false;
this.CanCollide = true;
this.Colour = "rgb(0,100,0)";
}
var Me = new Player(new Vector(25,25), new Vector(10,10));
console.log(Me.Velocity);
If you look at the first statement in the constructor function, 'Player', you'll see that I called the function Box; I'm trying to inherit the properties and methods of 'Box' into 'Player'. I don't get any errors, however when I try and reference an inherited property, (the last statement), it returns undefined.
Why doesn't Player inherit Box's properties? I understand that JS is prototype based, and that this is extremely unorthodox, but I cannot make any sense as to how to inherit through multiple objects using prototypes.
It will not inherit the properties of Box that way. That is because you are calling the Box function on the global context and the value of this will point to the global object window. To change the value of the this inside the function, use call() or apply() or even bind().
When you change the value of this this way inside the Player function, the initialisation code inside Box will be run with the instance of Player as its context.
function Player(Size,Position)
{
Box.call(this,Size,Position); //the this value will point to instance of Player
}
Another options is to set Player's prototype:
Player.prototype = new Box();
Player.prototype.constructor = Player;
Then you wouldn't call the Box constructor directly. It would be called for you, so:
function Player(Size,Position)
{
this.Size = Size;
this.Position = Position;
this.Anchored = false;
this.CanCollide = true;
this.Colour = "rgb(0,100,0)";
}
I'm currently in the process of writing a javascript game and I'm trying to take advantage of inheritance for the object structure, with a base GameObject that all objects inherit from. However, I keep coming up against strange behaviour.
While the GameObject works fine, when its invoked by a subclass it tells me that some of its members or members of objects I pass through the constructor, are not defined, even though they are. Note, I'm also using pixi.js engine. Specifically what's happing is I'm passing a sprite to the super class GameObject, and its telling me that the position member of that sprite, is not defined.
This breaks the inheritance and the subclass is no longer an instance of the super class. The two classes are shown below. I don't think this is a fault of the pixi engine, rather some syntax mistake on my end. Unfortunately I'm baffled at what could be going wrong. Also note that each class is in a separate js file.
I would like to know why its behaving like this, and how to fix it.
GameObject :
function GameObject(x_, y_, width_, height_, actor, stage) {
this.actor = actor;
var position = { x : x_, y : y_ }; //because I want the position data to be private
var size = { width : width_, height : height_ };
this.actor.position.x = position.x; //this is where it throws the reference error
this.actor.position.y = position.y;
this.actor.width = size.width;
this.actor.height = size.height;
stage.addChild(this.actor);
this.GetPosition = function() {
return position;
}
this.SetPosition = function(x, y) {
position.x = x;
position.y = y;
this.actor.position.x = position.x;
this.actor.position.y = position.y;
}
this.GetSize = function() {
return size;
}
this.SetSize = function(x, y) {
size.x = x;
size.y = y;
actor.width = size.x;
actor.height = size.y;
}
var collisionGroup = -1;
this.SetCollisionGroup = function(index) {
collisionGroup = index;
}
this.GetCollisionGroup = function() {
return collisionGroup;
}
}
subclass inheriting from GameObject :
FlappyBird.prototype = new GameObject();
FlappyBird.prototype.constructor = FlappyBird;
function FlappyBird(stage) {
GameObject.call(this, 50, 50, 50, 50, PIXI.Sprite.fromFrame("flappy01.png"), stage); //inherits from the game object
console.log(this instanceof GameObject);
console.log(this instanceof FlappyBird);
}
When you're invoking the GameObject function here:
FlappyBird.prototype = new GameObject();
...you're not passing any arguments, so clearly the members, like actor, that expect those arguments will have the value undefined. So this.actor.position will fail with a TypeError.
A better approach would be to do this:
FlappyBird.prototype = Object.create(GameObject.prototype);
...since the per-instance stuff shouldn't be needed on the FlappyBird.prototype.
Code in the question was updated.
And this is obviously going to be a problem since you're going to have infinite recursion.
function FlappyBird(stage) {
// vv---Will infinitely recurse
FlappyBird.call(this, 50, 50, 50, 50, PIXI.Sprite.fromFrame("flappy01.png"), stage); //inherits from the game object
console.log(this instanceof GameObject);
console.log(this instanceof FlappyBird);
}
You probably meant to call GameObject instead
function FlappyBird(stage) {
// vv---Applies the `GameObject` constructor to the `FlappyBird` object.
GameObject.call(this, 50, 50, 50, 50, PIXI.Sprite.fromFrame("flappy01.png"), stage); //inherits from the game object
console.log(this instanceof GameObject);
console.log(this instanceof FlappyBird);
}