I'm trying to learn javascript (especially the OOP part) and made this simple game. I'm looking for advice and ways to improve my code.
- Did I handle OOP well enough?
- What's the best way to create a new object of a given class (
object.assign
, newObject()
etc)? There are a lot of them - Which variables should be named using only uppercase letters
- Did I make a mistake by defining variables like
score
globaly? foodEaten() { score++; food = new Food(); }
Is it a good way to override an object? How can I do this using "this" keyword?- Should I stick to
window.requestAnimationFrame(gameLoop);
or just use the function with given interval?
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
let score, food, player, board;
class Board {
constructor(width, height, backgroundColor) {
this.height = height;
this.width = width;
this.backgroundColor = backgroundColor;
}
drawBoard(score) {
ctx.clearRect(0, 0, this.width, this.height) //clears board
ctx.fillStyle = this.backgroundColor; //sets background color
ctx.fillRect(0, 0, this.width, this.height); //background
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.fillText(score, 10, 30);
}
}
class Player {
constructor(positionx, positiony, size, color, dx, dy) {
this.x = positionx;
this.y = positiony;
this.size = size;
this.color = color;
this.dx = dx;
this.dy = dy;
}
playerMove() {
this.x += this.dx;
this.y += this.dy;
}
drawPlayer() {
ctx.strokeStyle = this.color;
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.size, this.size);
ctx.stroke();
}
checkCollision() {
this.x + this.size > canvas.width || this.x < 0 ||
this.y + this.size > canvas.height || this.y < 0 ?
newGame() : null;
}
changeDirection(direction) {
switch (direction) {
case 1: //up
this.dx = 0;
this.dy = -2;
break;
case 2: //left
this.dx = -2;
this.dy = 0;
break;
case 3: //down
this.dx = 0;
this.dy = 2;
break;
case 0: //right
this.dx = 2;
this.dy = 0;
break;
}
}
checkCollisionFood(food) {
(food.x > this.x - food.size && food.x < this.x + this.size) &&
(food.y > this.y - food.size && food.y < this.y + this.size)
? food.foodEaten() : null
}
}
class Food {
constructor() {
this.size = 20;
this.x = Math.floor(Math.random() * (canvas.width - this.size + 1));
this.y = Math.floor(Math.random() * (canvas.height - this.size + 1));
this.color = "red";
}
drawFood() {
ctx.strokeStyle = this.color;
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.size, this.size);
ctx.stroke();
}
foodEaten() {
score++;
food = new Food();
}
}
function gameLoop() {
board.drawBoard(score);
player.drawPlayer();
player.playerMove();
player.checkCollision();
player.checkCollisionFood(food);
food.drawFood();
window.requestAnimationFrame(gameLoop);
}
newGame();
window.requestAnimationFrame(gameLoop);
function newGame() {
score = 0;
food = new Food();
player = new Player(40, 40, 40, "blue", 0, 0);
board = new Board(c.width, c.height, "green");
}
document.addEventListener('keydown', (event) => {
switch (event.keyCode) {
case 87: //"w" key
player.changeDirection(1);
break;
case 83: //"s" key
player.changeDirection(3);
break;
case 68: //"d" key
player.changeDirection(0);
break;
case 65: //"a" key
player.changeDirection(2);
break;
default:
break;
}
});
<!DOCTYPE html>
<html>
<head>
<title>canvas</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<canvas id="c" width="300px" height="400px">
</body>
<script src="main.js"></script>
</html>
1 Answer 1
Thanks for the specific questions. It really helps guide feedback.
Did I handle OOP well enough?
This seems pretty good, although a little more thinking about the "responsibilities" of a class will help.
What's the best way to create a new object of a given class (object.assign, new Object() etc)? There are a lot of them
new Foo()
is the standard way.
Which variables should be named using only uppercase letters
Usually just constants, which include class names.
Did I make a mistake by defining variables like score globaly?
Yes, generally it's good to avoid globals.
foodEaten() {
... Is it a good way to override an object? How can I do this using "this" keyword?
You need to step back and look at the design a little bit. Some fn/object/class should be responsible for keeping track of the food, and therefore it would "own" the food
variable (as this.food
). It would be responsible for creating the original food and replenishing as needed after foodEaten
is called. You can sometimes figure this out by passing the variables into where they are needed until you get to the code that can simply define a local variable... and sometimes it takes a little more refactoring.
Should I stick to window.requestAnimationFrame(gameLoop); or just use the function with given interval?
These are basically the same, and probably not worth fretting about. There does probably need some sort of event loop that has locally scoped variables for the game and perhaps food... that's how you would refactor out the global variables.
Explore related questions
See similar questions with these tags.