Shooter game in Javascript

Posted on

Problem

I have written a small game in javascript and I was wondering if the code is acceptable. I am reasonably new to javascript although I can’t really get past the basics. All this code was originally in multiple files.

function Enemy(x,y,w,h,moveX){
	this.x = x;
	this.y = y;
	this.w = w;
	this.h = h;
	this.speedX = 1;
	this.speedY = 1;
	this.dead = false;

	this.show = function(){
		ctx.fillRect(this.x, this.y, this.w, this.h);
	}

	this.update = function(){
		if(this.dead == true){
			ctx.clearRect(this.x, this.y, this.w, this.h);		
		}
		else{
			this.show();
		}		

		this.move();

	}

	this.move = function(){
		this.x += this.speedX;
		if(this.x > canvas.width - this.w){
			this.speedX = -this.speedX;
		}
		else if (this.x < 0){
			this.speedX = -this.speedX;
		}
		
		this.y += this.speedY;
		if(this.y > canvas.height - this.h){
			this.speedY = -this.speedY;
		}
		else if (this.y < 0){
			this.speedY = -this.speedY;
		}
		
	}

}

function Bullet(x,y,w,h,up,down,left,right, sprite){
	this.x = x;
	this.y = y;
	this.w = w;
	this.h = h;
	this.hit = false;
	this.show = function(){
		if(sprite == null){
			ctx.fillRect(this.x,this.y,this.w,this.h);
		}
		else{
			let img = new Image();
			img.src = sprite;
			ctx.drawImage(img,this.x,this.y,this.w,this.h)
		}
	}

	this.moveX = function(dir){
		this.x += dir;
	}

	this.moveY = function(dir){
		this.y += dir;
	}

	this.update = function(){
		this.show();
		if(up) this.moveY(-5);
		if(down) this.moveY(5);
		if(left) this.moveX(-5);
		if(right) this.moveX(5);
	}

	this.clear = function(){
		ctx.clearRect(this.x,this.y,this.w,this.h);
	}
}

function Player(x, y, w, h, sprite){
	this.x = x;
	this.y = y;
	this.w = w;
	this.h = h;
	this.show = function(){
		if(sprite == null){
			ctx.fillRect(this.x,this.y,this.w,this.h);
		}
		else {
			let img = new Image();
			img.src = sprite;
			ctx.drawImage(img,this.x,this.y,this.w,this.h);
		}
	}

	this.moveX = function(moveX){
		this.x += moveX;
	}

	this.moveY = function(moveY){
		this.y += moveY;
	}
	this.wallCollide = function(){
		if(this.x <= 0) this.x = 0;
		else if(this.x >= canvas.width - this.w) this.x = canvas.width - this.w;

		if(this.y <= 0) this.y = 0;
		else if(this.y >= canvas.height - this.h) this.y = canvas.height - this.h;
	}
	this.update = function(){
		this.show();
		this.wallCollide();
		if(keys.up) player.moveY(-2);
		if(keys.down) player.moveY(2);
		if(keys.right) player.moveX(2);
		if(keys.left) player.moveX(-2); 
	}


}


let canvas = document.getElementById('mainCanv');
let ctx = canvas.getContext('2d');
let keys = {
	up: false,
	down: false,
	left: false,
	right: false
};
let player = new Player(canvas.width/2, canvas.height/2, 40, 40);
let bullets = [];
let enemy = [];
let enemyCount = 3;
let totalEnemy = enemyCount;
let scoreNum = 0;

for(let i = 0; i < enemyCount; i++){
	enemy.push(new Enemy(Math.floor(Math.random()*canvas.width),Math.floor(Math.random()*canvas.height),20,20, Math.floor(Math.random()*2)));
}
function Update(){
	ctx.clearRect(0,0, canvas.width, canvas.height);
	drawScore();
	player.update();

	//Draw Enemies
	for(let j = 0; j < enemy.length; j++){
		enemy[j].update();
		if(Collides(enemy[j], player)){
			location.reload();
		}
	}
	
	if(enemyCount == 0){
		enemyCount = totalEnemy;
		totalEnemy += 1;
		for(let i = 0; i < enemyCount; i++){
			enemy.push(new Enemy(Math.floor(Math.random()*canvas.width),Math.floor(Math.random()*canvas.height),20,20, Math.floor(Math.random()*2)));
		}
		for(let j = 0; j < enemy.length; j++){
			enemy[j].speedX += 1;
			enemy[j].speedY += 1;
		}
	}


	//Draw bullets
	for(let i = 0; i < bullets.length; i++){
		bullets[i].update();
		if(bullets[i].x > canvas.width - bullets[i].w || bullets[i].x < 0){
			bullets.splice(i,1);
		}
		for(let j = 0; j < enemy.length; j++){
			if (Collides(bullets[i], enemy[j])){
				bullets.splice(i,1);
				enemy.splice(j,1);
				enemyCount -= 1;
				scoreNum +=  1;
			}
		}
	}

}


window.addEventListener('keydown', function(event){
	switch(event.keyCode){
		case 87:
			keys.up = true;
			break;
		case 83:
			keys.down = true;
			break;
		case 68:
			keys.right = true;
			break;
		case 65:
			keys.left = true;
			break;
		case 38:
			bullets.push(new Bullet(player.x+15, player.y+15, 5, 5,true,false,false,false));
			break;
		case 40:
			bullets.push(new Bullet(player.x+15, player.y+15, 5, 5,false,true,false,false))
			break;
		case 37:
			bullets.push(new Bullet(player.x+15, player.y+15, 5, 5,false,false,true,false))
			break;
		case 39:
			bullets.push(new Bullet(player.x+15, player.y+15, 5, 5,false,false,false,true))

	}
});

window.addEventListener('keyup', function(event){
	switch(event.keyCode){
		case 87:
			keys.up = false;
			break;
		case 83:
			keys.down = false;
			break;
		case 68:
			keys.right = false;
			break;
		case 65:
			keys.left = false;
			break;
	
	}
});

function Collides(spriteOne, spriteTwo){
	if(spriteOne.x > spriteTwo.x && spriteOne.x < spriteTwo.x+spriteTwo.w && spriteOne.y > spriteTwo.y && spriteOne.y < spriteTwo.y + spriteTwo.h){
		return true;
	}
}



function drawScore(){
	let scoreText = document.getElementById('score').innerHTML = scoreNum + " Enemies Killed";
}
setInterval(Update, 1);
<canvas id="mainCanv" width="800" height="600" style="background-color: lightgrey"></canvas>
<p id="score" style="position:absolute; top:0px"></p>

Solution

I was wondering if the code is acceptable

The code is somewhat acceptable, though please consider the suggestions below- especially the first one!

Instance/Prototype Methods

It would be much more efficient to define the methods on the prototypes instead of attaching to each instance.

For example, take the methods out of the Enemy constructor:

this.show = function(){
    ctx.fillRect(this.x, this.y, this.w, this.h);
}

this.update = function(){
    ....

And make those methods of the prototype (after the definition of the constructor function):

Enemy.prototype.show = function(){
    ctx.fillRect(this.x, this.y, this.w, this.h);
}

Enemy.prototype.update = function(){
         ...

That way each method is declared only once instead of once per each instance. That would have huge impacts on the memory used by the browser. For more information about that topic, read about it in this post

Conditional logic

The logic in the Enemy method move() that conditionally negates the speed, I.e.:

if(this.x > canvas.width - this.w){
    this.speedX = -this.speedX;
}
else if (this.x < 0){
    this.speedX = -this.speedX;
}

Could be simplified using Logical OR:

if(this.x <0 || this.x > canvas.width - this.w){
    this.speedX = -this.speedX;
}

Comparison operators

The first Enemy method has an if statement like below:

if(this.dead == true){

If the aim is to ensure that this.dead is both true and in fact a Boolean, then the strict equality comparison operator (I.e. ===) can be used.

if(this.dead === true){

Otherwise if any value that is truthy will suffice then remove the == true.

if(this.dead){

Leave a Reply

Your email address will not be published. Required fields are marked *