GabeLinux

Web - 9 - 2

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