breakout game
This commit is contained in:
parent
f5fb571823
commit
aea0c5558e
148
app/nose/bin/breakout.ts
Normal file
148
app/nose/bin/breakout.ts
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/// <reference lib="dom" />
|
||||
export const game = true
|
||||
|
||||
import type { GameContext, InputState } from "@/shared/game"
|
||||
|
||||
const CELL = 20
|
||||
const COLS = 30 // 600 px wide
|
||||
const ROWS = 12 // 240 px tall of bricks
|
||||
|
||||
const PADDLE_W = 6
|
||||
const PADDLE_H = 1
|
||||
const BALL_SPEED = 4
|
||||
|
||||
const ROW_COLORS = ["red", "orange", "yellow", "green", "blue", "magenta"]
|
||||
|
||||
let paddleX = 0
|
||||
let ball = { x: 0, y: 0, dx: BALL_SPEED, dy: -BALL_SPEED }
|
||||
let bricks: { x: number, y: number, alive: boolean }[] = []
|
||||
let score = 0
|
||||
let dead = false
|
||||
|
||||
export function init() {
|
||||
paddleX = (COLS * CELL - PADDLE_W * CELL) / 2
|
||||
ball = {
|
||||
x: COLS * CELL / 2,
|
||||
y: ROWS * CELL + 60,
|
||||
dx: BALL_SPEED,
|
||||
dy: -BALL_SPEED
|
||||
}
|
||||
|
||||
bricks = []
|
||||
for (let r = 0; r < ROWS; r++) {
|
||||
for (let c = 0; c < COLS; c += 2) {
|
||||
bricks.push({
|
||||
x: c * CELL,
|
||||
y: r * CELL,
|
||||
alive: true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
dead = false
|
||||
}
|
||||
|
||||
export function update(_delta: number, input: InputState) {
|
||||
if (dead) return
|
||||
|
||||
if (input.pressed.has("ArrowLeft") || input.pressed.has("a")) {
|
||||
paddleX -= 6
|
||||
}
|
||||
if (input.pressed.has("ArrowRight") || input.pressed.has("d")) {
|
||||
paddleX += 6
|
||||
}
|
||||
|
||||
// keep paddle on screen
|
||||
paddleX = Math.max(0, Math.min(paddleX, COLS * CELL - PADDLE_W * CELL))
|
||||
|
||||
// move ball
|
||||
ball.x += ball.dx
|
||||
ball.y += ball.dy
|
||||
|
||||
// wall bounce
|
||||
if (ball.x < 0 || ball.x > COLS * CELL) ball.dx *= -1
|
||||
if (ball.y < 0) ball.dy *= -1
|
||||
|
||||
// paddle bounce
|
||||
const paddleY = ROWS * CELL + 60
|
||||
const paddleH = CELL
|
||||
|
||||
if (
|
||||
ball.x > paddleX &&
|
||||
ball.x < paddleX + PADDLE_W * CELL &&
|
||||
ball.y + 6 >= paddleY &&
|
||||
ball.y - 6 <= paddleY + paddleH
|
||||
) {
|
||||
ball.dy *= -1
|
||||
ball.y = paddleY - 6
|
||||
}
|
||||
|
||||
// brick collision
|
||||
for (const b of bricks) {
|
||||
if (!b.alive) continue
|
||||
if (
|
||||
ball.x > b.x &&
|
||||
ball.x < b.x + (CELL * 2) &&
|
||||
ball.y > b.y &&
|
||||
ball.y < b.y + CELL
|
||||
) {
|
||||
score += 100
|
||||
b.alive = false
|
||||
ball.dy *= -1
|
||||
}
|
||||
}
|
||||
|
||||
// death
|
||||
if (ball.y > ROWS * CELL + 100) dead = true
|
||||
}
|
||||
|
||||
export function draw(ctx: GameContext) {
|
||||
ctx.clear("#6C6FF6")
|
||||
|
||||
const boardW = COLS * CELL
|
||||
const boardH = ROWS * CELL + 100
|
||||
const offsetX = (ctx.width - boardW) / 2
|
||||
const offsetY = (ctx.height - boardH) / 2
|
||||
|
||||
const c = ctx.ctx
|
||||
c.save()
|
||||
c.translate(offsetX, offsetY)
|
||||
|
||||
// background
|
||||
ctx.rectfill(0, 0, boardW, boardH, "black")
|
||||
|
||||
// paddle
|
||||
ctx.rectfill(
|
||||
paddleX,
|
||||
ROWS * CELL + 60,
|
||||
paddleX + PADDLE_W * CELL,
|
||||
ROWS * CELL + 60 + CELL,
|
||||
"white"
|
||||
)
|
||||
|
||||
// ball
|
||||
ctx.circfill(ball.x, ball.y, 6, "red")
|
||||
|
||||
// bricks
|
||||
for (const b of bricks) {
|
||||
if (b.alive) {
|
||||
const color = pickColor(b.x, b.y)
|
||||
ctx.rectfill(b.x, b.y, (b.x + (CELL * 2)) - .5, (b.y + CELL) - .5, color)
|
||||
}
|
||||
}
|
||||
// score!
|
||||
ctx.text(`Score: ${score}`, 5, boardH - 18, "cyan", 12)
|
||||
|
||||
c.restore()
|
||||
|
||||
// ya dead
|
||||
if (dead) {
|
||||
ctx.centerTextX("GAME OVER", boardH + 30, "red", 24)
|
||||
}
|
||||
}
|
||||
|
||||
function pickColor(x: number, y: number): string {
|
||||
const row = Math.floor(y / CELL)
|
||||
const colorIndex = Math.floor(row / 2) % ROW_COLORS.length
|
||||
return ROW_COLORS[colorIndex] || "white"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user