Reputation: 41
In my project I came across this problem, where I have an abstract class of Entity, and it's children are Player, Shot and Enemy. I want to check for collision between them. A separate Physics class is doing the collision evaluation with the following code:
public class Physics {
private static int height = 32;
private static int width = 32;
public static void collision(Entity entity, LinkedList<Entity> eList) {
for (int i = 0; i<eList.size(); i++) {
if (entity.getBounds(width, height).intersects(eList.get(i).getBounds(width, height))) {
entity.collidesWith(eList.get(i));
}
}
}
}
The linkedList contains both Shots and Enemies, and yet for some reason, the collision only calls the collidesWith(Entity entity) method, instead of the collidesWith(Shot b) or collidesWith(Enemy e).
edit: The mentioned classes (with only the code that I think would matter from them in this case)
Entity:
public abstract class Entity {
protected double x;
protected double y;
public Entity (double x, double y) {
this.x = x;
this.y = y;
}
public abstract void tick();
public double getX() { return x; }
public double getY() { return y; }
public Rectangle getBounds(int width, int height) {
return new Rectangle((int)x, (int)y, width, height);
}
public abstract void collidesWith(Entity e);
public abstract void collidesWith(Enemy e);
public abstract void collidesWith(Shot s);
Player:
public class Player extends Entity {
private boolean alive;
private int gameWidth, gameHeight;
private GameController gCont;
private Textures textures;
public Player( String name, int x, int y, int gameWidth, int gameHeight, Textures textures, GameController gCont) {
super(x,y);
this.name = name;
this.score = 0;
this.gameWidth = gameWidth;
this.gameHeight = gameHeight;
this.gCont = gCont;
this.textures = textures;
this.alive = true;
}
public void tick() {
gCont.collisionCheck(this);
}
public void collidesWith(Enemy e) {
System.out.println("Player collides with enemy");
this.alive = false;
}
public void collidesWith(Shot s) {
return;
}
public void collidesWith(Entity e) {
System.out.println("collided with entity");
return;
}
Shot
public class Shot extends Entity {
private Textures textures;
private GameController gCont;
public Shot(double x, double y, Textures textures, GameController gCont) {
super(x, y);
this.textures = textures;
this.gCont = gCont;
}
public void tick() {
x+=10;
gCont.collisionCheck(this);
}
public void collidesWith(Entity e) {
return;
}
public void collidesWith(Enemy e) {
gCont.removeEntity(e);
gCont.removeEntity(this);
gCont.addScore();
}
@Override
public void collidesWith(Shot s) {
return;
}
Enemy
public class Enemy extends Entity {
private int speed;
public Enemy(double x, double y) {
super(x, y);
Random random = new Random();
speed = random.nextInt(3)+1;
}
public void tick() {
x-=speed;
}
public void collidesWith(Entity e) {
return;
}
@Override
public void collidesWith(Enemy e) {
return;
}
@Override
public void collidesWith(Shot s) {
return;
}
How can I get it to call to the correct functions?
Upvotes: 2
Views: 500
Reputation: 2552
I think a solution other than Generic type as specified by Jamie Bisotti, is to use an interface and a switch to check which class is what.
This is the interface that declares a method that all entities that can collide must have:
public interface Collidable {
public boolean collidesWith(Collidable entity);
}
Then each class that you want to be able to collide has to implement that:
public class Enemy extends Entity implements Collidable {
private int speed;
public Enemy(double x, double y) {
super(x, y);
Random random = new Random();
speed = random.nextInt(3)+1;
}
public void tick() {
x-=speed;
}
@Override
public void collidesWith(Collidable e) {
if (e.getClass().equals(Shot.class)) {
// DO SOMETHING, I am colliding with a shot
} else if(e.getClass().equals(Enemy.class)) {
// I am colliding with an Enemy
}
. . . etc
}
I prefer to use Interfaces so I can specify each behaviour. At the moment it seems to be all simple and everything can be extended from the Entity abstract class, but there will be a moment when you will differentiate each entity by many other feature.
For example a flying enemy, a walking enemy, ecc ecc and you can specify each feature with an interface.
In this case, the interface is also very simple. But you could specify many methods that you want to be implemented such as
public boolean canCollide();
public boolean isAlive(); //if the entity is already dead you might want not to stop a bullet
public boolean isAnimatingDeath(); //if the entity is animating death could collide with another antity because of its exploding animation, maybe you want to avoid that.
You can implement som method in the abstract Entity class, but that abstract entity shouldn't know about its children. This is the reason to implement some methods using the generic "Collidable" type as input, directly in the children.
Upvotes: 0
Reputation: 917
For me the Entity class knowing about Enemy and Shot is wrong.
Why don't you remove these two methods from Entity and subclasses:
public abstract void collidesWith(Enemy e);
public abstract void collidesWith(Shot s);
And keep and implement only:
public abstract void collidesWith(Entity e);
If you need to know the type of Entity e passed as argument, you can use reflection, but this is bad design. It is better to implement collidesWith in such way that it doesn't need to know the exact type of the passed argument.
Upvotes: 0
Reputation: 11
Might be wrong, I am new to answering questions on stackoverflow.
Hope this gives you some clarity:
entity.collidesWith(eList.get(i));
eList.get(i) in that line, in your Physics class Entity returns an an object of type Entity. This is because it is defined like that:
LinkedList<Entity> eList
That means that if you have an overload that takes that Entity it would just call that method. This is exactly what I see in your code. You have a method overload for "collidesWith" with Argument: Entity. In all of the children classes of Entity.
I think you should read more about "Java Polymorphism".
Upvotes: 1
Reputation: 2685
Look into Java's Generics. I think you could use something like this:
public abstract class Entity<T> {
protected double x;
protected double y;
public Entity (double x, double y) {
this.x = x;
this.y = y;
}
public abstract void tick();
public double getX() { return x; }
public double getY() { return y; }
public Rectangle getBounds(int width, int height) {
return new Rectangle((int)x, (int)y, width, height);
}
public abstract void collidesWith(T e);
}
Upvotes: 1