I have been learning a lot about prototypical and classical inheritance, and I am trying to rewrite some spaghetti code I have written to use one of these approaches.
The issue I am having is I am not sure the best way to initialize the properties of the constructor function because some of them require some logic to determine the value.
My example below is an enemy constructor function which is used in the spawnEnemy function beneath it. When I rewrite this I think I can make spawnEnemy a function of the prototype and have it inherited by the objects I create.
The first value I need to identify is the spawnDir, because this is used to set the x and y coordinates of the enemy. Speed is also randomized between a set of values, and the color will be a sprite in a later rewrite which will be an object that is passed in (that is not in the scope of my question).
I have provided my existing code below, maybe there isn't a "clean" solution like I am hoping. I want to learn best practices, and I can't help feel like there is a solution to this using inheritance somewhere and running some additional methods upon instantiating a sub class of some sort. I am just having trouble conceptualizing this model.
Ideal Solution
I am not sure if I am explaining things the best way possible above, so I will try to be concise here with what I am looking for. I am hoping to use the classical model, preferably, to provide a solution using constructors and inheritance for setting up the properties of the objects instantiated from the resulting class.
Spaghetti Code (yeah I know, it is very spaghetti)
var Enemy = function(color, spawnDir, speed, w, h) {
// spawnDir is randomized and passed in as argument
// objects starting position is set based on what spawn direction was set
if (spawnDir == 0) {
this.x = -w;
} else if (spawnDir) {
this.x = GAME_WIDTH + w;
}
this.y = (GAME_HEIGHT / 2) - (h / 2); // objects y coordinate always set to very middle of screen
this.color = color; // color or sprite image of enemy
this.spawnDir = spawnDir; // spawn direction passed as argument
this.speed = speed; // speed passed as argument, randomized
this.width = w; // width passed as argument
this.height = h; // height passed as argument
this.collision = 0; // sets whether object has collided, 0 = default, incremenets with each collision detected
//this.id = generateRandomId(8); // randomized id assigned to object as identifier (NOT USED CURRENTLY)
// called in the draw loop, renders object to canvas
this.draw = function(ctx) {
ctx.shadowColor = "black";
ctx.shadowBlur = 5;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
};
};
// enemy spawn function
var spawnEnemy = function() {
// if enemies array length is less than enemy reserve limit and spawn counter is set to 0, then success
if (enemies.length < enemyReserve && spawnCounter == 0) {
var t;
var x = Math.floor(Math.random() * 99 + 1); // randomizes spawn direction by generating random number between 0-100
if (x <= 50) { // if the number is less than 50 then spawn is from the left side of the screen
t = 0; // if the number is greater than 50 then spawn is from the right side of the screen
} else if (x > 50) {
t = 1;
}
var s = Math.floor(Math.random() * 3 + 1); // randomizes speed of the enemy
var enemy = new Enemy("purple", t, s, 40, 20); // instantiates new enemy object with some statis + dynamic arguments
spawnCounter = spawnRate; // spawn counter reset back to default value set in global variables
enemies.push(enemy); // adds the new object to enemies global array
} else if (spawnCounter != 0) {
spawnCounter--; // if counter is not set to 0 then lowers counter value by 1
}
};
答案 0 :(得分:1)
I prefer to use a pattern where I define the properties within the constructor, any static methods after the constructor, then member methods on the prototype after the static methods. It ends up looking something like this:
function Enemy(color, spawnDir, speed, w, h) {
//Define instance properties
this.spawnDir = spawnDir;
this.speed = speed;
this.width = w;
this.height = h;
//...
};
//Static functions
Enemy.spawn = function(){
var color, spawnDir, speed, w, h;
//Calculate variables as needed
return new Enemy(color, spawnDir, speed, w, h);
};
//Member functions
Enemy.prototype.draw = function(){
//Do instance stuff.
//Access instance properties with 'this'
};
As far as trying to create subclass (of different enemy types for example) look at the answer to Subclassing a class with required parameters in JavaScript