|
|
@@ -0,0 +1,123 @@ |
|
|
let gameState = 'start'; |
|
|
let paddle_1 = document.querySelector('.paddle_1'); |
|
|
let paddle_2 = document.querySelector('.paddle_2'); |
|
|
let board = document.querySelector('.board'); |
|
|
let initial_ball = document.querySelector('.ball'); |
|
|
let ball = document.querySelector('.ball'); |
|
|
let score_1 = document.querySelector('.player_1_score'); |
|
|
let score_2 = document.querySelector('.player_2_score'); |
|
|
let message = document.querySelector('.message'); |
|
|
let paddle_1_coord = paddle_1.getBoundingClientRect(); |
|
|
let paddle_2_coord = paddle_2.getBoundingClientRect(); |
|
|
let initial_ball_coord = ball.getBoundingClientRect(); |
|
|
let ball_coord = initial_ball_coord; |
|
|
let board_coord = board.getBoundingClientRect(); |
|
|
let paddle_common = |
|
|
document.querySelector('.paddle').getBoundingClientRect(); |
|
|
|
|
|
let dx = Math.floor(Math.random() * 4) + 3; |
|
|
let dy = Math.floor(Math.random() * 4) + 3; |
|
|
let dxd = Math.floor(Math.random() * 2); |
|
|
let dyd = Math.floor(Math.random() * 2); |
|
|
|
|
|
document.addEventListener('keydown', (e) => { |
|
|
if (e.key == 'Enter') { |
|
|
gameState = gameState == 'start' ? 'play' : 'start'; |
|
|
if (gameState == 'play') { |
|
|
message.innerHTML = 'Game Started'; |
|
|
message.style.left = 42 + 'vw'; |
|
|
requestAnimationFrame(() => { |
|
|
dx = Math.floor(Math.random() * 4) + 3; |
|
|
dy = Math.floor(Math.random() * 4) + 3; |
|
|
dxd = Math.floor(Math.random() * 2); |
|
|
dyd = Math.floor(Math.random() * 2); |
|
|
moveBall(dx, dy, dxd, dyd); |
|
|
}); |
|
|
} |
|
|
} |
|
|
if (gameState == 'play') { |
|
|
if (e.key == 'w') { |
|
|
paddle_1.style.top = |
|
|
Math.max( |
|
|
board_coord.top, |
|
|
paddle_1_coord.top - window.innerHeight * 0.06 |
|
|
) + 'px'; |
|
|
paddle_1_coord = paddle_1.getBoundingClientRect(); |
|
|
} |
|
|
if (e.key == 's') { |
|
|
paddle_1.style.top = |
|
|
Math.min( |
|
|
board_coord.bottom - paddle_common.height, |
|
|
paddle_1_coord.top + window.innerHeight * 0.06 |
|
|
) + 'px'; |
|
|
paddle_1_coord = paddle_1.getBoundingClientRect(); |
|
|
} |
|
|
|
|
|
if (e.key == 'ArrowUp') { |
|
|
paddle_2.style.top = |
|
|
Math.max( |
|
|
board_coord.top, |
|
|
paddle_2_coord.top - window.innerHeight * 0.1 |
|
|
) + 'px'; |
|
|
paddle_2_coord = paddle_2.getBoundingClientRect(); |
|
|
} |
|
|
if (e.key == 'ArrowDown') { |
|
|
paddle_2.style.top = |
|
|
Math.min( |
|
|
board_coord.bottom - paddle_common.height, |
|
|
paddle_2_coord.top + window.innerHeight * 0.1 |
|
|
) + 'px'; |
|
|
paddle_2_coord = paddle_2.getBoundingClientRect(); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
function moveBall(dx, dy, dxd, dyd) { |
|
|
if (ball_coord.top <= board_coord.top) { |
|
|
dyd = 1; |
|
|
} |
|
|
if (ball_coord.bottom >= board_coord.bottom) { |
|
|
dyd = 0; |
|
|
} |
|
|
if ( |
|
|
ball_coord.left <= paddle_1_coord.right && |
|
|
ball_coord.top >= paddle_1_coord.top && |
|
|
ball_coord.bottom <= paddle_1_coord.bottom |
|
|
) { |
|
|
dxd = 1; |
|
|
dx = Math.floor(Math.random() * 4) + 3; |
|
|
dy = Math.floor(Math.random() * 4) + 3; |
|
|
} |
|
|
if ( |
|
|
ball_coord.right >= paddle_2_coord.left && |
|
|
ball_coord.top >= paddle_2_coord.top && |
|
|
ball_coord.bottom <= paddle_2_coord.bottom |
|
|
) { |
|
|
dxd = 0; |
|
|
dx = Math.floor(Math.random() * 4) + 3; |
|
|
dy = Math.floor(Math.random() * 4) + 3; |
|
|
} |
|
|
if ( |
|
|
ball_coord.left <= board_coord.left || |
|
|
ball_coord.right >= board_coord.right |
|
|
) { |
|
|
if (ball_coord.left <= board_coord.left) { |
|
|
score_2.innerHTML = +score_2.innerHTML + 1; |
|
|
} else { |
|
|
score_1.innerHTML = +score_1.innerHTML + 1; |
|
|
} |
|
|
gameState = 'start'; |
|
|
|
|
|
ball_coord = initial_ball_coord; |
|
|
ball.style = initial_ball.style; |
|
|
message.innerHTML = 'Press Enter to Play Pong'; |
|
|
message.style.left = 38 + 'vw'; |
|
|
return; |
|
|
} |
|
|
ball.style.top = ball_coord.top + dy * (dyd == 0 ? -1 : 1) + 'px'; |
|
|
ball.style.left = ball_coord.left + dx * (dxd == 0 ? -1 : 1) + 'px'; |
|
|
ball_coord = ball.getBoundingClientRect(); |
|
|
requestAnimationFrame(() => { |
|
|
moveBall(dx, dy, dxd, dyd); |
|
|
}); |
|
|
} |
There are a lot of hardcoded strings throughout this app:
"start"
,"play"
"w"
,"s"
,"ArrowUp"
,"ArrowDown"
,"Enter"
0
and1
are used to represent the four directions that the ball can moveStrings are great to represent state concepts: they are clear and expressive indicators of what a state change signifies. I would argue that numbers are not as expressive as strings for communicating state changes. The usage of numbers for ball direction is kind of "boolean" in nature:
0
for "up",1
for "down", but you have to study the code closely to understand what0
actually means in the context in which it's being used. A string that clearly states "up" or "down" doesn't require close study to understand.So! We've got some good strategies for representing state change, and some strategies that would benefit from refactoring to strings, but we can do better still. Seeing as the strings are hard-coded, it makes it easier to misspell them, or forget to update all of the instances of a hardcoded string if you decide to refactor the string in the future.
We can make our own home-rolled Typescript-style enums to save these strings in objects. This lets us divide the strings up into categories (states, movement keys, ball directions), and makes them easy and foolproof to update or expand if we need to: