Youtube space invaders tutorial array collision issue

29 Views Asked by At

Hi there I hope everyone's well.

I have this game tutorial here I have nearly completed from a Mr. Chris Jones on youtube. This is the tutorial here https://www.youtube.com/watch?v=MCVU0w73uKI

I have virtually everything done that he has listed off as tasks in the video, there's just one issue it seems where in the collision detection, when the particle hits the invader grid, it erases the whole grid.

I don't believe it actually removes the grid array either as sometimes a shot will come out of an invisible grid.

Help to understand where I have mistaken something would be great but I'm also just trying to reach out and get some sound advice as the people I live and work with don't really like talking about math and programming work so it's tough to keep motivated.

I didn't find much info on op's tutorial written by others so I was hoping this would also create a paper trail for following along as I had some trouble writing the thing out, only going by what he said. I don't mind it being used as reference but if it's too much than please remove the question.

Anyways, much love and thanks.

const scoreCounter = document.querySelector('#scoreCounter')
const canvas = document.querySelector("canvas");
const c = canvas.getContext("2d");
canvas.width = 1024;
canvas.height = 576;
//Player class to be made for 
//abstraction purposes
//keep function titles simple so they make sense in usage
//Example: Player.draw() as you know it will obviously draw the player
class Particle {
  constructor({position, velocity, radius, color, fades}) {
    this.position = position
    this.velocity = velocity
    this.radius = radius
    this.color = color
    this.opacity = 1
    this.fades = fades
  }
  draw() {
    c.save()
    c.globalAlpha = this.opacity
    c.beginPath()
    c.arc(this.position.x, this.position.y, this.radius, 0, Math.PI * 2)
    c.fillStyle = this.color
    c.fill()
    c.closePath()
    c.restore()    
  }
  update() {
    this.draw()
    this.position.x += this.velocity.x
    this.position.y += this.velocity.y
    if(this.fades)
    this.opacity -= 0.01
  }
}//end of projectile class
class projectile{
  constructor({position, velocity}) {
    this.position = position
    this.velocity = velocity
    this.radius = 4
  }
  draw() {
    c.beginPath()
    c.arc(this.position.x, this.position.y, this.radius, 0, Math.PI * 2)
    c.fillStyle = 'red'
    c.fill()
    c.closePath()    
  }
  update() {
    this.draw()
    this.position.x += this.velocity.x
    this.position.y += this.velocity.y
  }
}//end of projectile class
class Player {
  constructor() {    
    this.velocity = {
      x: 0,
      y: 0,
    }
    this.rotation = 0
    this.opacity = 1
    const image = new Image();
    image.src ='./img/spaceship.png';
    image.onload = () => {
      const scale = 0.15
      this.image = image
      this.width = image.width*scale
      this.height = image.height*scale
      this.position = {
        x: canvas.width / 2 - this.width / 2,
        y: canvas.height - this.height - 20       
      }      
    }    
  }//end of construct
  rotateImage() { // separating math functionally so it's easier to read
    c.translate(
      player.position.x + player.width / 2, 
      player.position.y + player.height / 2
    )
    c.rotate(this.rotation)
    c.translate(
      -player.position.x - player.width / 2, 
      -player.position.y - player.height / 2)
  }
  draw() {
    //c.fillStyle = "red";
    //c.fillRect(this.position.x, this.position.y, this.width, this.height);
    c.save()
    c.globalAlpha = this.opacity
    this.rotateImage()
    c.drawImage(
      this.image, 
      this.position.x, 
      this.position.y,
      this.width,
      this.height)  
    c.restore()    
  }  
  updateLocation() {
    if(this.image){
    this.draw()
    this.position.x += this.velocity.x
    }    
  }
}//End of player class
class invaderProjectile{
  constructor({position, velocity}) {
    this.position = position
    this.velocity = velocity
    this.width = 3
    this.height = 10
  }
  draw() {
    c.fillStyle = 'orange'
    c.fillRect(this.position.x, this.position.y, this.width, this.height)  
     
  }
  update() {
    this.draw()
    this.position.x += this.velocity.x
    this.position.y += this.velocity.y
  }
}//end of invaderProjectile class
/*
This is the start of the Invader class.
*/
class Invader {
  constructor({position}) {    
    this.velocity = {
      x: 0,
      y: 0,
    }
    this.rotation = 0
    const image = new Image();
    image.src ='./img/invader.png';
    image.onload = () => {
      const scale = 1
      this.image = image
      this.width = image.width*scale
      this.height = image.height*scale
      this.position = {
        x: position.x,
        y: position.y       
      }      
    }    
  }//end of construct
  rotateImage() { // separating math functionally so it's easier to read
    c.translate(
      player.position.x + player.width / 2, 
      player.position.y + player.height / 2
    )
    c.rotate(this.rotation)
    c.translate(
      -player.position.x - player.width / 2, 
      -player.position.y - player.height / 2)
  }
  draw() {
    //c.fillStyle = "red";
    //c.fillRect(this.position.x, this.position.y, this.width, this.height);
    c.save()
    this.rotateImage()
    c.drawImage(
      this.image, 
      this.position.x, 
      this.position.y,
      this.width,
      this.height)  
    c.restore()    
  }  
  updateLocation({velocity}) {
    if(this.image){
    this.draw()
    this.position.x += velocity.x,
    this.position.y += velocity.y
    }    
  }
  shoot(invaderProjectiles) {
    invaderProjectiles.push(
      new invaderProjectile({
      position: {
        x: this.position.x + this.width / 2,
        y: this.position.y + this.height
      }, 
      velocity: {
        x: 0,
        y: 5        
      }
    }))    
  }
}//End of invader class

/*
Start of the grid class
this grid defines the space in which the invaders form together
*/
class Grid {
  constructor(){
    this.position = {
      x:0,
      y:0
    }    
    this.velocity = {
      x:3,
      y:0
    } 
    this.invaders = []
    const columns = Math.floor((Math.random() * 10) + 5)
    const rows = Math.floor((Math.random() * 5) + 2)
    //variable helps bounce grid to left side
    this.width = columns * 30
    for(let x=0; x<columns;x++){
      for(let y=0;y<rows;y++) {
        this.invaders.push(new Invader({position: {
          x: x*30,
          y: y*30
        }        
        }))        
      }
    }
    console.log('this.invaders:=>',this.invaders)
  } 
  updateLocation() {
    this.position.x += this.velocity.x
    this.position.y += this.velocity.y
    this.velocity.y = 0
    if (
      this.position.x + this.width >= canvas.width ||
      this.position.x <= 0) { //<= bounces grid back right
      this.velocity.x = -this.velocity.x
      this.velocity.y = 30 //brings grid down by 1 increment every bounce
    }
  }
}//end of grid class

/*
This is where we initiate the new objects
for the animate functions to use
*/
const player = new Player()
const projectiles = []
const grids = []
const invaderProjectiles = []
const particles = []
//defines the keys for input
const keys = {
  a: {pressed:false},  
  d: {pressed:false},  
  space: {pressed:false}} 

//if statement that allows user to move player left or right
function moveLeftOrRight() {
  if (keys.a.pressed && player.position.x >= 0) {
    player.velocity.x = -7 
    player.rotation = -0.15
  }
  else if (
    keys.d.pressed && 
    player.position.x +player.width <= canvas.width
  ) {
    player.velocity.x = 7 
    player.rotation = 0.15
  }
  else {player.velocity.x = 0, player.rotation = 0}
}

//refreshes the player projectile sprite so it moves across the screen
function shootProjectile() {  
  projectiles.forEach((projectile, index) => {
    if(projectile.position.y + projectile.radius <= 0){
      setTimeout(() => {
        projectiles.splice(index, 1)
      }, 0)      
    } else {
      projectile.update()
    }    
  })
}

function enemyShoot(grid) {
  invaderProjectiles.forEach((invaderProjectile, index) => {
    if(invaderProjectile.position.y + 
      invaderProjectile.height >= 
      canvas.height) {
      setTimeout(() => {
        invaderProjectiles.splice(index, 1)
      },0)
    } else{
      invaderProjectile.update()
    }    
    if(
      invaderProjectile.position.y + 
      invaderProjectile.height >=
      player.position.y &&
      invaderProjectile.position.x +
      invaderProjectile.width >=
      player.position.x &&
      invaderProjectile.position.x <= player.position.x +
      player.width
      ) {
        setTimeout(() => {
          invaderProjectiles.splice(index, 1)
          player.opacity = 0
          game.over = true
        },0)
        setTimeout(() => {          
          game.active = false
        },2000)
        createParticles({
          object: player,
          color: 'white',
          fades:true
        })
        console.log('youloselol')}    
  })
  if (frames % 100 === 0 && grid.invaders.length > 0) {
    grid.invaders[
                  Math.floor(Math.random() * grid.invaders.length
                  )].shoot(invaderProjectiles)
  }  
}//end of enemy shoot
function animateInvaderGrid() {  
  grids.forEach((grid, gridIndex) => {
    grid.updateLocation()         
    //enemie projectiles
    enemyShoot(grid)
    shootProjectile()
    // I imagine enemy collision detection is next    
    grid.invaders.forEach((invader, i) => {
      //updates the invaders location in a grid across screen
      invader.updateLocation({velocity: grid.velocity})      
      //tracking projectile for collision detection      
      projectiles.forEach((projectile, j) => {
        if (
          //if top side of projectile touches an invaders top 
          projectile.position.y - projectile.radius <= 
          invader.position.y + invader.height && 
          //and the left side touches 
          projectile.position.x + projectile.radius >= 
          invader.position.x - invader.height&& 
          projectile.position.x - projectile.radius <= 
          invader.position.x + invader.width &&
          projectile.position.y + projectile.radius >= 
          invader.position.y
          ){          
          //this whole thing is the collision detection
            setTimeout(() => {
              const invaderFound = grid.invaders.find(
                invaderLocated => 
                {return invaderLocated === invader})
              const projectileFound = projectiles.find(
                projectileLocated => projectileLocated === projectile)                
              if (invaderFound && projectileFound){
                //add the store here as well
                score += 100;
                console.log('Score:   =>',score)
                scoreCounter.innerHTML = score
                //calling the animation for explosions here
                createParticles({
                  object: invader,
                  fades:true
                })
                grid.invaders.splice(i, 1)
                projectiles.splice(j,1)
                if (grid.invaders.length > 0) {
                  const firstInvader = grid.invaders[0]
                  const lastInvader = grid.invaders[
                    grid.invaders.length - 1]
                  grid.width = 
                  lastInvader.position.x - 
                  firstInvader.position.x +
                  lastInvader.width
                  grid.position.x = firstInvader.position.x
                }
              } else  {
                grids.splice(gridIndex, 1)
              }             
            }, 0)//end setTimeout
        }
      })
    })
  })
}
//messes with it if these aren't global
let frames = 0
let randomIntervals = Math.floor((Math.random() * 500) + 500)
let game = {
  over:false,
  active: true
}
let score = 0
function spawnEnemy() {
  //if statement contained as function for readability  
  if (frames % randomIntervals ===0) {
    //console.log("randomIntervals=", randomIntervals)
    grids.push(new Grid())    
    randomIntervals = Math.floor(((Math.random() * 500) + 500))
    console.log('Random Intervals=>',randomIntervals)
    frames = 0
  }  
}
function createParticles({object,color, fades}) {
  for (let i=0;i<15;i++) {
    particles.push(
      new Particle({
        position: {
          x: object.position.x + object.width / 2,
          y: object.position.y + object.height / 2
        },
        velocity: {
          x: ((Math.random() - 0.5)*2),
          y: ((Math.random() - 0.5)*2)
        },
        radius: Math.random() * 3,
        color: color || '#BAA0DE',
        fades
      })
    )
  }   
}
function animateStars() {
  for (let i=0;i<100;i++) {
    particles.push(
      new Particle({
        position: {
          x: Math.random() * canvas.width,
          y: Math.random() * canvas.height
        },
        velocity: {
          x: 0,
          y: 0.3
        },
        radius: Math.random() * 3,
        color:'white'
      })
    )
    setTimeout(() => {
      particles.splice(i,1)        
    }, 0); 
  }     
}

function animateParticles() {
  particles.forEach(((particle, i) => {
    if(particle.position.y - particle.radius >=
      canvas.height){
        particle.position.x = Math.random() * canvas.width
        particle.position.y = -particle.radius
      }
    if(particle.opacity <= 0){
      setTimeout(() => {
        particles.splice(i,1)        
      }, 0);      
    }else {
      particle.update()
    }    
  }))
}
//animates the player sprite
function animate() {
  if(!game.active) return
  //initiate canvas  
  requestAnimationFrame(animate);
  c.fillStyle = 'black'
  c.fillRect(0,0, canvas.width, canvas.height)  
  player.updateLocation()
  
  
  animateInvaderGrid()
  animateParticles()
  
  //console.log(particles)
  //functions for gameplay  
  
  moveLeftOrRight()
  //console.log(frames) 
  spawnEnemy()  
  frames++
}
//runs the game
animate();
animateStars()
/**
 * Event listeners used for keyboard inputs
 */
addEventListener('keydown', ({key}) => {
  if (game.over) return
  switch (key) {
    case 'a': //left
    keys.a.pressed = true
    console.log('leftdown')
      break;     
    case 'd': //right
      keys.d.pressed = true  
      console.log('rightdown')
      break;     
    case ' ':  //space     
    keys.space.pressed = true
    console.log('spacedown')
    projectiles.push(
      new projectile({
        position: {
          x:player.position.x + player.width/2,
          y:player.position.y
        },
        velocity: {
          x:0,
          y:-10
        }
      })
    )
      break;      
  }}) 
addEventListener('keyup', ({key}) => {
  switch (key) {
    case 'a': //left
    keys.a.pressed = false
      break;      
    case 'd': //right
      keys.d.pressed = false   
      break;     
    case ' ':  //space  
    keys.space.pressed = false    
      break;      
  }})

I have written the program out as best I can.

I tried changing positions in the collision detection section in the if statement starting at line 335 but it still seems to target the grid when two invaders are hit at the same time.

0

There are 0 best solutions below