Advertisement
GabeLinux

Web - 7 - 3

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