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.
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.