# Collisions mit festen Objekten



## Mr. Moon (11. Sep 2013)

Hallo, ich baue zur Zeit einen Bombermanklon und orientiere mich an http://archon.bplaced.net/bain09/Studium/Programmieren/Java/Turorials/Spiele/Tutorial%20f%FCr%20Java-Spiele%20.pdf

Ich bin zuzeit am Grübeln, wie ich es mache, dass man durch bestimmte Objekte nicht hindurch gehen kann. Ich habe die Kollisionserkennungsmethoden aus dem Tutorial übernommen und bin am überlegen, ob ich es damit irgendwie machen kann.  Mein Problem ist, dass wenn jetzt bspw. die Spielfigur mit einem Objekt kollidiert müsste ich den horizontalen oder vertikalen Speed der Spielfigur auf Null setzen und die Spielfigur ein paar Pixel weiter nach links,rechts, oben, unten hinsetzen, je nachdem von welcher Seite die Spielfigur mit dem Objekt kollidiert. Da ist auch mein Problem. Wie finde ich heraus von welcher Seite die Spielfigur mit dem Objekt kollidiert. Wenn zum Beispiel die rechte obere Ecke des Spielers mit dem Objekt kollidiert, weiß man ja nicht, ob die Figur nun von unten oder links kam.

Ich bin dankbar für jede Hilfe

Grüße Mr. Moon


----------



## Mr. Moon (11. Sep 2013)

Könnt ihr mir gleich auch nochmal bei diesem Fehler helfen:

Exception in thread "Thread-3" java.awt.image.RasterFormatException: (y + height) is outside of Raster
	at sun.awt.image.ByteInterleavedRaster.createWritableChild(Unknown Source)
	at java.awt.image.BufferedImage.getSubimage(Unknown Source)
	at Sprite.checkOpaqueColorCollisions(Sprite.java:68)
	at Block.collidedWith(Block.java:12)
	at GamePanel.doLogic(GamePanel.java:154)
	at GamePanel.run(GamePanel.java:203)
	at java.lang.Thread.run(Unknown Source)


```
import  java.awt.Graphics;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

public abstract class Sprite extends Rectangle2D.Double implements Drawable, Movable {

	long delay;
	long animation = 0;
	protected double dx;
	protected double dy;
	GamePanel parent;
	BufferedImage[] pics;
	int currentpic = 0;
	boolean remove = false;
	int loop_from;
	int loop_to;
	
	public Sprite(BufferedImage[] i, double x, double y, long delay, GamePanel p) {
		pics        = i;
		this.x      = x;
		this.y      = y;
		this.delay  = delay;
		this.width  = pics[0].getWidth();
		this.height = pics[0].getHeight();
		parent      = p;
		loop_from   = 0;
		loop_to     = pics.length - 1;
	}
	
	public void setX(double i) {
		x = i;
	}
	
	public void setY(double i) {
		y = i;
	}
	
	public void setVerticalSpeed(double d) {
		dy = d;
	}
	
	public void setHorizontalSpeed(double d) {
		dx = d;
	}
	
	public double getVerticalSpeed() {
		return dy;
	}
	
	public double getHorizontalSpeed() {
		return dx;
	}
	
	public abstract boolean collidedWith(Sprite s);
	
	public boolean checkOpaqueColorCollisions(Sprite s) {
		
		Rectangle2D.Double cut = (Double) this.createIntersection(s);
		
		if((cut.width < 1) || (cut.height < 1)) {
			return false;
		}
		
		Rectangle2D.Double sub_me  = getSubRec(this, cut);
		Rectangle2D.Double sub_him = getSubRec(s, cut);
		
		BufferedImage img_me = pics[currentpic].getSubimage((int) sub_me.x, (int) sub_me.y, (int) sub_me.width, (int) sub_me.height);
		BufferedImage img_him = pics[currentpic].getSubimage((int) sub_him.x, (int) sub_him.y, (int) sub_him.width, (int) sub_him.height);
		
		for(int i = 0; i < img_me.getWidth(); i++) {
			for(int n = 0; n < img_him.getHeight(); n++) {
				
				int rgb1 = img_me.getRGB(i, n);
				int rgb2 = img_him.getRGB(i, n);
				
				if(isOpaque(rgb1) && isOpaque(rgb2)) {
					return true;
				}
			}
		}
		
		return false;
	}
	
	protected Rectangle2D.Double getSubRec(Rectangle2D.Double source, Rectangle2D.Double part) {
		
		Rectangle2D.Double sub = new Rectangle2D.Double();
		
		if(source.x > part.x) {
			sub.x = 0;
		}
		
		else {
			sub.x = part.x - source.x;
		}
		
		if(source.y > part.y) {
			sub.y = 0;
		}
		
		else {
			sub.y = part.y - source.y;
		}
		
		sub.width  = part.width;
		sub.height = part.height;
		
		return sub;
	}
	
	protected boolean isOpaque(int rgb) {
		
		int alpha = (rgb >> 24) & 0xff;
		//red   = (rgb >> 16) & 0xff;
		//green   = (rgb >> 8) & 0xff;
		//blue   = (rgb) & 0xff;
		
		if(alpha == 0) {
			return false;
		}
		
		return true;
		
	}
	
	public void drawObjects(Graphics g) {
		g.drawImage(pics[currentpic], (int) x, (int) y, null);
	}
	
	public void doLogic(long delta) {
	
		animation += (delta / 1000000);
		
		if(animation > delay) {
			animation = 0;
			computeAnimation();
		}
		
	}
	
	public void move(long delta) {
		
		if(dx != 0) {
			x += dx * (delta / 1e9);
		}
		
		if(dy != 0) {
			y += dy * (delta / 1e9);
		}
	}
	
	private void computeAnimation() {
		
		currentpic++;
		
		if(currentpic > loop_to) {
			currentpic = loop_from;
		}
	}
	
	public void setLoop(int from, int to) {
		loop_from  = from;
		loop_to    = to;
		currentpic = from;	
	}
}
```


```
import java.awt.image.BufferedImage;


public class Block extends Sprite {
    
	public Block(BufferedImage[] i, double x, double y, long delay, GamePanel p) {
		super(i, x, y, delay, p);
	}
	
	public boolean collidedWith(Sprite s) {
		
		return checkOpaqueColorCollisions(s);
				
	}
}
```


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

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Vector;

public class GamePanel extends JPanel implements Runnable, KeyListener {
    
	private static final long serialVersionUID = 1L;
	boolean game_running = true;
	
	long delta = 0;
	long last  = 0;
	long fps   = 0;
	
	boolean up    = false;
	boolean down  = false;
	boolean left  = false;
	boolean right = false;
	boolean drop  = false;
	
	int speed = 50;
	int h = 0;
	Sprite player, bomb;
	Sprite blocks[];
	Vector<Sprite> actors;
	BufferedImage[] bombpics;
	
	public GamePanel(int w, int h) {
		this.setPreferredSize(new Dimension(w,h));
		JFrame frame = new JFrame("BomberMJ");
		frame.setLocation(100,100);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.addKeyListener(this);
		frame.add(this);
		frame.pack();
		frame.setVisible(true);
		doInitializations();
	}
	
	private void doInitializations() {
	    int k = 0;
		
		last = System.nanoTime();
		
	    actors = new Vector<Sprite>();
	    BufferedImage[] mjpics = this.loadPics("pics/MJ.gif", 36);
	    BufferedImage[] blockpics = this.loadPics("pics/Block.gif", 1);
	    bombpics = this.loadPics("pics/Bomb.gif", 2);
	    
	    player = new Player(mjpics, 0, 0, 100, this);
	    player.setLoop(1,1);
	    
	    blocks = new Sprite[20];
	    
	    
	    for(int i = 1; i < 8; i = i + 2) {
	    	
	    	for(int j = 1; j < 10; j = j + 2) {
	    		
	    		if(k < blocks.length ) {
	    			blocks[k] = new Block(blockpics, j * 75 , i * 75, 100, this);
	    			actors.add(blocks[k]);
	    			System.out.println(j*75 + "," + i*75 + "," + k);
	    			k++;
	    		}
	    			
	    	}
	    	
	    }
	    
	    actors.add(player);
		Thread t = new Thread(this);
	    t.start();
	}
	
	private BufferedImage[] loadPics(String path, int pics) {
		
		BufferedImage[] anim = new BufferedImage[pics];
		BufferedImage source = null;
		
		URL pic_url = getClass().getClassLoader().getResource(path);
		
		try {
			source = ImageIO.read(pic_url);
		}
		
		catch(IOException e) {
			
		}
		
		for(int x = 0; x < pics; x++) {
			anim[x] = source.getSubimage(x * source.getWidth() / pics, 0, source.getWidth() / pics, source.getHeight());
		}
		
		return anim;
	}
	
	private void dropBomb() {
		bomb = new Bomb(bombpics, player.getX() , player.getY() + 25, 100, this);
		actors.add(bomb);
	}
	
	private void computeDelta() {
		delta = System.nanoTime() - last;
		last  = System.nanoTime();
		
		fps = ((long) 1e9) / delta;
	}
	
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		
		g.setColor(Color.red);
		g.drawString("FPS: " + Long.toString(fps), 20, 10);
		
		if(actors != null) {
			for(Drawable draw : actors) {
				draw.drawObjects(g);
			}
		}
	}
	
	private void doLogic() {
		
		Vector<Sprite> trash = new Vector<Sprite>();
		Sprite check, s1, s2;
		
		for(Movable mov : actors) {
			mov.doLogic(delta);
			check = (Sprite) mov;
			
			if(check.remove) {
				trash.add(check);
			}
		}
		
		for(int i = 0; i < actors.size(); i++) {
			for(int n = i + 1; n < actors.size(); n++) {
				
				s1 = actors.elementAt(i);
				s2 = actors.elementAt(n);
				
				s1.collidedWith(s2);
			}
		}
		
		if(trash.size() > 0) {
			actors.removeAll(trash);
			trash.clear();
		}
	}
	
	private void moveObjects() {
		for(Movable mov : actors) {
			mov.move(delta);
		}
	}
	
	private void checkKeys() {
		if(drop) {
			dropBomb();
		}
		
		if(up) {
			player.setVerticalSpeed(-speed);
		}
		
		if(down) {
			player.setVerticalSpeed(speed);
		}
		
		if(right) {
			player.setHorizontalSpeed(speed);
		}
		
		if(left) {
			player.setHorizontalSpeed(-speed);
		}
		
		if(!up && !down) {
			player.setVerticalSpeed(0);
		}
		
		if(!left && !right) {
			player.setHorizontalSpeed(0);
		}
	}
	public void run() {
		while(game_running) {
		    computeDelta();
		    checkKeys();
		    doLogic();
		    moveObjects();
		    
		    repaint();
			
			
			try {
				Thread.sleep(10);
			}
			
			catch (InterruptedException e) {}
		}
	}
	public static void main(String[] args) {
		
		new GamePanel(825,675);
	}

	@Override
	public void keyPressed(KeyEvent e) {
		// TODO Auto-generated method stub
		
		if(e.getKeyCode() == KeyEvent.VK_SPACE) {
			drop = true;
			
		}
		
		if(e.getKeyCode() == KeyEvent.VK_UP) {
			up = true;
			
			if(h != 1) {
				player.setLoop(4, 7);
				h = 1;
			}
		}
		
		if(e.getKeyCode() == KeyEvent.VK_DOWN) {
			down = true;

            if(h != 2) {
            	player.setLoop(0, 3);
            	h = 2;
            }
		}
		
		if(e.getKeyCode() == KeyEvent.VK_LEFT) {
			left = true;
			
			if(h != 3) {
				player.setLoop(12, 15);
				h = 3;
			}
		}
		
		if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
			right = true;
			if(h != 4) {
				player.setLoop(8, 11);
				h = 4;
			}
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub
		
		if(e.getKeyCode() == KeyEvent.VK_SPACE) {
			drop = false;
			
		}
		
		if(e.getKeyCode() == KeyEvent.VK_UP) {
			up = false;
			if(h != 0) {
				player.setLoop(1,1);
				h = 0;
			}
		}
		
		if(e.getKeyCode() == KeyEvent.VK_DOWN) {
			down = false;
			if(h != 0) {
				player.setLoop(1,1);
				h = 0;
			}
		}
		
		if(e.getKeyCode() == KeyEvent.VK_LEFT) {
			left = false;
			if(h != 0) {
				player.setLoop(1,1);
				h = 0;
			}
		}
		
		if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
			right = false;
			if(h != 0) {
				player.setLoop(1,1);
				h = 0;
			}
		}
		
	}

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


```
import java.awt.Graphics;

public interface Drawable {
    
	public void drawObjects(Graphics g);
}
```


```
public interface Movable {
    
	public void doLogic(long delta);
	
	public void move(long delta);
}
```


----------



## Gucky (11. Sep 2013)

Für die Kollision gibt es die Polygone. Diese haben die contains(int,int) Methode, mit der man prüfen kann, ob der Punkt im Polygon enthalten ist. Du könntest jedem Objekt ein Polygon geben und diese mit einer Extraklasse laufend mit den Positionen der Figuren abgleichen (die Figuren rufen dann eine entsprechende Methode auf). Wenn die Figur im Objekt enthalten ist, dann gibt die Kollisionsklasse eine Meldung zurück, wenn nicht dann nicht.


----------



## protectedzone (11. Sep 2013)

Mr. Moon hat gesagt.:


> Wie finde ich heraus von welcher Seite die Spielfigur mit dem Objekt kollidiert. Wenn zum Beispiel die rechte obere Ecke des Spielers mit dem Objekt kollidiert, weiß man ja nicht, ob die Figur nun von unten oder links kam.



Ich hab das jetzt nicht klar vor Augen, aber die Figur könnte ja während jeder Bewegung speichern, von wo sie kam. Bei einer Kollision braucht man dann nur noch die entsprechende Variable aufzurufen und die Figur entsprechend zurückzusetzen.

Für die Kollisionserkennung reicht bei Bombernman meiner Meinung nach auch ein zweidimensionales Array, das in booleans speichert, ob das Feld 'besetzt' ist. Also sowas: (isOccupied[x][y] == true).


----------



## Mr. Moon (12. Sep 2013)

Danke schonmal,  könnt ihr mir vielleicht nochmal bei dem obigen Fehler helfen?


----------



## Gucky (13. Sep 2013)

Wenn es ein Bomberman mit Bewegungen fließenden Bewegungen sein soll, reicht es nicht, meiner Meinung nach. Denn dann kann eine Figur auch mal zur Hälfte auf einem Feld sein. Korrigiert mich, wenn ich falsch liege.


----------



## Mr. Moon (13. Sep 2013)

So Thema ist erledigt. Habe den Fehler gefunden und behoben und es hingekriegt, dass ich nicht durch bestimmte Objekte gehen kann indem ich gespeichert habe, von welcher Richtung ich komme.


----------



## protectedzone (14. Sep 2013)

Gucky hat gesagt.:


> Wenn es ein Bomberman mit Bewegungen fließenden Bewegungen sein soll, reicht es nicht, meiner Meinung nach. Denn dann kann eine Figur auch mal zur Hälfte auf einem Feld sein. Korrigiert mich, wenn ich falsch liege.



Ja, aber ich denke, dass das keine Rolle spielt, wenn man es gescheit programmiert. Auf einem Schachfeld müssen die Figuren ja auch nicht exakt im Zentrum des Feldes stehen, aber besetzen immer ein eindeutiges Feld der 64 möglichen. 

Gruss, protectedzone


----------



## Gucky (14. Sep 2013)

Aber auf einem Schachbrett können die Figuren nicht auf der Grenze zwischen zwei Feldern stehen.

PS: Beim Schreiben dieses Textes fällt mir auf: die Figur könnte dann halt zwei Felder belegen.


----------

