main.coffee | |
|---|---|
sketch = (p) ->
sfx = (() ->
files = [
"sfx/boost2.wav",
"sfx/jump1.wav",
"sfx/death.wav",
"sfx/drone.mp3",
"sfx/track1v2.mp3"
]
objects = (new Audio( file ) for file in files)
play = (n) ->
if objects[n].currentTime != 0
objects[n].currentTime = 0;
objects[n].play();
return
pause = (n) ->
objects[n].pause()
set_looping = (n) ->
objects[n].addEventListener 'ended', (() ->
play n)
preload = (obj) ->
obj.addEventListener 'loaded', (() ->
console.log "loaded", obj)
preload obj for obj in objects
crash = -> play 2
jump = -> play 1
boost = -> play 0
set_looping 3
drone = -> play 3
drone_stop = -> pause 3
set_looping 4
track = -> play 4
track_stop = -> pause 4
crash: crash
boost: boost
jump: jump
drone: drone
drone_stop: drone_stop
track: track
track_stop: track_stop
)()
gfx = (() ->
files = [
"gfx/test-street2.png", # 0
"gfx/test-warn.png", # 1
"gfx/test-boost2.png", # 2
"gfx/car2x32.png", # 3
"gfx/shadow3.png", # 4
"gfx/logo320b.png", # 5
"gfx/boost-empty.png", # 6
"gfx/boost-full.png" # 7
]
objects = (p.loadImage imgname for imgname in files)
preview = () ->
previewsize = 32
p.stroke 0, 0, 100
p.fill 0, 0, 100
p.image img, x*previewsize, 0, previewsize, previewsize for img, x in objects
return
get = (n) ->
objects[n]
car = -> get 3
car_shadow = -> get 4
logo = -> get 5
boost_full = -> get 7
boost_empty = -> get 6
space = -> get 8
streets = [ 0, 1, 2 ]
street = (n) -> get streets[n]
preview: preview
car: car
car_shadow: car_shadow
logo: logo
boost_full: boost_full
boost_empty: boost_empty
space: space
street: street
)() | |
Timecalculates a time delta to gain framerate-independence | time = (() ->
now = previous = delta = 0
update = () ->
previous = now
now = p.millis()
delta = (now - previous)/ 1000
return
get_delta = () -> delta
update: update
delta: get_delta
)() |
Trackcore track function that delivers the street | track = (() ->
track_depth = 3000
street_count = 32
street_gap = track_depth/street_count |
| depth iterator, for looping the z-translation | depth_iter = (i, offset) ->
if offset == undefined then offset = 0
offset+(i*street_gap)
track_scale = 1200 |
| standard noise | src_perlin = (scale=track_scale, offset=0, size=100) ->
((step) ->
[(p.noise(step+offset)-0.3)*scale,
(p.noise(step+211+offset)-0.3)*scale,
size,
0])
|
| straight line | src_straight = ->
(() ->
[ 0, 0, 100, 0 ]) |
| lerped line | src_line = (seeda, seedb, len) ->
((step) ->
x = step / len
[ p.lerp( p.noise( seeda ), p.noise( seedb ), x ),
p.lerp( p.noise( seeda + 211 ), p.noise( seedb + 211 ), x ),
100,
0 ]) |
| make a track approach 0 around range | zerofy = (range,track) ->
((step)->
t = track(step)
t[0] *= p.min(1,step/range)
t[1] *= p.min(1,step/range)
t) |
| scale positions for a track | track_scale = (scale, track) ->
((step) ->
t = track step
t[0] *= scale
t[1] *= scale
t) |
| scale steps for a track | step_scale = (scale,track) ->
((step) ->
track step * scale) |
| modify street diameter | diam_scale = (scale, track) ->
((step) ->
t = track step
t[2] *= scale
t)
|
| add walls to track | wallify = (mod,offset,track) ->
((step) ->
t = track step
if (( step + offset )% mod ) < street_gap * 3
t[2] = 220
t[3] = 1
t) |
| add boosters to track | boostify = (mod,offset,track) ->
((step) ->
t = track step
if (( step + offset )% mod ) < street_gap * 3
t[3] = 2
t) |
| move a track | move = (x, y, track) ->
((step) ->
t = track step
t[0] += x
t[1] += y
t) |
| delay a tracks steps | delay = (d, track) ->
((step) ->
track(step-d)) |
| modulo a tracks steps | mod = (m, track) ->
((step) ->
track step % m) |
| switch from track a to track b | shunt = (dist, tracka, trackb) ->
((step) ->
if step < dist
tracka step
else |
| t = tracka dist move( t[0], t[1], delay( dist, trackb ))( step )) | trackb step)
comp_three_straight = ->
((step) ->
length = 7200
wallify( length, length / 2.3,
boostify( length, length / 12,
mod( length / 3,
shunt( length / 6,
move( 0, 9000, src_straight()),
src_straight()))))(step))
comp_fast_perlin_snake = (offset=0,size=100)->
((stepo) ->
length = 6000
step = stepo + ( offset * length )
sscale = 0.0004
tscale = 1300
src = step_scale( sscale, src_perlin( tscale, offset * 120000, size ))
offlen = offset * length
if step % length < length / 2
boostify( length / 2, 0, src )(step)
else
wallify( length / 2, 0, src)(step))
street_at = (step) ->
[ comp_fast_perlin_snake(0)(step),
comp_fast_perlin_snake(0.3)(step),
comp_fast_perlin_snake(0.6)(step)
] |
| draw helper: z-translate a drawing function | put_at_depth = (depth, draw_fn) ->
p.pushMatrix()
p.translate 0, 0, depth
p.fill 0, 0, 100-p.abs(depth/track_depth*100)
draw_fn()
p.popMatrix()
return
plot_sprite = (street) ->
p.image gfx.street(street[3]), street[0]-(street[2]/2), street[1]-(street[2]/2), street[2], street[2]
p.translate 0, 0, 1
plot_sprites = (streets) ->
(() -> plot_sprite street for street in streets)
draw = () ->
offset = player.depth() % street_gap
rest = player.depth() - offset
p.noStroke()
p.fill 0, 0, 100
put_at_depth depth_iter(-i, offset), plot_sprites track.street_at depth_iter(i, rest) for i in [street_count-1..0] by -1
street_at: street_at
draw: draw
)() |
| Player player entity, also controls camera | player = (() ->
alive = false
turnSpeed = 600
driveSpeed = 1000
camY = 60
camZ = 80 #120
camAhead = 100
boost = (() ->
count = 1
hold = false
add = () ->
if count < 3
count += 1
sfx.boost()
hold = true
return
held = () -> hold
release = () ->
hold = false
draw = ->
ww = p.width / 8
p.image gfx.boost_empty(), x, 0, ww, ww for x in [ww*2.5..ww*4.5] by ww
if count > 0
p.image gfx.boost_full(), x, 0, ww, ww for x in [ww*2.5..ww*(2.5+count-1)] by ww
return
get_count = -> count
spend = ->
if count > 0
count -= 1
true
else
false
reset = ->
count = 1
add: add
spend: spend
held: held
release: release
draw: draw
count: get_count
reset: reset
)()
position = new p.PVector 0, -200
shadow = 0
force = new p.PVector 0, 0
upvector = new p.PVector 0, 1
upforce = new p.PVector 0, 1
depth = 0
jumpvector = new p.PVector 0, 0
jumppotencial = 30
jumptime = 0.4
jumpforce = 0
sinkpotential = 100
sinktime = 2
sinkforce = 0
floor_contact = false
hover_escape = 50
hover_downto = 10 #10
hover_upto = 10 #5
crash_tolerance = 20
get_position = -> position
get_depth = -> depth
crash = ->
if alive
sfx.crash()
sfx.drone()
sfx.track_stop()
boost.reset()
alive = false
start = ->
alive = true
sfx.drone_stop()
sfx.track()
has_alive = -> alive
closest_street = (streets) ->
dists = ((p.dist position.x, position.y, street[0], street[1])-(street[2]/2) for street in streets)
min = Math.min dists...
(streets[i] for d,i in dists when d is min)[0]
find_shadow = (street) ->
shadow_vector = new p.PVector street[0]-position.x, street[1]-position.y
length = shadow_vector.mag()
surface = street[2]/2
shadow = p.PVector.limit( shadow_vector, length - surface ).mag() |
| use the closest street to determine an appropriate up-vector for the camera as well as any pushing/pulling gravity forces | adjust_forces_to = (street, dt) ->
vector = new p.PVector street[0]-position.x, street[1]-position.y
upforce.set vector
upforce.normalize()
length = vector.mag()
surface = street[2]/2
force.set 0, 0, 0
if jumpforce > 0
if floor_contact == true then jumpvector.set upforce
force.set p.PVector.mult jumpvector, -jumpforce
jumpforce = p.max jumpforce - ( jumppotencial / jumptime * dt ), 0
floor_contact = false
else if length > ( surface + hover_escape )
force.set p.PVector.mult upforce, sinkforce
force.limit length - ( surface + hover_downto )
sinkforce = p.min sinkforce + ( sinkpotential / sinktime * dt ), sinkpotential
floor_contact = false
else if length > ( surface + hover_downto )
vector.limit length - ( surface + hover_downto )
force.set vector
floor_contact = true
else if length < (surface+hover_upto)
vector.normalize()
vector.mult ((surface+hover_upto)-length)*-1
force.set vector
floor_contact = true
if length < (surface-crash_tolerance)
crash()
if floor_contact == true
sinkforce = 0
return
apply_forces = (dt) ->
upvector.add p.PVector.mult upforce, dt*5
upvector.normalize()
position.add force
return |
| speed & direction changes | drive = (x,y,dt) ->
position.add( upvector.y * x * turnSpeed * dt, upvector.x * -x * turnSpeed * dt,0)
depth += if alive then y * driveSpeed * dt else y * driveSpeed * dt / 10
return
jump = () ->
if floor_contact and boost.spend()
jumpforce = jumppotencial
sfx.jump()
return |
| update player position & up-vector | update = (dt) ->
closest = closest_street track.street_at depth
if closest[3] == 2 and floor_contact and alive
if not boost.held()
boost.add()
else
boost.release()
adjust_forces_to closest, dt
apply_forces dt
find_shadow closest
return
plot_circle = (circle) ->
p.ellipse circle[0], circle[1], circle[2], circle[2]
return
|
| draw the player | draw = () ->
p.pushMatrix()
p.translate position.x, position.y
p.rotateZ (p.atan2 upforce.y, upforce.x)-p.HALF_PI
if alive
p.noStroke()
p.fill 0, 0, 100
p.scale 0.5
p.image gfx.car_shadow(), -16, -8+shadow, 32, 32
p.scale 0.6
p.image gfx.car(), -64, -64, 128, 128
p.popMatrix()
p.camera()
p.hint p.DISABLE_DEPTH_TEST
if alive then boost.draw() else p.image gfx.logo(), 0, 0
return
cam = ->
p.camera position.x-(upvector.x*camY), position.y-(upvector.y*camY), camZ,
position.x, position.y, -camAhead,
upvector.x, upvector.y, 0
p.perspective(p.PI/3.0, p.width/p.height, 10, 3000)
return
position: get_position
depth: get_depth
drive: drive
jump: jump
update: update
draw: draw
cam: cam
alive: has_alive
start: start
)() |
| Key State | keys = (() ->
state =
ff: false
left: false
right: false
jump: false
set = (keyCode, nustate) ->
switch keyCode
when p.LEFT then state.left = nustate
when p.RIGHT then state.right = nustate
when p.UP then state.jump = nustate
when p.DOWN then state.start = nustate
when 8 then abort = true
return
get_state = ->
state
set: set
state: get_state
)()
|
Setup | p.setup = ->
p.size 320, 240, p.OPENGL
p.frameRate 30
p.rectMode p.CENTER
p.ellipseMode p.CENTER
p.colorMode p.HSB, 100
p.noiseDetail 2
sfx.drone()
return
|
Draw | draw = ->
time.update() |
if player.alive()
player.drive (if keys.state().left then -1 else keys.state().right), 1, time.delta()
if keys.state().jump then player.jump()
else
player.drive 0, 1, time.delta()
if keys.state().start then player.start()
player.update time.delta() | |
p.background 0 | |
player.cam()
track.draw()
player.draw()
return
preload = ->
p.background 0
gfx.preview()
p.draw = draw
p.draw = preload | |
Events | p.keyPressed = ->
keys.set p.keyCode, true
p.keyReleased = ->
keys.set p.keyCode, false |
| end of sketch | return |
Init | window.onload = ->
canvas = document.getElementById("canvas")
p5 = new Processing canvas, sketch
canvas.style.width = "640px"
return
|