Skip to content

Instantly share code, notes, and snippets.

@gabrielflorit
Last active February 13, 2019 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gabrielflorit/ede19adb3a48ca0fd6b30818c5309f5b to your computer and use it in GitHub Desktop.
Save gabrielflorit/ede19adb3a48ca0fd6b30818c5309f5b to your computer and use it in GitHub Desktop.
SCRIPT-8
{
"0": {
"0": {
"0": 2
}
}
}
// title: Break-9
// BUGS:
// - diagonal hits shouldn't always reverse both directions
const e = {
paddle: {
width: 24,
height: 3,
speed: 3,
brake: 0.8
},
ball: {
radius: 2
},
maxlives: 3,
brick: {
width: 14,
height: 3,
color: 3
}
}
const modes = {
start: 'start',
game: 'game',
gameover: 'gameover'
}
// called on init, and when the game is over
const defaultBricks = () =>
flatten(
range(8).map(row =>
range(8).map(col => ({
x: 1 + col * (e.brick.width + 2),
y: 24 + row * (e.brick.height + 2),
color: e.brick.color,
destroyed: false
}))
)
)
// called on init, and when we lose a ball
const defaultBall = () => ({
x: 64,
y: 98,
dx: -1,
dy: -1,
sticky: true
})
// called when game is over
const resetGame = ({ state, bricks, paddle, ball }) => {
state.lives = e.maxlives
state.score = 0
bricks.bricks = defaultBricks()
ball.sticky = true
paddle.x = 64 - e.paddle.width / 2
}
initialState = {
collision: '',
mode: modes.start,
lives: e.maxlives,
score: 0,
actors: [
{
name: 'paddle',
x: 64 - e.paddle.width / 2,
y: 120 - e.paddle.height - 2,
dx: 0,
color: 2
},
{
name: 'bricks',
bricks: defaultBricks()
},
{
name: 'ball',
color: 0,
...defaultBall()
}
]
}
const collider = {
lineLine (a, b) {
const [[ax1, ay1], [ax2, ay2]] = a
const [[bx1, by1], [bx2, by2]] = b
// calculate the distance to intersection point
const uA =
((bx2 - bx1) * (ay1 - by1) - (by2 - by1) * (ax1 - bx1)) /
((by2 - by1) * (ax2 - ax1) - (bx2 - bx1) * (ay2 - ay1))
const uB =
((ax2 - ax1) * (ay1 - by1) - (ay2 - ay1) * (ax1 - bx1)) /
((by2 - by1) * (ax2 - ax1) - (bx2 - bx1) * (ay2 - ay1))
// if uA and uB are between 0-1, lines are colliding
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
return true
} else {
return false
}
},
lineRect ({ line, rect }) {
let collision = []
if (
line[1][0] >= rect.x &&
line[1][0] <= rect.x + rect.width &&
line[1][1] >= rect.y &&
line[1][1] <= rect.y + rect.height
) {
const pointLine = [[line[0][0], line[0][1]], [line[1][0], line[1][1]]]
const leftLine = [[rect.x, rect.y], [rect.x, rect.y + rect.height]]
const rightLine = [
[rect.x + rect.width, rect.y],
[rect.x + rect.width, rect.y + rect.height]
]
const topLine = [[rect.x, rect.y], [rect.x + rect.width, rect.y]]
const bottomLine = [
[rect.x, rect.y + rect.height],
[rect.x + rect.width, rect.y + rect.height]
]
if (collider.lineLine(pointLine, leftLine)) collision.push('left')
if (collider.lineLine(pointLine, rightLine)) collision.push('right')
if (collider.lineLine(pointLine, topLine)) collision.push('top')
if (collider.lineLine(pointLine, bottomLine)) collision.push('bottom')
}
return collision
}
}
const bounceBallOffPaddle = ({ ball, paddle, state }) => {
const collision = collider.lineRect({
line: [[ball.x - ball.dx, ball.y - ball.dy], [ball.x, ball.y]],
rect: {
...paddle,
width: e.paddle.width,
height: e.paddle.height
}
})
if (collision.length === 1) {
const [single] = collision
if (single === 'left') ball.dx = -Math.abs(ball.dx)
if (single === 'right') ball.dx = Math.abs(ball.dx)
if (single === 'top') {
ball.dy = -Math.abs(ball.dy)
if (ball.x < paddle.x + e.paddle.width / 4) {
ball.dx -= 0.5
} else if (ball.x > paddle.x + 3 * e.paddle.width / 4) {
ball.dx += 0.5
}
}
if (single === 'bottom') ball.dy = Math.abs(ball.dy)
playPhrase(1)
}
if (collision.length === 2) {
ball.dy *= -1
playPhrase(1)
}
state.collision = collision.join(',')
}
const movePaddle = ({ ball, paddle, input }) => {
// If we're pressing left or right, set paddle speed to max.
if (input.left && paddle.x > 0) {
paddle.dx = -e.paddle.speed
} else if (input.right && paddle.x < 128 - e.paddle.width) {
paddle.dx = e.paddle.speed
} else {
// Otherwise decelerate.
paddle.dx *= e.paddle.brake
}
// Move the paddle,
paddle.x = Math.round(paddle.dx + paddle.x)
// but don't let it go past the screen edges.
paddle.x = clamp(paddle.x, 0, 128 - e.paddle.width)
}
const bounceBallOffWalls = ball => {
if (ball.x <= e.ball.radius - 1) {
ball.dx = Math.abs(ball.dx)
playPhrase(0)
}
if (ball.x >= 127 - e.ball.radius + 1) {
ball.dx = -Math.abs(ball.dx)
playPhrase(0)
}
if (ball.y <= 11 - e.ball.radius - 1) {
ball.dy = Math.abs(ball.dx)
playPhrase(0)
}
}
const bounceBallOffBricks = ({ ball, bricks, state }) => {
bricks.bricks.filter(brick => !brick.destroyed).forEach(brick => {
const collision = collider.lineRect({
line: [[ball.x - ball.dx, ball.y - ball.dy], [ball.x, ball.y]],
rect: {
...brick,
width: e.brick.width,
height: e.brick.height
}
})
if (collision.length) {
if (collision.length === 1) {
const [single] = collision
if (single === 'left') ball.dx = -Math.abs(ball.dx)
if (single === 'right') ball.dx = Math.abs(ball.dx)
if (single === 'top') ball.dy = -Math.abs(ball.dy)
if (single === 'bottom') ball.dy = Math.abs(ball.dy)
}
if (collision.length === 2) {
ball.dy *= -1
ball.dx *= -1
}
state.score++
playPhrase(3)
brick.destroyed = true
}
})
}
const moveBall = ({ ball, paddle, input }) => {
if (ball.sticky) {
ball.x = paddle.x + e.paddle.width / 2
ball.y = paddle.y - e.ball.radius
ball.dx = paddle.x + e.paddle.width / 2 < 64 ? 1 : -1
ball.sticky = !input.a
} else {
ball.x = ball.x + ball.dx
ball.y = ball.y + ball.dy
}
}
const loseBall = ({ ball, state, paddle }) => {
if (ball.y >= 127) {
Object.entries(defaultBall()).forEach(([key, value]) => {
ball[key] = value
})
state.lives--
playPhrase(2)
}
}
update = (state, input) => {
if (state.mode === modes.game) {
const paddle = state.actors.find(d => d.name === 'paddle')
const ball = state.actors.find(d => d.name === 'ball')
const bricks = state.actors.find(d => d.name === 'bricks')
bounceBallOffWalls(ball)
bounceBallOffPaddle({ ball, paddle, state })
bounceBallOffBricks({ ball, bricks, state })
loseBall({ ball, paddle, state })
movePaddle({ ball, paddle, input })
moveBall({ ball, paddle, input })
if (state.lives < 0) {
state.mode = modes.gameover
resetGame({ ball, paddle, bricks, state })
}
} else if (state.mode === modes.start) {
if (input.start) {
state.mode = modes.game
}
} else if (state.mode === modes.gameover) {
if (input.start) {
state.mode = modes.game
}
}
}
const drawActor = (actor, fade) => {
if (actor.name === 'paddle') {
// Draw entire paddle.
rectFill(
actor.x,
actor.y,
e.paddle.width,
e.paddle.height,
actor.color + (fade ? 3 : 0)
)
// Draw inner highlight.
rectFill(
actor.x + 1,
actor.y + 1,
e.paddle.width - 2,
e.paddle.height - 2,
actor.color + (fade ? 4 : 2)
)
}
if (actor.name === 'bricks') {
actor.bricks.forEach(brick => {
if (!brick.destroyed) {
rectFill(brick.x, brick.y, e.brick.width, e.brick.height, brick.color)
}
})
}
if (actor.name === 'ball') {
// Draw sticky preview.
if (actor.sticky) {
line(actor.x, actor.y, actor.x + 5 * actor.dx, actor.y + 5 * actor.dy, 4)
}
circFill(actor.x, actor.y, e.ball.radius, actor.color + (fade ? 3 : 0))
}
}
drawActors = (state, fade) => {
state.actors.forEach(actor => drawActor(actor, fade))
}
draw = state => {
if (state.mode === modes.start) {
clear()
print(51, 46, 'break-8', 0)
print(35, 95, 'left / right / a', 4)
print(44, 76, 'press start', 3)
// line(0, 0, 127, 127, 0)
// line(127, 0, 0, 127, 0)
} else if (state.mode === modes.game) {
clear()
print(51, 46, 'break-8', 0)
rectFill(0, 7, 128, 128, 6)
print(0, 120, state.collision, 0)
print(0, 0, `lives:${state.lives}`, 2)
print(40, 0, `score:${state.score}`, 2)
drawActors(state)
} else if (state.mode === modes.gameover) {
// clear()
rectFill(0, 64 - 12, 128, 24, 7)
print(47, 57, 'game over', 0)
print(43, 66, 'press start', 3)
}
}
{
"0": [
"0c07"
],
"1": [
"0c17"
],
"2": [
"0b37",
"1d#37",
"2g27",
"3b17",
"4d#17",
"5g07",
"6c07"
],
"3": [
"0g37"
]
}
{
"0": {
"0": 0
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment