Advertisement
GabeLinux

Web - 7 - 4

Feb 20th, 2017
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // declare all our variables
  2. const WIDTH = 720
  3. const HEIGHT = 480
  4. const GROUNDHEIGHT = HEIGHT - 40
  5. const GRAVITY = -1
  6. const GAMESPEED = 1
  7. const TILESPEEDS = {
  8.   ground: 0.25,
  9.   backdrop: 0.1
  10. }
  11.  
  12. const canvas = document.querySelector('#game')
  13. canvas.width = WIDTH
  14. canvas.height = HEIGHT
  15.  
  16. const ctx = canvas.getContext('2d')
  17.  
  18. const imgLoader = createImageLoader()
  19.  
  20. const playerAnimations = Object.assign({},
  21.   createAnimation('run', 'images/kenny/run', 6, 7),
  22.   createAnimation('idle', 'images/kenny/idle', 2, 25),
  23.   createAnimation('jump', 'images/kenny/jump', 1, 0, false),
  24.   createAnimation('fall', 'images/kenny/fall', 1, 0, false),
  25.   createAnimation('dead', 'images/kenny/hit', 1, 0, false)
  26. )
  27.  
  28. const groundImage = imgLoader.loadImage('images/tile.png')
  29. const holeImage = imgLoader.loadImage('images/hole.png')
  30. const backdropImage = imgLoader.loadImage('images/backdrop.png')
  31.  
  32. imgLoader.loadAll()
  33.   .then(() => { gameStart() })
  34.  
  35. let prevLoop
  36. let prevDraws = []
  37. let keysPressed = []
  38. let player
  39. let score
  40. let ground
  41. let backdrop
  42.  
  43. // create an image loader that will use promises to load all of the given images
  44. function createImageLoader() {
  45.   let l = {}
  46.   l.images = []
  47.  
  48.   l.loadImage = (src) => {
  49.     let img = new Image()
  50.     img.src = src
  51.  
  52.     let p = new Promise((resolve, reject) => {
  53.       img.addEventListener('load', () => { resolve(img) })
  54.       img.addEventListener('error', (err) => { reject(new Error('Image failed to load')) })
  55.     })
  56.  
  57.     l.images.push(p)
  58.  
  59.     return img
  60.   }
  61.  
  62.   l.loadAll = () => {
  63.     return Promise.all(l.images)
  64.       .then((images) => l.images = images)
  65.       .catch((error) => { console.error(error) })
  66.   }
  67.  
  68.   return l
  69. }
  70.  
  71. // make an animation object with our parameters and loaded animation frames
  72. function createAnimation(name, path, length, time, loop = true) {
  73.   let a = {}
  74.   a[name] = {}
  75.  
  76.   a[name].frames = new Array(length).fill(null).map((val, indx) => imgLoader.loadImage(`${path}/${indx}.png`))
  77.   a[name].frameTime = time
  78.   a[name].loop = loop
  79.  
  80.   return a
  81. }
  82.  
  83. // make a player object that will animate and jump
  84. function createPlayer(animations, x, scale = 1) {
  85.   let p = {}
  86.  
  87.   p.state = 'run'
  88.   p.prevState = p.state
  89.  
  90.   p.frame = 0
  91.   p.frameTicks = 0
  92.   p.animations = animations
  93.  
  94.   p.frameTime = p.animations[p.state].frameTime
  95.   p.image = p.animations[p.state]['frames'][p.frame]
  96.  
  97.   p.scale = scale
  98.   p.width = p.image.width * p.scale
  99.   p.height = p.image.height * p.scale
  100.   p.x = x
  101.   p.y = GROUNDHEIGHT - p.height
  102.  
  103.   p.vel = 0
  104.   p.forces = 0
  105.   p.mass = 200
  106.  
  107.   p.score = 0
  108.  
  109.   p.getRight = () => {
  110.     return p.x + p.width
  111.   }
  112.  
  113.   p.getBottom = () => {
  114.     return p.y + p.height
  115.   }
  116.  
  117.   p.updateImage = () => {
  118.     const bottom = p.getBottom()
  119.     p.image = p.animations[p.state]['frames'][p.frame]
  120.     p.width = p.image.width * p.scale
  121.     p.height = p.image.height * p.scale
  122.     p.y = bottom - p.height
  123.   }
  124.  
  125.   p.updateAnimation = (delta) => {
  126.     p.frameTicks += 0.06 * delta
  127.  
  128.     if ((p.animations[p.state].loop || p.frame < p.animations[p.state]['frames'].length - 1) && p.frameTicks >= p.frameTime) {
  129.       p.frame++
  130.       p.frameTicks = 0
  131.  
  132.       if (p.frame === p.animations[p.state]['frames'].length)
  133.         p.frame = 0
  134.  
  135.       p.updateImage()
  136.     }
  137.   }
  138.  
  139.   p.setState = (state) => {
  140.     if (state === p.state) return
  141.  
  142.     p.frame = 0
  143.     p.frameTicks = 0
  144.     p.state = state
  145.     p.frameTime = p.animations[p.state].frameTime
  146.  
  147.     p.updateImage()
  148.   }
  149.  
  150.   p.addForce = (amount) => {
  151.     p.forces += amount
  152.   }
  153.  
  154.   p.jump = () => {
  155.     if (p.state === 'run' || p.state === 'idle') {
  156.       p.prevState = p.state
  157.       p.addForce(500)
  158.     }
  159.   }
  160.  
  161.   p.checkGround = (delta) => {
  162.     const groundCollisions = ground.tiles.filter((val) =>
  163.       val.type === 'ground' &&
  164.       p.x < val.getRight() &&
  165.       p.getRight() > val.x &&
  166.       p.getBottom() - p.vel * delta >= val.y &&
  167.       p.y < val.y
  168.     )
  169.  
  170.     return groundCollisions
  171.   }
  172.  
  173.   p.simulatePhysics = (delta) => {
  174.     const onGroundTiles = p.checkGround(delta)
  175.  
  176.     if (!onGroundTiles.length || p.forces || p.vel) {
  177.       const dt = !p.vel ? 1 : delta
  178.       p.addForce(GRAVITY)
  179.  
  180.       const acceleration = p.forces / p.mass
  181.       p.y -= dt * (p.vel + dt * acceleration / 2)
  182.  
  183.       const nextAcceleration = GRAVITY / p.mass
  184.       p.vel += dt * (acceleration + nextAcceleration) / 2
  185.  
  186.       p.forces = 0
  187.     }
  188.  
  189.     if (onGroundTiles.length && p.getBottom() !== onGroundTiles[0].y) {
  190.       p.setState(p.prevState)
  191.       p.y = onGroundTiles[0].y - p.height
  192.       if (p.vel < 0)
  193.         p.vel = 0
  194.     }
  195.   }
  196.  
  197.   p.update = (delta) => {
  198.     p.simulatePhysics(delta)
  199.  
  200.     if (p.vel > 0)
  201.       p.setState('jump')
  202.  
  203.     if (p.vel < 0)
  204.       p.setState('fall')
  205.  
  206.     if (p.y > HEIGHT)
  207.       p.setState('dead')
  208.  
  209.     if (p.state !== 'dead')
  210.       p.score += 0.0125 * delta * GAMESPEED
  211.  
  212.     p.updateAnimation(delta)
  213.   }
  214.  
  215.   p.draw = () => {
  216.     ctx.drawImage(p.image, p.x, p.y, p.width, p.height)
  217.  
  218.     return [{
  219.       x: p.x,
  220.       y: p.y,
  221.       width: p.width,
  222.       height: p.height
  223.     }]
  224.   }
  225.  
  226.   return p
  227. }
  228.  
  229. // create a score object which will update and draw each frame
  230. function createScore(player, font, size, color, x, y) {
  231.   let s = {}
  232.  
  233.   s.player = player
  234.   s.text = 'Score: ' + parseInt(s.player.score)
  235.   s.font = font
  236.   s.size = size
  237.   s.color = color
  238.   s.x = x
  239.   s.y = y
  240.  
  241.   s.update = () => {
  242.     s.text = 'Score: ' + parseInt(s.player.score)
  243.   }
  244.  
  245.   s.draw = () => {
  246.     ctx.font = `${size}px ${font}`
  247.     ctx.fillStyle = s.color
  248.     ctx.textBaseline = 'hanging'
  249.     ctx.fillText(s.text, s.x, s.y)
  250.  
  251.     return [{
  252.       x: s.x,
  253.       y: s.y,
  254.       width: ctx.measureText(s.text).width,
  255.       height: s.size
  256.     }]
  257.   }
  258.  
  259.   return s
  260. }
  261.  
  262. // create an image tile with the given size and coordinates
  263. function createTile(image, x, y, width, height, type, speed) {
  264.   let t = {}
  265.  
  266.   t.type = type
  267.  
  268.   t.speed = speed
  269.   t.image = image
  270.   t.x = x
  271.   t.y = y
  272.   t.width = width
  273.   t.height = height
  274.  
  275.   t.getRight = () => {
  276.     return t.x + t.width
  277.   }
  278.  
  279.   t.getBottom = () => {
  280.     return t.y + t.height
  281.   }
  282.  
  283.   t.update = (delta) => {
  284.     t.x -= t.speed * GAMESPEED * delta
  285.  
  286.     if (t.getRight() < 0)
  287.       return false
  288.  
  289.     return true
  290.   }
  291.  
  292.   t.draw = () => {
  293.     ctx.drawImage(t.image, t.x, t.y, Math.ceil(t.width), t.height)
  294.  
  295.     return {
  296.       x: t.x,
  297.       y: t.y,
  298.       width: t.width,
  299.       height: t.height
  300.     }
  301.   }
  302.  
  303.   return t
  304. }
  305.  
  306. // create moving ground tiles with a chance of obstacles
  307. function createGround(image, holeImage) {
  308.   let g = {}
  309.  
  310.   g.height = HEIGHT - GROUNDHEIGHT
  311.   g.y = GROUNDHEIGHT
  312.  
  313.   g.speed = TILESPEEDS.ground
  314.   g.image = image
  315.   g.tileWidth = image.width * (g.height / image.height)
  316.   g.tiles = new Array(Math.ceil(WIDTH / g.tileWidth) + 1).fill(null).map((val, index) =>
  317.     createTile(image, index * g.tileWidth, g.y, g.tileWidth, g.height, 'ground', g.speed)
  318.   )
  319.  
  320.   g.holeImage = holeImage
  321.   g.holeLength = 3
  322.   g.obstacleChance = 9
  323.  
  324.   g.holeTiles = 0
  325.   g.lastObstacleTile = g.tiles.length
  326.  
  327.   g.update = (delta) => {
  328.     let shiftTiles = false
  329.     g.tiles.forEach((val) => {
  330.       if (!val.update(delta)) shiftTiles = true
  331.     })
  332.  
  333.     if (shiftTiles) {
  334.       g.tiles.shift()
  335.  
  336.       const x = g.tiles[g.tiles.length - 1].getRight()
  337.       g.tiles[g.tiles.length] = createTile(g.image, x, g.y, g.tileWidth, g.height, 'ground', g.speed)
  338.  
  339.       if (g.lastObstacleTile > 6 && Math.floor(Math.random() * (g.obstacleChance + 1)) === g.obstacleChance) {
  340.         g.lastObstacleTile = 0
  341.         g.holeTiles = g.holeLength
  342.       } else {
  343.         g.lastObstacleTile++
  344.       }
  345.  
  346.       if (g.holeTiles) {
  347.         g.lastObstacleTile = 0
  348.         g.holeTiles--
  349.         g.tiles.pop()
  350.         const x = g.tiles[g.tiles.length - 1].getRight()
  351.         g.tiles[g.tiles.length] = createTile(g.holeImage, x, g.y, g.tileWidth, g.height, 'hole', g.speed)
  352.       }
  353.     }
  354.   }
  355.  
  356.   g.draw = () => {
  357.     let draws = []
  358.  
  359.     g.tiles.forEach((val) => {
  360.       draws.push(val.draw())
  361.     })
  362.  
  363.     return draws
  364.   }
  365.  
  366.   return g
  367. }
  368.  
  369. // create a backdrop for our game
  370. function createBackdrop(image) {
  371.   let b = {}
  372.  
  373.   b.x = 0
  374.   b.y = 0
  375.   b.width = image.width * (GROUNDHEIGHT / image.height)
  376.   b.height = GROUNDHEIGHT
  377.  
  378.   b.speed = TILESPEEDS.backdrop
  379.   b.image = image
  380.   b.tiles = new Array(Math.ceil(WIDTH / b.width) + 1).fill(null).map((val, index) =>
  381.     createTile(b.image, index * b.width, b.y, b.width, b.height, 'backdrop', b.speed)
  382.   )
  383.  
  384.   b.update = (delta) => {
  385.     let shiftTiles = false
  386.     b.tiles.forEach((val) => {
  387.       if (!val.update(delta)) shiftTiles = true
  388.     })
  389.  
  390.  
  391.     if (shiftTiles) {
  392.       b.tiles.shift()
  393.  
  394.       const x = b.tiles[b.tiles.length - 1].getRight()
  395.       b.tiles[b.tiles.length] = createTile(b.image, x, b.y, b.width, b.height, 'backdrop', b.speed)
  396.     }
  397.   }
  398.  
  399.   b.draw = () => {
  400.     b.tiles.forEach((val, index) => {
  401.       val.draw()
  402.     })
  403.   }
  404.  
  405.   return b
  406. }
  407.  
  408. // when all images have loaded, create our needed objects and start drawing
  409. function gameStart() {
  410.   ctx.fillStyle = '#FFFFFF'
  411.   ctx.fillRect(0, 0, WIDTH, HEIGHT)
  412.  
  413.   player = createPlayer(playerAnimations, 100, 0.1)
  414.   score = createScore(player, 'Roboto', 32, 'black', 25, 25)
  415.   ground = createGround(groundImage, holeImage)
  416.   backdrop = createBackdrop(backdropImage)
  417.  
  418.   prevLoop = performance.now()
  419.   window.requestAnimationFrame(gameLoop)
  420.  
  421.   document.addEventListener('keydown', (e) => {
  422.     const key = e.key
  423.  
  424.     if (!keysPressed.includes(key))
  425.       keysPressed.push(key)
  426.   })
  427. }
  428.  
  429.  
  430. // update all our game objects
  431. function update(delta) {
  432.   if (keysPressed.includes(' '))
  433.     player.jump()
  434.  
  435.   keysPressed = []
  436.  
  437.   backdrop.update(delta)
  438.   ground.update(delta)
  439.   player.update(delta)
  440.   score.update()
  441. }
  442.  
  443. // clear the areas that were drawn last frame and draw updated objects
  444. function draw() {
  445.   backdrop.draw()
  446.   ground.draw()
  447.   player.draw()
  448.   score.draw()
  449. }
  450.  
  451. // sync our game loop to the window framerate, then update and draw each frame
  452. function gameLoop(time) {
  453.   const delta = time - prevLoop
  454.   prevLoop = time
  455.  
  456.   update(delta)
  457.   draw()
  458.  
  459.   window.requestAnimationFrame(gameLoop)
  460. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement