Skip to content

Instantly share code, notes, and snippets.

@zeffii
Last active September 29, 2015 09:42
Show Gist options
  • Save zeffii/c21da8df731bfd2f9d2a to your computer and use it in GitHub Desktop.
Save zeffii/c21da8df731bfd2f9d2a to your computer and use it in GitHub Desktop.
Rocket_lander rdx Vec2d FX1
<div class='hawthorn'>
<canvas width="600px" height="600px"></canvas>
<br>
<button class='start'>Start!!!</button>
<button class='end'>End!!!</button>
<input id='debug_checkbox' type="checkbox" name="debug_stats" value="debug" checked>Display Debug Info
<input type="radio" id="easy" name="difficulty" value="Easy" checked/> Easy
<input type="radio" id="medium" name="difficulty" value="Medium" /> Medium
<input type="radio" id="harder" name="difficulty" value="Harder" /> Harder
</br>
<div class='instructions'><div class='inner'>
<ul>
<li>
<span class='kb'>W</span>, <span class='kb'>S</span> for upper jet bursts
</li>
<li>
<span class='kb'>A</span>, <span class='kb'>D</span> for lower rocket direction
</li>
<li>
<span class='kb'>E</span> cut engines,
<span class='kb'>Up</span> throttle up,
<span class='kb'>Down</span> throttle down
</li>
</ul>
</div>
</div>
</div>
###
Done for educational purposes only. This is not my idea of efficient or pretty code.
If this message is still here, the code hasn't been refactored much.
Cleanups, additions
[ ] horizontal delta velocity considered during landing
[ ] topple animation, detonation of combustables
[ ] smoke / steam on water/rocket proximity
[ ] switch for widescreen mode (current hardcoded 600*600 )
###
landed = false
tick = 0
show_text = true
show_debug = true
timerVar = {}
difficulty = 'Easy'
## ------------- util functions ----------------------------- ##
color = (r,g,b) -> 'rgb(' + [r, g, b].toString() + ')'
colorA = (r,g,b,a) -> 'rgba(' + [r, g, b, a].toString() + ')'
drawPolygonStroke = (ctx, loc, co, col, lw) ->
ctx.strokeStyle = col
ctx.beginPath()
ctx.lineWidth = lw
ctx.moveTo(co[0].x + loc.x, co[0].y + loc.y)
for i in [1...co.length]
ctx.lineTo(co[i].x + loc.x, co[i].y + loc.y)
ctx.stroke()
drawPolygonFill = (ctx, loc, co, col) ->
ctx.fillStyle = col
ctx.beginPath()
ctx.moveTo(co[0].x + loc.x, co[0].y + loc.y)
for i in [1...co.length]
ctx.lineTo(co[i].x + loc.x, co[i].y + loc.y)
ctx.fill()
## ------- objects ------------------------------------ ##
Meteo = ->
@.update = ->
a = Math.sin(@.PI) * 3
b = Math.sin(@.PI * 4) * 3
c = Math.sin(@.PI * 10) * 3
d = Math.sin(@.PI * 2) * 2
@.force = ((a + b + c + d) * 4)
if Math.random() > 0.7
@.PI += 0.01
if @.PI > (30 * Math.PI)
@.PI = 0.0
@.reset = ->
@.PI = Math.random() * 30 * Math.PI
@.force = 0
@.reset()
return @
Rocket = (wind) ->
@.x = 0
@.y = -50
@.angle = 1.2
@.delta_vector2d = {x: 0, y: 0}
@.max_thrust = 24
@.stabilizer_steps = 122
@.stabilizer_step = 0
@.update = ->
old_x = @.vector2d.x
old_y = @.vector2d.y
@.angle = Math.abs(Math.cos(@.rotation))
if @.rocket_fuel <= 0
@.thrust = 0
@.rocket_fuel = 0
else
@.rocket_fuel -= @.thrust
@.jet_left_tick -= 1 if (@.jet_left_tick > 0)
@.jet_right_tick -= 1 if (@.jet_right_tick > 0)
amp = @.thrust * 0.463
upper_jets = ((@.jet_left_tick - @.jet_right_tick) * 0.007)
@.rotation += (@.thrust_direction * 0.014) + upper_jets
# ever present downwards force
downwards = 7.74
if difficulty == 'Easy'
[force_amp, rotation_amp] = [73, 0.03]
else if difficulty == 'Medium'
[force_amp, rotation_amp] = [43, 1.23]
else if difficulty == 'Harder'
[force_amp, rotation_amp] = [33, 2.93]
horizontal = ((wind.force/force_amp) * Math.abs(Math.cos(@.rotation)) * rotation_amp)
@.orientation2d = {
x: Math.cos(@.rotation - (Math.PI/2)),
y: Math.sin(@.rotation - (Math.PI/2))
}
horizontal += (@.orientation2d.x * amp)
downwards += (@.orientation2d.y * amp)
@.vector2d = {x: @.vector2d.x + horizontal, y: @.vector2d.y + downwards}
if @.vector2d.y > 700
@.reset()
@.delta_vector2d = {x: (@.vector2d.x - old_x), y: (@.vector2d.y - old_y)}
if @.stabilizer_step == 48
@.deploy_stabilizers = false
if @.deploy_stabilizers
@.stabilizer_step += 2
@.reset = ->
tick = 0
@.stabilizer_step = 0
@.deploy_stabilizers = false
landed = false
@.orientation2d = {x:0, y:1} # y=vertical x=horizontal
@.vector2d = {x:0, y:1.64} # y=vertical x=horizontal
@.rotation = (Math.random() - 0.5) * 0.6
if 0.0 <= @.rotation <= 0.1
@.rotation = 0.2 + (Math.random() * 0.2)
if -0.1 <= @.rotation < 0.0
@.rotation = -0.2 - (Math.random() * 0.2)
@.rocket_fuel = 2300
@.thrust = 8
@.thrust_direction = 0.00
@.jet_fuel = 4000
@.jet_duration = 8
@.jet_left_tick = 0
@.jet_right_tick = 0
# will optimize this bullshit.
@.update_thrust_direction = (dir) ->
ext = 8.9
if -ext <= @.thrust_direction <= ext
@.thrust_direction += (dir * 0.9)
else
@.thrust_direction = ext if (@.thrust_direction > ext)
@.thrust_direction = -ext if (@.thrust_direction < -ext)
@.update_thrust = (key) ->
if key == 101
@.thrust = 0
return
@.thrust = if key == 38 then @.thrust + 1 else @.thrust - 1
@.thrust = 0 if @.thrust < 0 # off
@.thrust = @.max_thrust if @.thrust > @.max_thrust # limit
@.update_jet_direction = (dir) ->
if dir == -1
@.jet_left_tick = @.jet_duration
else
@.jet_right_tick = @.jet_duration
@.reset()
return @
## ---- role one out of the factory! ----------------------------- ##
wind = new Meteo()
rocket = new Rocket(wind)
## -----------------drawing routines ----------------------------- ##
drawStabilizers = (ctx, obj, w) ->
ctx.strokeStyle = color(195, 195, 195)
ctx.beginPath()
ctx.lineWidth = 1
theta = Math.PI * 2 / obj.stabilizer_steps
y = 80
offset = Math.PI / 2
arm1 = 11.2
arm2 = 18.6
a_ = -Math.sin(theta * obj.stabilizer_step) * arm1
y_ = Math.cos(theta * obj.stabilizer_step) * arm1
B = arm2
A = Math.abs(a_)
C = Math.sqrt( (B*B) - (A*A) )
G = [[0,0], [a_, y_], [0, y_ + C]]
# left stabilizer
ctx.moveTo(obj.x + G[0][0] - w, 80 + obj.y - G[0][1])
ctx.lineTo(obj.x + G[1][0] - w, 80 + obj.y - G[1][1])
ctx.lineTo(obj.x + G[2][0] - w, 80 + obj.y - G[2][1])
# right stabilizer
ctx.moveTo(obj.x - G[0][0] + w, 80 + obj.y - G[0][1])
ctx.lineTo(obj.x - G[1][0] + w, 80 + obj.y - G[1][1])
ctx.lineTo(obj.x - G[2][0] + w, 80 + obj.y - G[2][1])
ctx.stroke()
DrawRocket = (ctx, obj, wind) ->
ctx.save()
ctx.translate(250 + obj.vector2d.x, obj.vector2d.y)
ctx.rotate(obj.rotation)
w = 3 # rocket width
# bg...tricolor
sections = [
{start: 2, end: 32, col: color(115, 115, 115)},
{start: 32, end: 52, col: color(175, 175, 175)},
{start: 52, end: 82, col: color(105, 105, 105)}
]
ctx.lineWidth = 6
for s in sections
ctx.strokeStyle = s.col
ctx.beginPath()
ctx.moveTo(obj.x, obj.y + s.start)
ctx.lineTo(obj.x, obj.y + s.end)
ctx.stroke()
# draw gradient
grd = ctx.createLinearGradient(obj.x - w, 0, obj.x + w,0)
grd.addColorStop(0, colorA(47, 47, 47, 0.5))
grd.addColorStop(1, colorA(147, 147, 147, 0.8))
ctx.fillStyle = grd
ctx.fillRect(obj.x - w, obj.y, 2*w, 80)
ctx.strokeStyle = color(155, 155, 155)
ctx.beginPath()
ctx.lineWidth = 1
# start bottom left, up, over, down, bottom
ctx.moveTo(obj.x - w, obj.y + 80)
ctx.lineTo(obj.x - w, obj.y + 10)
ctx.lineTo(obj.x - (w - 1), obj.y + 1)
ctx.lineTo(obj.x, obj.y)
ctx.lineTo(obj.x + (w - 1), obj.y + 1)
ctx.lineTo(obj.x + w, obj.y + 10)
ctx.lineTo(obj.x + w, obj.y + 80)
ctx.stroke()
drawStabilizers(ctx, obj, w)
# thrust
if not landed
coords = [
{x: -w, y: 80},
{x:obj.thrust_direction, y: 96 + (obj.thrust/2)},
{x: w, y: 80}
]
drawPolygonFill(ctx, {x: obj.x, y: obj.y}, coords, color(225, 188, 92))
draw_angled = (x_start, y_start, theta, jet_tick) ->
col = color(225,225,225)
x_dest = Math.cos(theta) * jet_tick * 1.3
y_dest = Math.sin(theta) * jet_tick * 1.3
coords = [{x:0, y:0}, {x:x_dest, y:y_dest}]
drawPolygonStroke(ctx, {x: x_start, y: y_start}, coords, col, 2)
draw_angled(obj.x - w, obj.y + 7, Math.PI + 0.4, rocket.jet_left_tick)
draw_angled(obj.x + w, obj.y + 7, -0.4, rocket.jet_right_tick)
ctx.lineWidth = 1
# left darkest Stroke
ctx.strokeStyle = color(30,30,30)
ctx.beginPath()
ctx.moveTo(obj.x - w, obj.y + 80)
ctx.lineTo(obj.x - w, obj.y + 10)
ctx.lineTo(obj.x - (w-1), obj.y + 1)
ctx.stroke()
ctx.restore()
drawBg = (ctx) ->
grd = ctx.createLinearGradient(0,0,0,600)
grd.addColorStop(0, color(117, 122, 142))
grd.addColorStop(0.41, color(110, 122, 145))
grd.addColorStop(0.45, color(90, 102, 135))
grd.addColorStop(1, color(28, 43, 98))
ctx.fillStyle = grd
ctx.fillRect(0,0,600,600)
DrawText = (ctx, location, msg, size, align, col = '#efefef') ->
textColor = col
textSize = size.toString()
fontFace = "Open Sans"
ctx.textAlign = align
ctx.fillStyle = textColor
ctx.font = textSize + "px " + fontFace
ctx.fillText(msg, location.x, location.y)
DrawBarge = (ctx) ->
gamma = Math.PI*2/300 * tick
x = 250 + Math.sin(gamma) * 2
y = 510 + Math.sin(gamma) * 3
w = 39
ctx.fillStyle = color(42, 42, 42)
ctx.beginPath()
ctx.moveTo(x - w, y)
ctx.lineTo(x - (w+4), y + 18)
ctx.lineTo(x + (w+4), y + 18)
ctx.lineTo(x + w, y)
ctx.fill()
ctx.fillStyle = color(22, 22, 72)
ctx.beginPath()
ctx.moveTo(x - w, y + 18)
ctx.lineTo(x - (w-4), y + 23)
ctx.lineTo(x + (w-4), y + 23)
ctx.lineTo(x + w, y + 18)
ctx.fill()
coords = []
theta = Math.PI * 2 / 23
for j in [0..23]
coords.push({x: Math.sin(theta*j) * 26, y: Math.cos(theta*j) * 6})
drawPolygonStroke(ctx, {x: x+0, y: y+9}, coords, color(150, 150, 60), 1)
return {x: x, y: y, w: w}
DrawMeteoData = (ctx, wind) ->
ypos = 280
coords = [{x:0, y:-8}, {x:wind.force, y:-8}]
drawPolygonStroke(ctx, {x: 300, y: ypos}, coords, color(150, 250, 160), 4)
for i in [-10..10]
drawPolygonStroke(
ctx,
{x: 300 + (7 * i), y: ypos-6},
[{x:0,y:0},{x:0, y: if i%3==0 then 10 else 4}],
color(150, 250, 160), 1
)
updateTicks = () ->
tick += 1
if tick > 300
tick = 0
if tick > 23
show_text = false
updateText = (ctx) ->
if show_text
DrawText(ctx, {x:300, y:60}, 'R T F M!', 32, 'center')
if show_debug
lineheight = 20
line_1 = 'rotation: ' + rocket.rotation.toFixed(3)
line_2 = 'rot cos: ' + rocket.angle.toFixed(3)
line_3 = 'rocket fuel: ' + rocket.rocket_fuel
for line, i in [line_1, line_2, line_3]
DrawText(ctx, {x:20, y:30 + lineheight*i}, line, 18, 'left')
if i==1 and (0.9994 <= rocket.angle <= 1.0006)
ctx.fillRect(5, 19 + (lineheight * i), 10, 10)
ypos = 30 + lineheight*4
DrawText(ctx, {x:20, y: ypos}, 'orien 2d', 18, 'left')
x1 = rocket.orientation2d.x * 14
y1 = rocket.orientation2d.y * 14
coords = [{x:0, y:0}, {x:x1, y:y1}]
xypos = {x: 120, y: ypos-4}
drawPolygonStroke(ctx, xypos, coords, '#efefef', 2)
coords = []
theta = Math.PI * 2 / 23
for j in [0..23]
coords.push({x: Math.sin(theta*j) * 15, y: Math.cos(theta*j) * 15})
drawPolygonStroke(ctx, xypos, coords, '#efefef', 1)
line5 = 'thrust: ' + rocket.thrust
DrawText(ctx, {x:20, y: lineheight*7}, line5, 18, 'left')
line6 = 'difficulty: ' + difficulty
DrawText(ctx, {x:20, y: lineheight*8}, line6, 18, 'left')
check_if_landed = (rocket, limits, ctx) ->
rx = rocket.vector2d.x
ry = rocket.vector2d.y
w = limits.w - 5
if (0.9994 <= rocket.angle <= 1.0006)
if (limits.x - w) <= (rx + 250) <= (limits.x + w)
if (limits.y+6) <= (ry + 40) <= (limits.y + 15)
console.log(rocket.delta_vector2d)
if rocket.delta_vector2d.y < Math.abs(0.6) # 1.6 .... 0.2
msg = 'soft landing, power down. party time.'
else
msg = 'hard landing, structure fractured..'
DrawText(ctx, {x:300, y: 300}, msg, '20', 'center')
return true
return false
Draw = (ctx) ->
drawBg(ctx)
limits = DrawBarge(ctx)
landed = check_if_landed(rocket, limits, ctx)
DrawRocket(ctx, rocket, wind)
DrawMeteoData(ctx, wind)
if landed
window.clearInterval timerVar
updateTicks()
if tick == 13
console.log('deploy!!!')
rocket.deploy_stabilizers = true
updateText(ctx)
rocket.update()
wind.update()
removeTimer = ->
rocket.reset()
wind.reset()
window.clearInterval timerVar
addTimer = (ctx) ->
removeTimer() # remove any existing timer
tick = 0
show_text = true
timerVar = setInterval((->
Draw(ctx)
return
), 50)
$(document).ready( ->
ctx = $("canvas")[0].getContext('2d');
DrawText(ctx, {x:300, y:100}, 'Occupy Mars!', '65', 'center', '#555555')
DrawText(ctx, {x:300, y:150}, 'Play first stage landing game', '30', 'center', '#555555')
$('.start').click( -> addTimer(ctx) )
$('.end').click( -> removeTimer() )
$( "body" ).keypress( (event) ->
if (event.which == 97 ) # A
rocket.update_thrust_direction(-1)
else if (event.which == 100) # D
rocket.update_thrust_direction(1)
else if (event.which == 119) # W
rocket.update_jet_direction(-1)
else if (event.which == 115) # S
rocket.update_jet_direction(1)
else if (event.which == 101) # up / down
rocket.update_thrust(event.which)
)
$( "body" ).keydown( (event) ->
if event.which in [38, 40]
rocket.update_thrust(event.which)
)
$('input#debug_checkbox').change ( ->
show_debug = $( this )[0].checked
)
$('input:radio[name="difficulty"]').change( ->
difficulty = $(this).val()
)
)
## ------------------------------ END --------------------------------- ##
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
@import url(http://fonts.googleapis.com/css?family=Pacifico|Open+Sans:300)
@link-font: "Open Sans"
body
font-family: Open Sans
.kb
border: 1px solid #dddddd
background-color: #666666
color: #363636
padding: 2px 8px 3px 8px
background: #ffffff
background: -moz-linear-gradient(left, #ffffff 0%, #f6f6f6 47%, #ededed 100%)
background: -webkit-gradient(linear, left top, right top, color-stop(0%, #ffffff), color-stop(47%, #f6f6f6), color-stop(100%, #ededed))
background: -webkit-linear-gradient(left, #ffffff 0%, #f6f6f6 47%, #ededed 100%)
background: -o-linear-gradient(left, #ffffff 0%, #f6f6f6 47%, #ededed 100%)
background: -ms-linear-gradient(left, #ffffff 0%, #f6f6f6 47%, #ededed 100%)
background: linear-gradient(to right, #ffffff 0%, #f6f6f6 47%, #ededed 100%)
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#ededed',GradientType=1 )
ul
margin: 0
padding-left: 0
li
margin: 6px 0 11px 0
list-style-type: none
body
background-color: #dedede
.instructions
width: 600px
padding: 0px
margin-top: 10px
background-color: #fefefe
.inner
padding: 10px
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment