Skip to content

Instantly share code, notes, and snippets.

@manveru
Created February 13, 2011 18:31
Show Gist options
  • Save manveru/824927 to your computer and use it in GitHub Desktop.
Save manveru/824927 to your computer and use it in GitHub Desktop.
Pong in Nimrod
import math
import vector
import sdl
type
OBall = object
vector: TVector2
velocity: TVector2
radius: float
speed: float
color: uint32
OPaddle = object
vector: TVector2
target: TVector2
height, width, speed: float
color: uint32
OWorld = object
screen: sdl.PSurface
width: int16
height: int16
pause: bool
running: bool
ball: ref OBall
paddle: ref OPaddle
enemy: ref OPaddle
highlight: TVector2
score: tuple[paddle, enemy: int]
proc newPaddle(x, y, w, h: float): ref OPaddle =
new(result)
result.vector = (x, y)
result.target = (x, y)
result.height = h
result.width = w
result.color = 0x6666ff
result.speed = 8.0
proc rect(paddle: ref OPaddle): sdl.TRect =
result.x = int16(paddle.vector.x - (paddle.width / 2.0))
result.y = int16(paddle.vector.y - (paddle.height / 2.0))
result.w = uint16(paddle.width)
result.h = uint16(paddle.height)
proc draw(paddle: ref OPaddle, world: ref OWorld) =
var rect = paddle.rect
discard world.screen.fillRect(addr(rect), paddle.color)
proc updatePaddle(paddle: ref OPaddle, world: ref OWorld) =
var goal = paddle.target - paddle.vector
if goal.length > paddle.speed:
goal = goal.normalize * paddle.speed
var future = paddle.vector + goal
if future.y < (paddle.height / 2.0):
return
if (future.y + (paddle.height / 2.0)) > float(world.height):
return
paddle.vector = future
proc updateEnemy(paddle: ref OPaddle, world: ref OWorld) =
if world.ball.velocity.x > 0.0:
var targetX = paddle.vector.x
var targetY = world.ball.vector.y
var goal = (targetX, targetY) - paddle.vector
if goal.length > paddle.speed:
goal = goal.normalize * paddle.speed
paddle.target = goal
var future = paddle.vector + paddle.target
var halfHeight = float(paddle.height) / 2.0
if future.y < halfHeight or (future.y + halfHeight) > float(world.height):
return
paddle.vector = future
type THit = tuple[hit: bool, place: TVector2]
proc hitCore(paddle: ref OPaddle, x0, y0, x1, y1: float, past, future: TVector2): THit =
# line between past and future
var x2 = past.x
var y2 = past.y
var x3 = future.x
var y3 = future.y
var d = (x1-x0)*(y3-y2) - (y1-y0)*(x3-x2)
if abs(d) < 0.001:
result.hit = false
return
# never hit since parallel
var ab = ((y0-y2)*(x3-x2) - (x0-x2)*(y3-y2)) / d
if ab > 0.0 and ab < 1.0:
var cd = ((y0-y2)*(x1-x0) - (x0-x2)*(y1-y0)) / d
if cd > 0.0 and cd < 1.0:
var linx = x0 + ab*(x1-x0)
var liny = y0 + ab*(y1-y0)
result.hit = true
result.place = (linx, liny)
return
result.hit = false
proc hitEnemy(paddle: ref OPaddle, past, future: TVector2): THit =
var vector = paddle.vector
var halfWidth = paddle.width / 2.0
var halfHeight = paddle.height / 2.0
var x0 = vector.x - halfWidth
var x1 = vector.x + halfWidth
var y0 = vector.y - halfHeight
var y1 = vector.y + halfHeight
return paddle.hitCore(x0, y0, x1, y1, past, future)
proc hitPaddle(paddle: ref OPaddle, past, future: TVector2): THit =
var vector = paddle.vector
var halfWidth = paddle.width / 2.0
var halfHeight = paddle.height / 2.0
var x0 = vector.x - halfWidth
var x1 = vector.x + halfWidth
var y0 = vector.y - halfHeight
var y1 = vector.y + halfHeight
return paddle.hitCore(x0, y0, x1, y1, past, future)
proc newBall(x, y: float): ref OBall =
new(result)
result.vector = (x, y)
result.speed = 8.0
result.radius = 2.0
result.color = 0xffffff
result.velocity = (2.0, 2.0)
proc rect(ball: ref OBall): sdl.TRect =
var size = ball.radius * 2.0
result.x = int16(ball.vector.x - ball.radius)
result.y = int16(ball.vector.y - ball.radius)
result.w = uint16(size)
result.h = uint16(size)
proc draw(ball: ref OBall, world: ref OWorld) =
var rect = ball.rect
discard world.screen.fillRect(addr(rect), ball.color)
proc update(ball: ref OBall, world: ref OWorld) =
var velocity = ball.velocity
var future = ball.vector + velocity
var radius = ball.radius
if velocity.y < 0.0 and future.y < radius:
velocity.y = -velocity.y
if velocity.y > 0.0 and future.y >= float(world.height) - radius:
velocity.y = -velocity.y
if velocity.x < 0.0:
var paddle = world.paddle
var collision = paddle.hitPaddle(ball.vector, future)
if collision.hit:
velocity.x = -velocity.x
ball.speed = ball.speed + 0.1
echo(ball.speed)
velocity = velocity.normalize * ball.speed
world.highlight = collision.place
elif future.x <= radius:
echo("Enemy scores")
ball.speed = ball.speed + 0.1
ball.speed.echo()
inc(world.score.enemy)
velocity.x = -velocity.x
if velocity.x > 0.0:
var enemy = world.enemy
var collision = enemy.hitEnemy(ball.vector, future)
if collision.hit:
velocity.x = -velocity.x
ball.speed = ball.speed + 0.1
echo(ball.speed)
velocity = velocity.normalize * ball.speed
world.highlight = collision.place
elif future.x >= float(world.width):
echo("Player scores")
ball.speed = ball.speed + 0.1
ball.speed.echo()
inc(world.score.paddle)
velocity.x = -velocity.x
ball.velocity = velocity
ball.vector = ball.vector + velocity
proc drawScore(world: ref OWorld) =
var rect: sdl.TRect
rect.x = int16(world.paddle.width + world.paddle.vector.x)
rect.y = int16(3)
rect.w = int16(3)
rect.h = int16(3)
var score = world.score
for p in countdown(score.paddle, 1):
rect.x = int16(rect.x + 6)
discard world.screen.fillRect(addr(rect), 0x6666ff)
if rect.x > world.width:
echo("You Win!")
world.running = false
rect.x = int16(world.enemy.vector.x - world.enemy.width)
rect.y = int16(world.height - 6)
for p in countdown(score.enemy, 1):
rect.x = int16(rect.x - 6)
discard world.screen.fillRect(addr(rect), 0xff6666)
if rect.x <= 0:
echo("You Lose!")
world.running = false
proc draw(world: ref OWorld) =
discard world.screen.FillRect(nil, 0x0)
var center: sdl.TRect
center.x = int16(world.width / 2) - int16(1)
center.y = int16(0)
center.h = world.height
center.w = uint16(2)
discard world.screen.fillRect(addr(center), 0x333333)
var high: sdl.TRect
high.x = int16(world.highlight.x - 5.0)
high.y = int16(world.highlight.y - 5.0)
high.h = uint16(10)
high.w = uint16(10)
discard world.screen.fillRect(addr(high), 0xff0000)
world.ball.draw(world)
world.paddle.draw(world)
world.enemy.draw(world)
world.drawScore()
discard world.screen.flip
proc update(world: ref OWorld) =
world.ball.update(world)
world.paddle.updatePaddle(world)
world.enemy.updateEnemy(world)
proc fail(msg: string) =
echo(msg)
quit(1)
if sdl.Init(sdl.INIT_EVERYTHING) != 0:
fail "couldn't initialize sdl"
if sdl.enableUnicode(1) != 0:
fail "couldn't initialize unicode"
if sdl.enableKeyRepeat(25, 25) != 0:
fail "couldn't set key repetition"
sdl.wmSetCaption("Pong", "")
var world: ref OWorld
new(world)
world.width = int16(640)
world.height = int16(480)
world.screen = sdl.SetVideoMode(world.width, world.height, 32, 0)
world.ball = newBall(100.0, 100.0)
world.paddle = newPaddle(10.0, float(world.height) / 2.0, 10.0, 30.0)
world.enemy = newPaddle(float(world.width) - 10.0, float(world.height) / 2.0, 10.0, 30.0)
world.pause = false
world.running = true
world.score = (0, 0)
while world.running == true:
var event: sdl.TEvent
var eventp = addr(event)
if eventp.pollEvent() == 1:
case event.kind:
of sdl.KeyDown, sdl.KeyUp:
var key = $EvKeyboard(eventp).keysym.sym.getkeyname()
case key
of "p":
world.pause = not world.pause
of "j":
world.paddle.target = world.paddle.vector + (0.0, world.paddle.speed)
of "k":
world.paddle.target = world.paddle.vector - (0.0, world.paddle.speed)
of "escape", "q":
world.running = false
else:
echo(key)
else:
nil
sdl.delay(25)
world.update()
world.draw()
sdl.quit()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment