Skip to content

Instantly share code, notes, and snippets.

@zspecza
Created March 29, 2013 00:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zspecza/df23db9dbc546f5b11d8 to your computer and use it in GitHub Desktop.
Save zspecza/df23db9dbc546f5b11d8 to your computer and use it in GitHub Desktop.
###
# Solo Pong - Declan de Wet
# TODO: Prevent paddle from going out of the viewport
# TODO: Implement collision animations
# TODO: Music and sound
# TODO: Keyboard controls
###
# Create a self-instantiating shim/polyfill for the requestAnimationFrame API, if it does not exist,
# then fall back to setTimeOut set to 60fps (average)
do ->
lastTime = 0
vendors = ["webkit", "moz"]
x = 0
while x < vendors.length and not window.requestAnimationFrame
window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"]
window.cancelAnimationFrame = window[vendors[x] + "CancelAnimationFrame"] or window[vendors[x] + "CancelRequestAnimationFrame"]
++x
unless window.requestAnimationFrame
window.requestAnimationFrame = (callback, element) ->
currTime = new Date().getTime()
timeToCall = Math.max(0, 16 - (currTime - lastTime))
id = window.setTimeout(->
callback currTime + timeToCall
, timeToCall)
lastTime = currTime + timeToCall
id
unless window.cancelAnimationFrame
window.cancelAnimationFrame = (id) ->
clearTimeout id
# We'll need to set this to some arbitrary value for now - it changes later, but we need it to keep
# track of the requestAnimationFrame if we ever need to stop the animation
animID = 0
jQuery ->
# Get the canvas 2D context
canvas = $('#canvas')
can = canvas[0]
ctx = can.getContext '2d' if can.getContext
# Initialize some movement parameters
dirX = 6
dirY = 8
# Set the width and height of the canvas to the full window's width and height
can.width = $(window).width()
can.height = $(window).height()
# Find out the canvas' relaive Y position
can.minY = $("#canvas").offset().top;
can.maxY = can.minY + can.height;
# Move the paddle with the mouse
onMouseMove = (e) ->
if e.pageY > can.minY and e.pageY < can.maxY
paddle.posY = (e.pageY - can.minY) - paddle.height / 2
# Initialize the player's score and number of lives so we can keep track
score = 0
lives = 0
# Instantiate the ball object and set it's size and position
ball = {}
ball.size = 10
ball.posX = can.width / 2 - ball.size / 2
ball.posY = Math.round(Math.random() * can.height) # We don't always want it to start in the same place
# Instantiate the paddle object
paddle = {}
paddle.width = 10
paddle.height = can.height / 2
paddle.posX = can.width - paddle.width
paddle.posY = can.height / 2 - paddle.height / 2
# Create some drop-in functions for clearing and creating shapes, keeping it DRY
clear = ->
ctx.clearRect 0, 0, can.width, can.height
circle = (x, y, r, color) ->
ctx.fillStyle = color
ctx.beginPath()
ctx.arc(x, y, r, 0, Math.PI*2, true)
ctx.closePath()
ctx.fill()
rect = (x, y, w, h, color) ->
ctx.fillStyle = color
ctx.beginPath()
ctx.rect(x, y, w, h)
ctx.fill()
# Set up a draw function that will be called at each animation frame
draw = ->
# clear the canvas before we re-draw
do clear
# Display player's current score and remaining lives
ctx.font = '15pt Calibri'
ctx.fillStyle = 'white'
ctx.fillText "Score: #{score} Lives: #{lives}", 20, 30
# Instantiate the ball
circle(ball.posX, ball.posY, ball.size, 'red')
# Make the ball bounce
if ball.posX + dirX < ball.size
dirX = -dirX
# ...except if it passes the paddle
else if ball.posX + dirX > can.width - ball.size
# The paddle hit the ball! hooray!
if ball.posY > paddle.posY and ball.posY < paddle.posY + paddle.height
# Counter momentum
dirX = -dirX
# Let's reward the player :)
if score <= 50
score += 5
lives = 1
else if score <= 250
score += 10
lives = 2
# This time we make the paddle smaller so it's more of a challenge
paddle.height = can.height / 3
else if score <= 500
score += 50
lives = 3
paddle.height = can.height / 5
else if score <= 1000
score += 100
lives = 5
paddle.height = can.height / 7
else if score <= 5000
score += 1000
lives = 10
paddle.height = can.height / 10
else
score += 1500
# uh oh :( the player missed the ball. Better penalize them
else
# stop the animation so we don't decrement by 1 for every frame
window.cancelAnimationFrame animID
lives--
# clear the canvas, reset the ball and...
clear()
ball.posX = can.width / 2 - ball.size / 2
ball.posY = Math.round(Math.random() * can.height)
# ...restart the animation
window.requestAnimationFrame animloop
# Move the ball!
dirY = -dirY if ball.posY + dirY > can.height - ball.size or ball.posY + dirY < ball.size
ball.posX += dirX
ball.posY += dirY
# create the paddle
rect(paddle.posX, paddle.posY, paddle.width, paddle.height, 'yellow')
# update the canvas if the window ever gets resized
resize = ->
can.width = $(window).width()
can.height = $(window).height()
do draw
# A nice function representing our animation loop
animloop = ->
# Here's where we change that necessary identifier in earlier comments
animID = requestAnimationFrame(animloop)
# Has the player lost all of their lives?
if lives < 0
# Oh no, they have! Better stop the game and tell them their score.
clear()
ctx.font = '30pt Calibri'
ctx.textAlign = 'center'
ctx.fillStyle = 'white'
ctx.fillText "SCORE: #{score}", can.width / 2, can.height / 2
ctx.font = '15pt Calibri'
ctx.fillText "Created by Declan de Wet (declandewet@me.com)", can.width / 2, (can.height / 2) + 40
# No, they haven't - DRAW!
else draw()
return animID
init = ->
do animloop
$(window).on 'resize', resize
$(document).on 'mousemove', onMouseMove
do init
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment