Skip to content

Commit

Permalink
fix gameplay bugs, control keys in start menu
Browse files Browse the repository at this point in the history
  • Loading branch information
oshibka404 committed Aug 28, 2024
1 parent 94d3d28 commit 0fd5649
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 38 deletions.
1 change: 1 addition & 0 deletions src/screens/match.css → src/screens/match/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,5 @@ header {
position: fixed;
bottom: 0;
right: 0;
color: rgba(255,255,255,0.5);
}
71 changes: 34 additions & 37 deletions src/screens/match.ts → src/screens/match/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import './match.css'
import './index.css'

type MatchStatus = 'not_started' | 'running' | 'paused' | 'finished'

Expand All @@ -19,7 +19,8 @@ interface PaddleModel {

interface MatchState {
status: MatchStatus
timestamp: number
gameplayTime: number
pausedTime: number
score: {
p1: number
p2: number
Expand All @@ -37,8 +38,9 @@ const PADDLE_SIZE = 8;
const SPEED_CELLS_PER_MS = 20 / 1000;

const defaultState = {
status: 'not_started' as MatchStatus,
timestamp: 0,
status: 'not_started',
gameplayTime: 0,
pausedTime: 0,
table: {
ball: {
position: {x: 0, y: 0},
Expand All @@ -57,7 +59,7 @@ const defaultState = {
p1: 0,
p2: 0,
}
}
} satisfies MatchState

export class Match {

Expand All @@ -78,7 +80,7 @@ export class Match {
private quitMatchEl: HTMLElement
private score1El: HTMLElement
private score2El: HTMLElement
private debugEl: HTMLElement
// private debugEl: HTMLElement

private quit: () => void = () => {
throw "Quit match callback is not initialized"
Expand Down Expand Up @@ -120,20 +122,19 @@ export class Match {
this.quit()
})

this.debugEl = document.getElementById('debug')!
// this.debugEl = document.getElementById('debug')!
this.tick = this.tick.bind(this)
}

tick(timestamp: number) {
if (!this.state.timestamp) {
this.state.timestamp = timestamp
if (!this.state.gameplayTime) {
this.state.gameplayTime = timestamp
}
if (this.state.status !== 'running') {
this.state.timestamp
return
if (this.state.status === 'paused') {
this.state.pausedTime = timestamp - this.state.gameplayTime
}

const state = this.updateState(this.state, timestamp)
const state = this.updateState(this.state, timestamp - this.state.pausedTime)

this.renderTable(state)

Expand All @@ -150,43 +151,46 @@ export class Match {
this.score2El.innerText = state.score.p2.toString(10)
}

private updateState(state: MatchState, timestamp: number): MatchState {
const timeDelta = timestamp - this.state.timestamp
this.state.timestamp = timestamp
private updateState(state: MatchState, gameplayTime: number): MatchState {
const timeDelta = gameplayTime - this.state.gameplayTime
this.state.gameplayTime = gameplayTime

const {ball, paddle1, paddle2} = state.table

if (Math.abs(ball.position.y) > this.tableSize.height / 2) {
ball.speed.y = -ball.speed.y
if (ball.position.y + BALL_RADIUS > this.tableSize.height / 2) {
ball.speed.y = -Math.abs(ball.speed.y)
}

if (ball.position.y - BALL_RADIUS < -this.tableSize.height / 2) {
ball.speed.y = Math.abs(ball.speed.y)
}

const leftScoreLine = -this.tableSize.width / 2 + 2
const rightScoreLine = this.tableSize.width / 2 - 2

if (ball.position.x < leftScoreLine + BALL_RADIUS) {
const distanceFromPaddleCenter = paddle1.position - ball.position.y
if (Math.abs(distanceFromPaddleCenter) < PADDLE_SIZE / 2) {
this.bounceFromPaddle(ball, distanceFromPaddleCenter)
if (ball.position.x <= leftScoreLine + BALL_RADIUS) {
const distanceFromPaddleCenter = ball.position.y - paddle1.position
if (Math.abs(distanceFromPaddleCenter) <= PADDLE_SIZE / 2) {
ball.speed.x = Math.abs(ball.speed.x)
ball.speed.y = distanceFromPaddleCenter / (PADDLE_SIZE / 2) // 0 to 1
} else {
this.state.score.p2 += 1
this.resetBall()
}
}

const rightScoreLine = -leftScoreLine
if (ball.position.x > rightScoreLine - BALL_RADIUS) {
const distanceFromPaddleCenter = paddle2.position - ball.position.y
if (Math.abs(distanceFromPaddleCenter) < PADDLE_SIZE / 2) {
this.state.table.ball = this.bounceFromPaddle(ball, distanceFromPaddleCenter)
const distanceFromPaddleCenter = ball.position.y - paddle2.position
if (Math.abs(distanceFromPaddleCenter) <= PADDLE_SIZE / 2) {
ball.speed.x = -Math.abs(ball.speed.x)
ball.speed.y = distanceFromPaddleCenter / (PADDLE_SIZE / 2) // 0 to 1
} else {
this.state.score.p1 += 1
this.resetBall()
}
}

ball.position.x += ball.speed.x * timeDelta * SPEED_CELLS_PER_MS
ball.position.x = Math.max(ball.position.x, leftScoreLine)
ball.position.x = Math.min(ball.position.x, rightScoreLine)

ball.position.y += ball.speed.y * timeDelta * SPEED_CELLS_PER_MS

paddle1.position += paddle1.speed * timeDelta * SPEED_CELLS_PER_MS
Expand All @@ -197,17 +201,11 @@ export class Match {
paddle2.position = Math.max(paddle2.position, -this.tableSize.height / 2 + PADDLE_SIZE / 2)
paddle2.position = Math.min(paddle2.position, this.tableSize.height / 2 - PADDLE_SIZE / 2)

this.debugEl.innerHTML = `<pre>${JSON.stringify(this.state, undefined, ' ')}</pre>`
// this.debugEl.innerHTML = `<pre>${JSON.stringify(this.state, undefined, ' ')}</pre>`

return this.state
}

private bounceFromPaddle(ball: BallModel, distanceFromPaddleCenter: number): BallModel {
ball.speed.x = -ball.speed.x
ball.speed.y = -distanceFromPaddleCenter / (PADDLE_SIZE / 2) // 0 to 1
return ball
}

private resetBall() {
this.state.table.ball = {
position: {x: 0, y: 0},
Expand All @@ -232,7 +230,6 @@ export class Match {
this.matchStateMessageEl.innerHTML = 'Press SPACE to continue'
this.matchStateEl.style.display = 'flex'
this.quitMatchEl.style.display = 'block'
this.state.timestamp = 0
} else if (state === 'finished') {
this.matchStateMessageEl.innerHTML = 'Game ended'
this.matchStateEl.style.display = 'flex'
Expand Down
15 changes: 15 additions & 0 deletions src/screens/start-menu.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,19 @@
button {
font-size: 2em;
}

.controls {
display: flex;
flex-direction: column;
gap: .5em;
}

.key {
border-radius: 0.5em;
display: inline-block;
padding: 0.5em;
margin-right: 0.5em;
background-color: rgba(255, 255, 255, .1);
box-shadow: #fff .25em .25em 0 0;
}
}
5 changes: 5 additions & 0 deletions src/screens/start-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ export class StartMenu {
startMenuEl.innerHTML = `
<header>PONG</header>
<button id="start">Start</button>
<div class="controls">
<div>P1 <span class="key">S</span>↑ <span class="key">X</span>↓</div>
<div>P2 <span class="key">↑</span>↑ <span class="key">↓</span>↓</div>
<div><span class="key">Space</span>: Pause</div>
</div>
`
const startButton = document.getElementById('start')
if (!startButton) {
Expand Down
2 changes: 1 addition & 1 deletion vite.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** @type {import('vite').UserConfig} */
export default {
base: '/pong/'
}

0 comments on commit 0fd5649

Please sign in to comment.