2

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?

1
  • 1
    Please provide the declaration of each class you mentioned. Commented Nov 29, 2020 at 20:31

4 Answers 4

1

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);
}
Sign up to request clarification or add additional context in comments.

Comments

1

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

4 Comments

I like this solution, but I would use an interface as I specified in my answer. +1 tho
Thank you. I wasn't aiming to solve his problem, like your comprehensive answer. Mostly pointing as to why the code executes just those methods.
@OminousApple Really appreciating all the answers so far ^^ Polymorphism was my first idea, each class only having collidesWith(), with different parameter classes (eg. collidesWith(Shot s), collidesWIth(Enemy e)). But I also had to have the basic collidesWIth(Entity e) as it is an abstract function, and I think its necessary for the loop in Physics. The result was that only the cW(Entity e) was called in every class, instead of the ones with the matching parameter type. What can I do with that?
Let me put it like this. The collided object will always have a base type of Entity. (In the current case I can see in the code) You don't need the other method overloads. Because ` collidesWith(Entity e) ` will handle all of those. So you'll have your object Shot handled in that collidesWith(Entity e) . You'll have to figure out how to check if that Enitity e is Shot,Player,Bullet (a specific type that derives from Entity). A dirty way to check that is to use instanceOf in that method.
0

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.

Comments

0

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.

1 Comment

Thank you for your answer! Sadly, I'm afraid it wouldn't work for me now. It's similar to how I done it just a few hours ago, but after talking about it with my senior, he said that it wouldn't be a proper OOP way to go about it, because of the type checks in the "if"-s. I know that in the example code I gave is also far from correct, but that was the current state of my code, and I wanted to know where to go from here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.