GabeLinux

Web - 8 - 2

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