Colisão entre Enemy e Player no Game Space Invaders ou Asteróides no Eclipse

Olá! Abri esse tópico para conseguir ajuda de vocês na construção do código para colisão entre Player e Enemy com perda de vida do Player. Alguém poderia me ajudar?

Quer tipo de ajuda você quer? É pra fazer qualquer tipo de colisão, ou você quer reproduzir a colisão exatamente do jeito que funciona nesses jogos?

Um dos jeitos mais simples de calcular colisão é ter 2 retângulos (ou coordenadas que possam ser mapeadas para retângulos), e calcular a interseção entre eles (exemplo: link ). Deve ser suficiente para Space Invaders, mas em Asteroids, a colisão é com polígonos, sendo um pouco mais complexo. (link).

Abraço.

Vou compartilhar o link que contém o código do jogo. Game - Google Drive

Olá, Terra Skill! Tudo bem?
Sou iniciante no desenvolvimento de jogos digitais em Java. Já consigo ler e compreender o código, mas estou com problemas na elaboração de como fazer o Player colidir com o Enemy especificamente no jogo de Space Invaders(ou Asteróides).
Estou usando sprite com pixels, as naves e asteróides foram feitas por mim. Não estou sabendo com fazer a colisão entre Enemy e Player sabendo que ambas são classes dentro do pacote Entity

Não vou baixar seu código pra ver como está, poste ele aqui, copiando e colando as classes relevantes (Player e Enemy, e se estenderem uma outra classe, essa outra classe também). Se forem classes extensas, poste só o essencial pra colisão (coordenadas e tamanho dos objetos). E não esqueça de usar o botão </> pra formatar o código.

Dito isso, supondo que as entidades são algo como:

Player{
  float posicaoX, posicaoY;
  float largura, altura;
}

Enemy{
  float posicaoX, posicaoY;
  float largura, altura;
}

Sua verificação de colisão pode ser algo como:

public boolean verificarColisao(Player player, Enemy enemy){
return 
    player.posicaoX < enemy.posicaoX + enemy.largura &&
    player.posicaoX + player.largura > enemy.posicaoX &&
    player.posicaoY < enemy.posicaoY + enemy.altura &&
    player.posicaoY + player.altura > enemy.altura;
}

E, para verificar todos os inimigos em uma lista, use um loop:

List<Enemy> inimigos;
//....outros códigos
boolean colidiu = false;
for (Enemy e : inimigos) {
   if (verificaColisao(player, e){
     colidiu = true;
   }
}

Frisando que esse código é voltado para elementos retangulares, que é o que recomendo que você use inicialmente, já que não tem tanta experiência com jogos em Java. Em asteróides, os inimigos tem formatos poligonais, e calcular colisão dessa forma vai diferir da experiência esperada (vide segundo link).

Se você usa sprites/imagens (em vez de lagura e altura), você pode obter essa informação dessas imagens, então não muda tanto.

Abraço.

Olá!
Aqui está o código do Enemy:
package com.estudo.entities;

import java.awt.image.BufferedImage;

import com.estudo.main.Game;
import com.estudo.main.Sound;

public class Enemy extends Entity{

public double life = 2;


public Enemy(double x, double y, int width, int height, double speed, BufferedImage sprite) {
	super(x, y, width, height, speed, sprite);
}

public void tick() {
	
	y+=speed;
	if(y >= Game.HEIGHT) {
		Game.life--;
		Game.entities.remove(this);
		return;
	}
	
	
	for(int i =0; i < Game.entities.size(); i++) {
		Entity e = Game.entities.get(i);
		if(e instanceof Bullet) {
			if(Entity.isColidding(this, e)) {
				Game.entities.remove(e);
				life--;
				if(life == 0)
					
					
				{
					Explosion explosion = new Explosion(x,y,16,16,0,null);
					
					Game.entities.add(explosion);
					Sound.explosao.play();
					Game.score++;
					Game.entities.remove(this);
					return;
				}
				
				break;
			}
		}
	
	}
}

}

e o código do Player (Nave):
package com.estudo.entities;

import java.awt.image.BufferedImage;

import com.estudo.main.Game;

public class Player extends Entity{

public int life = 100;
public int maxlife = 100;

public boolean right, left, up, down;
public boolean isShooting = false;

public boolean isDamaged = false;

public double speed = 1;

public Player(int x, int y, int width, int height,double speed,BufferedImage sprite) {
	super(x, y, width, height,speed,sprite);
}

public void tick(){
	
	if (right) {
		x+=speed;	
	}else if (left) {
		x-=speed;
	}
			
	if (x >= Game.WIDTH) {
		 x = -16;
		
	}else if (x + 16 < 0){
		
	 x = Game.WIDTH;
		
	} else if (down) {
		
		y+=speed;
		
	}else if (up) {
	
		y-=speed;
	}
			
	if (y >= Game.HEIGHT) {
		y = 16;
		
	}else if (y + 16 < 0){
		
		y = Game.HEIGHT;
		
	}
	//Sistema de tiro!
			if(isShooting) {
				isShooting = false;
				int xx = this.getX() + 5;
				int yy = this.getY();
				Bullet bullet = new Bullet(xx,yy,2,2,5,null);
				Game.entities.add(bullet);
			}	
		}
		
	}

Ambos foram colocados como classes diferentes dentro de um pacatoe chamado de Entities.

Vou postar também a classe Game, que está dentro do pacote chanado de “main”

package com.estudo.main;

import java.awt.Canvas;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JFrame;

import com.estudo.entities.Entity;
import com.estudo.entities.Player;
import com.estudo.entities.Playerblue;
import com.estudo.entities.Playerred;
import com.estudo.graficos.Spritesheet;
import com.estudo.graficos.UI;
import com.estudo.world.World;
import com.estudo.main.Menu;

public class Game extends Canvas implements Runnable,KeyListener,MouseListener,MouseMotionListener{

private static final long serialVersionUID = 1L;
public static JFrame frame;
private Thread thread;
private boolean isRunning = true;
public static final int WIDTH = 120;
public static final int HEIGHT = 160;
public static final int SCALE = 4;
public static final String rand = null;

private BufferedImage image;

public static World world;
public static List<Entity> entities;
public static Spritesheet spritesheet;

public static Player player;

public static Playerblue playerblue;

public static Playerred playerred;

public static int score = 0;



public static double life = 100;
public static double Maxlife = 100;

public int framesGameOver = 0;

public EnemySpawn enemySpawn;

public static Menu menu;

public BufferedImage GAME_BACKGROUND;
public BufferedImage GAME_BACKGROUND2;

public int backY = 0;
public int backY2 = 160;
public int backSpd = 1;

public static Pausa pausa;
//public static String gameState;
public static Object enemies;


public UI ui;

public static String gameState = "MENU";
private boolean showMessageGameOver = true;
//private int framesGameOver = 0;
public boolean restartGame = false;
//public boolean restartGame;
public boolean saveGame;


//@SuppressWarnings("unused")
public Game(){
	
	Sound.bg.loop();
	
	addKeyListener(this);
	addMouseListener(this);
	addMouseMotionListener(this);
	
	
	setPreferredSize(new Dimension(WIDTH*SCALE,HEIGHT*SCALE));
	initFrame();
	image = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_RGB);
	
	//Inicializando objetos.

	pausa = new Pausa ();
	
	spritesheet = new Spritesheet("/spritesheet.png");
	
	entities = new ArrayList<Entity>();
	
	player = new Player(Game.WIDTH/2 - 50,Game.HEIGHT / 2 -25,16,16,1,spritesheet.getSprite(0, 0, 16, 16));
	
	playerblue = new Playerblue(Game.WIDTH/2 - 50,Game.HEIGHT/2-65,16,16,1,spritesheet.getSprite(48, 0, 16, 16));
	
	playerred = new Playerred(Game.WIDTH/2 - 51,Game.HEIGHT/2-45,16,16,1,spritesheet.getSprite(64, 0, 16, 16));
	
	world = new World();
	
	menu = new Menu();
	
			
	//com.estudo.main.Menu menu = new Menu(); 
	

	ui = new UI();
	
	enemySpawn = new EnemySpawn();
	try {
		GAME_BACKGROUND = ImageIO.read(getClass().getResource("/bg1.png"));
		GAME_BACKGROUND2 = ImageIO.read(getClass().getResource("/bg1.png"));
	} catch (IOException e) {
		e.printStackTrace();
	}
	
	entities.add(player);
	
	entities.add(playerblue);
	
	entities.add(playerred);
		
}


public void initFrame(){
	frame = new JFrame("INVASORES");
	frame.add(this);
	frame.setResizable(false);
	frame.pack();
	frame.setLocationRelativeTo(null);
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	frame.setVisible(true);
}

public synchronized void start(){
	thread = new Thread(this);
	isRunning = true;
	thread.start();
}

public synchronized void stop(){
	isRunning = false;
	try {
		thread.join();
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
}

public static void main(String args[]){
	Game game = new Game();
	game.start();
	
	
}

public void tick(){
	enemySpawn.tick();
	for(int i = 0; i < entities.size(); i++) {
		Entity e = entities.get(i);
		e.tick();
	}
	
	ui.tick();

	backY -= backSpd;
	if (backY + 160 <= 0 ) {
		backY = 160;
		
	}
	
	backY2 -= backSpd;
	if (backY2 + 160 <= 0 ) {
		backY2 = 160;
		
	}
}


public void render(){
	BufferStrategy bs = this.getBufferStrategy();
	if(bs == null){
		this.createBufferStrategy(3);
		return;
	}
	Graphics g = image.getGraphics();
	g.setColor(new Color(0,0,0));
	g.fillRect(0, 0,WIDTH,HEIGHT);
	g.drawImage(GAME_BACKGROUND,0, backY,null);
	g.drawImage(GAME_BACKGROUND2,0, backY2,null);
	
	//*Renderização do jogo*/	
	//Graphics2D g2 = (Graphics2D) g;

	Collections.sort(entities,Entity.nodeSorter);
	for(int i = 0; i < entities.size(); i++) {
		Entity e = entities.get(i);
		e.render(g);
	}
	
	/***/
	g.dispose();
	g = bs.getDrawGraphics();
	g.drawImage(image, 0, 0,WIDTH*SCALE,HEIGHT*SCALE,null);
	
	ui.render(g);
	//bs.show();
	
	g.setFont(new Font("arial",Font.BOLD,20));
	g.setColor(Color.white);
	//g.drawString("Munição: " + player.ammo,580,20);
	if(gameState == "GAME_OVER") {
		Graphics2D g2 = (Graphics2D) g;
		g2.setColor(new Color(0,0,0,100));
		g2.fillRect(0, 0, WIDTH*SCALE, HEIGHT*SCALE);
		g.setFont(new Font("arial",Font.BOLD,36));
		g.setColor(Color.white);
		g.drawString("Game Over",(WIDTH*SCALE) / 2 - 90,(HEIGHT* SCALE) / 2 - 20);
		g.setFont(new Font("arial",Font.BOLD,32));
		if(showMessageGameOver)
			g.drawString("Pressione Enter para reiniciar<",(WIDTH*SCALE) / 2 - 230,(HEIGHT* SCALE) / 2 + 40);
	}else if(gameState == "MENU") {
		menu.render(g);
	}
	
	bs.show();
	
	
	
}

public void run() {
	long lastTime = System.nanoTime();
	double amountOfTicks = 60.0;
	double ns = 1000000000 / amountOfTicks;
	double delta = 0;
	int frames = 0;
	double timer = System.currentTimeMillis();
	requestFocus();
	while(isRunning){
		long now = System.nanoTime();
		delta+= (now - lastTime) / ns;
		lastTime = now;
		if(delta >= 1) {
			tick();
			render();
			frames++;
			delta--;
		}
		
		if(System.currentTimeMillis() - timer >= 1000){
			System.out.println("FPS: "+ frames);
			frames = 0;
			timer+=1000;
		}
		
	}
	
	stop();
}

@Override
public void keyPressed(KeyEvent e) {
	if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
		player.right = true;
		player.left = false;
	}else if(e.getKeyCode() == KeyEvent.VK_LEFT) {
		player.left = true;
		player.right = false;
		
	}if(e.getKeyCode() == KeyEvent.VK_UP) {
		player.up = true;
		player.down = false;
	
		if(gameState == "MENU") {
			menu.up = true;
		}
	}else if(e.getKeyCode() == KeyEvent.VK_DOWN) {
		player.down = true;
		player.up = false;
		
		if(gameState == "MENU") {
			menu.down = true;
		}
		
	}
	
	if(e.getKeyCode() == KeyEvent.VK_Z) {
		player.isShooting = true;
	}

if(e.getKeyCode() == KeyEvent.VK_ENTER) {
this.restartGame = true;
if(gameState == “MENU”) {
menu.enter = true;
}
}

if(e.getKeyCode() == KeyEvent.VK_ESCAPE) {
gameState = “MENU”;
Menu.pause = true;

}

if (e.getKeyCode() == KeyEvent.VK_SPACE) {
if (gameState == “MENU”)
this.saveGame = true;

}

}

@Override
public void keyReleased(KeyEvent e) {

}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub

}

@Override
public void mouseClicked(MouseEvent arg0) {
	// TODO Auto-generated method stub
	
}

@Override
public void mouseEntered(MouseEvent arg0) {
	// TODO Auto-generated method stub
	
}

@Override
public void mouseExited(MouseEvent arg0) {
	// TODO Auto-generated method stub
	
}

@Override
public void mousePressed(MouseEvent e) {
	

}

@Override
public void mouseReleased(MouseEvent arg0) {
	// TODO Auto-generated method stub
	
}

@Override
public void mouseDragged(MouseEvent arg0) {
	// TODO Auto-generated method stub
	
}

@Override
public void mouseMoved(MouseEvent e) {

}

}

Seu código já tem todas as informações para testar a colisão da forma que mostei, e inclusive já tem códigos para testar a colisão entre projéteis (Bullet) e inimigos:

if(e instanceof Bullet) {
  if(Entity.isColidding(this, e)) {

O que mais você quer? Se esse método isColliding() já testa a colisão, você só precisa fazer algo similar para testar entre o Player e os inimigos. Não precisa nem implementar novamente o teste de colisão:

// na classe player
for(int i =0; i < Game.entities.size(); i++) {
  Entity e = Game.entities.get(i);
  if(e instanceof Enemy) {
    if(Entity.isColidding(this, e)) {
      // faz alguma coisa

Abraço.

Sim. Obrigado. Então basta refazer para ambos substituindo Bullet por Enemy e Player? Vou tentar fazer como vc está dizendo.

Obrigado mais uma vez! Consegui fazer a colisão entre Enemy e Player.