Loosing lives really fast when Collision happens

Hi,
I was trying to create a simple collision code for my Asteroid game. Basically I managed to create the collision between the objects (Spaceship and Asteroid), however when I created this if condition bellow:

if (xpos + asteroidWidth + vx > shiptrek.location.x &&
xpos + vx < shiptrek.location.x + shipWidth &&
ypos + asteroidHeight > shiptrek.location.y &&
ypos < shiptrek.location.y + shipHeight) {

 live = live - 1;
 }

}

What is happening is that I am loosing -10 lives, I guess it is because of the timing of collision exposure. How could I set this to make the game code understand that every time collision happens the user just loose 1 live?

if you aren’t removing the asteroid it probably just catching the collision multiple times? it’s hard to say without seeing the code.

[quote="hotfooted, post:2, topic:8627, full:true"]
if you aren't removing the asteroid it probably just catching the collision multiple times? it's hard to say without seeing the code.
[/quote]


Hi friend,

Hmm, I don´t want to remove the asteroid, basically the idea is: Having a spaceship inside the frame that have to fly around the frame without shocking with the asteroid. If the user could keep flying between shocking in 30s he moves on to the next level.

I will try to put the code here but will be a little bit large:


//THIS IS MY FRAME MODE.

//Loading font
PFont font;

//game levels
int mode; //1: intro, 2: level01, 3:level02, 4: level3, 5:gameover

//asteriodes movements
int xpos, ypos, vx, vy;

//Game features
int score;
int live;

//spaceship elements
PImage ship;
PImage ship2;
int shipsize = 100;
spaceship shiptrek;

//movement of the ship
boolean up = false;
boolean down = false;
boolean left = false;
boolean right = false;
//gif generator
PImage[] gif;
int currentPic = 0;

//Timer for the levels.
long timer = 0;
long TIMEOUT = 30000;
int time = 0;

//Asteroids
PImage asteroid;

//mouse hover for intro button:
float rectX;
float rectY;
float rectWidth;
float rectHeight;

//collision:

boolean collision = false;
float asteroidWidth = 100;
float asteroidHeight = 100;
float shipWidth = 50;
float shipHeight = 70;


void setup() {
 timer = millis();
 size(600,600);
 frameRate(40);
 mode = 1;
 xpos = 100;
 ypos = 300;
 vx = 2;
 vy = 5;
 asteroid = loadImage("asteroid.png");
 ship = loadImage("ship2.png");
 gif = new PImage[21];
 int index = 0;
 while (index < 21) {
   gif[index] = loadImage("starsfinal_" + index + ".jpg"); 
   index = index + 1;
 }
 
 //ship.resize(50,90);
 shiptrek = new spaceship();
 imageMode(CENTER);
 
 //mouse hover intro:
  rectX = 168;
  rectY = 280;
  rectWidth = 270;
  rectHeight = 128;
 

}

void draw() {
  if (mode == 1) {
    intro();
    
  } else if(mode == 2) {
    level01();
    
  } else if(mode == 3) {
    level02();
    
  } else if(mode == 4) {
    level03();
    
   } else if(mode == 5){
     gameOver();
     
   } else {
 background(25, 25, 25);
   
   }
  
}


//THIS IS THE SPACESHIP

class spaceship {
  //1. Instance variables
  int lives;
  PVector location;
  PVector velocity;
  PVector direction;
  PVector stop;
  
  //2. Constructor - same name as the class.  No void (return type)
  spaceship() {
    lives = 3;
    location = new PVector(300, 300);
    direction = new PVector(0, -0.05);
    velocity = new PVector(0, 0.2);
    stop = new PVector(0,0);
     
  }
  
  //3. Behaviour functions
  
  void show() {
    pushMatrix();
    translate(location.x, location.y);
    rotate(direction.heading());
    rotate(1.55);
    fill(255, 0, 255, 100);
    rect(0, 0, shipWidth, shipHeight);
    image(ship, 0, 0);
    popMatrix(); 
    
  }
  
  void act() {
    
    //velocity.rotate( radians(50) );
    
    location.add(velocity);
    
    if (left) direction.rotate( radians(-5) );
    if (right) direction.rotate( radians(5) );
    if (up) velocity.add(direction);
    if (down) velocity.add(stop);
    
  
    if (location.x > width || location.x < 0) {
      mode = 5;
    }
    
    if (location.y > height + 20 || location.y < -20) {
      mode = 5;
    }
    
   
  }
  
  
}


//THIS IS THE LEVEL ONE


void level01() {
  time = time + 1;
  
  if (time > 1150) mode = 3;
  imageMode(CORNER);
  background(0, 0, 0);
  image(gif[currentPic], 0, 0, 600, 600);
  currentPic++;
  if (currentPic >= 20) {
    currentPic = 0;
  }
  shiptrek.show();
  shiptrek.act();
  
   if (live == 0) {
    mode = 5;
  }
 
  font = loadFont("AmstoniaSans-34.vlw");
  textFont(font);
  //textSize(34);
  fill(255);
  //text("SCORE: " + score, 20, 40);
  text("LIVES: " + live, 20, 80);
  //noStroke();
  //ellipse(xpos, ypos, 50, 50);
  //imageMode(CENTER);
  image(asteroid, xpos, ypos);
  fill(255, 0, 0, 125);
  rect(xpos, ypos, asteroidWidth, asteroidHeight);
  
  xpos = xpos + vx;
  ypos = ypos + vy;
  
  if (ypos < -10 || ypos > height -80) {
    
    vy = vy * -1;
    
  }
  
  if (xpos < 0 || xpos > width-90) {
    
    vx = vx * -1;
    
  }
  
 
 //Defining the collision X
  if (xpos + asteroidWidth + vx > shiptrek.location.x && 
      xpos + vx < shiptrek.location.x + shipWidth && 
      ypos + asteroidHeight > shiptrek.location.y && 
      ypos < shiptrek.location.y + shipHeight) {
   
     if (time > 1) {   
     live = live - 1;
     }
  }
 
   //Defining the collision Y
  if (xpos + asteroidWidth + vx > shiptrek.location.x && 
      xpos < shiptrek.location.x + shipWidth && 
      ypos + asteroidHeight + vy > shiptrek.location.y && 
      ypos + vy < shiptrek.location.y + shipHeight) {
   
     live = live - 1;
     }
  }
  
 
  //Timer
  
   // draw the clock's rectangle
  fill(25, 25, 37, 127);
  stroke(168, 153, 108);
  rect(20,height-40,width-40,20);
  // calculate current time
  long currentTime = millis() - timer;  

  // choose the color, red for < 5s
  //gris
  //fill(197,211,211, 120);
  //Azul
  fill(27,72,113, 140);
  if(TIMEOUT - currentTime < 5000)
    fill(0,255,0);
  // width of the countdown
  float w = map(currentTime,0,TIMEOUT,0,width-40);
  // draw if not the end of time
  if(TIMEOUT - currentTime > 0)
    rect(20,height-40,width-40-w,20);
    
  
}

  
  void keyPressed() {
  if (keyCode == 'W') {
    up = true;
  }
  if (key == 's') {
    down = true;
  }
  if (key == 'a') {
    left = true;
  }
  if (key == 'd') {
    right = true;
  }
  }
  
  void keyReleased() {
  if (keyCode == 'W') { 
    up = false;
  }
  if (key == 's') {
    down = false;
  }
  if (key == 'a') {
    left = false;
  }
  if (key == 'd') {
    right = false;
  }
   }


//THIS IS MOUSE INTERACTION


void mouseReleased() {
  
  if (mode == 1) {
    time = 0;
    image(bgintro2, 0, 0);
    mode = 2;
    
    } else if (mode == 2){
      
        
      }
    
    } else if (mode == 3){
      
    
    } else if (mode == 4){
    
    } else if (mode == 5){
     setup();
     mode = 1;
     
    } else {
      println("mode is broken" + mode);
      
    }
}


//THIS IS INTROMODE

PImage bgintro;
PImage bgintro2;

void intro() {
  imageMode(CORNER);
 bgintro = loadImage ("button_off.jpg");
 bgintro2 = loadImage ("button_on.jpg");
 
 image(bgintro, 0, 0);
 live = 5;
 score = 0;
 
//mouse hover function 
  if (mouseX > rectX && mouseX < rectX + rectWidth && mouseY > rectY && mouseY < rectY + rectHeight) {
    noFill();
    noStroke();
    image(bgintro2, 0, 0);
  } 
  else {
    noFill();
    noStroke();
    image(bgintro, 0, 0);
  }
  
  rect(rectX, rectY, rectWidth, rectHeight);
 
}

//THIS IS GAMEOVER

PImage gameover;


void gameOver() {
  imageMode(CORNER);
  gameover = loadImage ("gameover.jpg");
  
 
 image(gameover, 0, 0);
  
}

i sort of get what you’re doing. i see you only have a single asteroid which makes it alot easier basically you can add a variable “colliding” and set it to true when you are colliding with the asteroid and only do collision testing when colliding is false. so basically

replace

  if (xpos + asteroidWidth + vx > shiptrek.location.x && 
    xpos + vx < shiptrek.location.x + shipWidth && 
    ypos + asteroidHeight > shiptrek.location.y && 
    ypos < shiptrek.location.y + shipHeight) {

    if (time > 1) {   
      live = live - 1;
    }
  }

  //Defining the collision Y
  if (xpos + asteroidWidth + vx > shiptrek.location.x && 
    xpos < shiptrek.location.x + shipWidth && 
    ypos + asteroidHeight + vy > shiptrek.location.y && 
    ypos + vy < shiptrek.location.y + shipHeight) {

    live = live - 1;
  }

with something like

    if (xpos + asteroidWidth + vx > shiptrek.location.x && 
        xpos < shiptrek.location.x + shipWidth && 
        ypos + asteroidHeight + vy > shiptrek.location.y && 
        ypos + vy < shiptrek.location.y + shipHeight) {
      
        if(!ship.isCollding) 
           live = live - 1;
        ship.isColliding = true;
    }
    else {
      ship.isColliding = false;
    }

and just add a variable isColliding to the ship

boolean isColliding = false;
1 Like

Hi,

Sorry my delay, it works perfectly. Thanks so much for the help.
Just to documented, I had to change only 2 things in your code, the first one is really simple,
In that line: if(!ship.isCollding)
we were missing an I in ¨ship.Collding¨.

And the last one, I replaced all the ship.XXX to shiptrek.XXX that was the name of the new class.
Bellow is the final code for the level 01:


void level01() {
  time = time + 1;
  
  if (time > 1150) mode = 3;
  background(0, 0, 0);
  
  
  //GIF
  imageMode(CORNER);
  image(gif[currentPic], 0, 0, 600, 600);
  currentPic++;
  if (currentPic >= 20) {
    currentPic = 0;
  }
  
  
  /*
  //ANIMATED STARS BACKGROUD CLASS
  background(0);
  translate(width/2, height/2);
  for (int i = 0; i < stars.length; i++) {
    stars[i] = update();
    stars[i] = show();
  }
  */
  
  shiptrek.show();
  shiptrek.act();
  
   if (live == 0) {
    mode = 5;
  }
 
  font = loadFont("AmstoniaSans-34.vlw");
  textFont(font);
  //textSize(34);
  fill(255);
  //text("SCORE: " + score, 20, 40);
  text("LIVES: " + live, 20, 80);
  //noStroke();
  //ellipse(xpos, ypos, 50, 50);
  //imageMode(CENTER);
  image(asteroid, xpos, ypos);
  fill(255, 0, 0, 125);
  rect(xpos, ypos, asteroidWidth, asteroidHeight);
  
  xpos = xpos + vx;
  ypos = ypos + vy;
  
  if (ypos < -10 || ypos > height -80) {
    
    vy = vy * -1;
    
  }
  
  if (xpos < 0 || xpos > width-90) {
    
    vx = vx * -1;
    
  }
  
 
 if (xpos + asteroidWidth + vx > shiptrek.location.x && 
        xpos < shiptrek.location.x + shipWidth && 
        ypos + asteroidHeight + vy > shiptrek.location.y && 
        ypos + vy < shiptrek.location.y + shipHeight) {
      
        if(!shiptrek.isColliding) 
           live = live - 1;
        shiptrek.isColliding = true;
    }
    else {
      shiptrek.isColliding = false;
    }
  
  

  
  //Timer
  
   // draw the clock's rectangle
  fill(25, 25, 37, 127);
  stroke(168, 153, 108);
  rect(20,height-40,width-40,20);
  // calculate current time
  long currentTime = millis() - timer;  

  // choose the color, red for < 5s
  //gris
  //fill(197,211,211, 120);
  //Azul
  fill(27,72,113, 140);
  if(TIMEOUT - currentTime < 5000)
    fill(0,255,0);
  // width of the countdown
  float w = map(currentTime,0,TIMEOUT,0,width-40);
  // draw if not the end of time
  if(TIMEOUT - currentTime > 0)
    rect(20,height-40,width-40-w,20);
    
  
}

  
  void keyPressed() {
  if (keyCode == 'W') {
    up = true;
  }
  if (key == 's') {
    down = true;
  }
  if (key == 'a') {
    left = true;
  }
  if (key == 'd') {
    right = true;
  }
  }
  
  void keyReleased() {
  if (keyCode == 'W') { 
    up = false;
  }
  if (key == 's') {
    down = false;
  }
  if (key == 'a') {
    left = false;
  }
  if (key == 'd') {
    right = false;
  }
   }

Once again, thank you so much!
Cheers

Other possibilities: if your objects are bouncing off of each other, only count a collision if they overlap AND they are moving towards each other. If they are moving apart, your simulator is “resolving” the collision, so it doesn’t count.

Another possibility is to recognize that you are testing the collision once per frame, e.g. 60 times per second. Scale your damage amount small enough or health large enough so that counting it every frame is reasonable. Leave it up to the player to fly back out of the collision.

Take a look at my p5.js code to see how I resolve the collisions. I scale the damage based on how hard the players’ ships hit other objects. https://codepen.io/scudly/pen/ZLeejK doBounce() at line 278 tests for the collisions and returns the impact force.

Good idea as well. Nice game by the way.
I saw that you created a deathFrame = 300, but I don´t get it how this works. Probably because I am really new learning Processing and I am not not familiar with some p5. components.

I know that I could control the frameRate in processing but this also influence in the general speed of the game. Do you have any example of processing elements based in frame control?

Thanks for answering by the way

When a player ship runs out of energy and dies, deathFrame is set to 300 and counts down to zero. During that time, the ship is shown as a fading circle “explosion” and ignores any collisions. Since the frameRate is 60, that gives five seconds dead before the ship re-spawns. deathFrame is just an integer counter, but I also treat it in a few places as a flag to tell me if the ship is still alive.

For a game, you’ll want to leave the frameRate at its default 60 fps to keep motion as smooth as possible. But don’t get stuck thinking that numbers have to be integers. Let your health values be floats and when colliding, subtract 1.0/60 from the health each frame to lose 1 point per second, for example. Start them with 5 or 10 health and see how long they can survive your asteroid field. Maybe at higher levels they start with a lower health in addition to more or faster asteroids.

3 Likes

These both work, although they both create double-hits in certain situations if the ship or asteroid direction changes.

A common method of dealing with this in classic video games is for “being damaged” to be a state with a duration – say, 1 second – and that while the ship is being damaged it may be knocked back multiple times, but is invincible to further damage. Often the avatar flashes etc. during this period. When that timer ends, the avatar may be damaged again.

2 Likes