Gus Saalfeld
Gus Saalfeld

Reputation: 63

Calling a subclass from a subclass in java

In my code I have a player class and from that Player class I want to call in a projectile, but whenever I try calling in the projectile in game, I get this error

Exception in thread "Thread-2" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at ca.runner.level.Level.tick(Level.java:127)
at ca.runner.game.Game.tick(Game.java:149)
at ca.runner.game.Game.run(Game.java:118)
at java.lang.Thread.run(Unknown Source)

I think it has to do maybe with the fact that both Mob and Projectile both call Entity or something like that, but I'm not sure how to fix this. If anyone could help that would be great.

Player Class:

package ca.runner.game.entities;

import ca.runner.game.Game;
import ca.runner.game.InputHandler;
import ca.runner.gfx.Colours;
import ca.runner.gfx.Screen;
import ca.runner.level.Level;
import ca.runner.game.InputHandler;
import ca.runner.gfx.Colours;
import ca.runner.gfx.Screen;
import ca.runner.level.Level;



public class Player extends Mob{

private InputHandler input;
private int colour = Colours.get(-1, 111, 145, 543);
private int scale = 1;
protected boolean isSwimming = false;
private int tickCount = 0;
private int health = 10;
public Player(Level level, int x, int y, InputHandler input) {
    super(level, "Player", x, y, 1);
    this.input = input;
    this.health = health;
}


public void tick() {
    int xa = 0;
    int ya = 0;

    if(input.up.isPressed()) {ya--;}
    if(input.down.isPressed()) {ya++;}
    if(input.left.isPressed()) {xa--;}
    if(input.right.isPressed()) {xa++;}
    if(input.space.isPressed()) {
        BasicAttack Fireball = new BasicAttack(level, false, "Fireball", 10, 10, 1, 1, 1,    getPlayerMoveDir());
        level.addEntity(Fireball);
    }

    if (xa != 0 || ya != 0) {
        move(xa, ya);
        isMoving = true;
    }else {
        isMoving = false;
    }
    if (level.getTile(this.x >> 3, this.y >> 3).getId() == 4) {
        isSwimming = true;
    }
    if (isSwimming && level.getTile(this.x >> 3, this.y >> 3).getId() != 4) {
        isSwimming = false;
    }
    tickCount++;
}


public void render(Screen screen) { 
    int xTile = 0;
    int yTile = 28;
    int walkingSpeed = 4;
    int flipTop = (numSteps >> walkingSpeed) & 1;
    int flipBottom = (numSteps >> walkingSpeed) & 1;;

    if (movingDir == 1) {
        xTile += 2;
    } else if (movingDir > 1) {
        xTile += 4 + ((numSteps >> walkingSpeed) & 1) * 2;
        flipTop = (movingDir - 1) % 2;
    }

    int modifier = 8 * scale;
    int xOffset = x - modifier / 2;
    int yOffset = y - modifier / 2 - 4;
    if (isSwimming) {
        int waterColour = 0;
        yOffset += 4;
        if (tickCount % 60 < 15) {
            waterColour = Colours.get(-1, -1, 225, -1);
        } else if (15 <= tickCount % 60 && tickCount % 60 < 30) {
            yOffset -= 1;
            waterColour = Colours.get(-1, 225, 115, -1);
        } else if (30 <= tickCount % 60 && tickCount % 60 < 45) {
            waterColour = Colours.get(-1, 115, -1, 225);
        } else {
            yOffset -= 1;
            waterColour = Colours.get(-1, 225, 115, -1);
        }
        screen.render(xOffset, yOffset+3, 0 + 27 * 32, waterColour, 0x00, 1);
        screen.render(xOffset + 8, yOffset+3, 0 + 27 * 32, waterColour, 0x01, 1);
    }
    //Upper Body
    screen.render(xOffset + (modifier * flipTop), yOffset, xTile + yTile * 32, colour, flipTop, scale);
    screen.render(xOffset + modifier - (modifier * flipTop), yOffset, (xTile + 1) + yTile * 32,  colour, flipTop, scale);

    if (!isSwimming) {
        //Lower Body
        screen.render(xOffset + (modifier * flipBottom), yOffset + modifier, xTile + (yTile+1) * 32, colour, flipBottom, scale);
        screen.render(xOffset + modifier - (modifier * flipBottom), yOffset + modifier, (xTile+1) + (yTile+1) * 32, colour, flipBottom, scale);
    }
}


public boolean hasCollided(int xa, int ya) {
    int xMin = 0;
    int xMax = 7;
    int yMin = 3;
    int yMax = 7;

    for (int x = xMin; x < xMax; x++) {
        if (isSolidTile(xa, ya, x, yMin)) {
            return true;
        }
    }
    for (int x = xMin; x < xMax; x++) {
        if (isSolidTile(xa, ya, x, yMax)) {
            return true;
        }
    }
    for (int y = yMin; y < yMax; y++) {
        if (isSolidTile(xa, ya, xMin, y)) {
            return true;
        }
    }
    for (int y = yMin; y < yMax; y++) {
        if (isSolidTile(xa, ya, xMax, y)) {
            return true;
        }
    }

    return false;
}

public int getPlayerHealth() {
    return health;
}

public int getPlayerMoveDir() {
    return movingDir;
}

}

Projectile Class:

package ca.runner.game.entities;

import ca.runner.level.Level;
import ca.runner.level.tiles.Tile;

public abstract class Projectile extends Entity{

protected String name;
protected int speed;
protected int numSteps = 0;
protected boolean isMoving;
protected int movingDir = 1;
protected int scale;
protected int damage;
protected boolean emitter;
protected int moveDir;

public Projectile(Level level, boolean isEmitter, String name, int x, int y, int speed, int damage, int scale, int MoveDir) {
    super(level);
    this.name = name;
    this.x = x;
    this.y = y;
    this.speed = speed;
    this.damage = damage;
    this.emitter = isEmitter;
    this.scale = scale;
    this.moveDir = moveDir;
}

public boolean isEmitter() {
    return emitter;
}

public void move(int xa, int ya) {
    //if(!hasCollided(xa, ya)) {
        x+= xa * speed;
        y += ya * speed;
    //} else {
    //  level.removeEntity(this);
    //}
}

//public abstract boolean hasCollided(int xa, int ya);

protected boolean isSolidTile(int xa, int ya, int x, int y) {
    if (level == null) { return false;}
    Tile lastTile = level.getTile((this.x + x) >> 3, (this.y + y) >> 3);
    Tile newTile = level.getTile((this.x + x + xa) >> 3, (this.y + y + ya) >> 3);
    if (!lastTile.equals(newTile) && newTile.isSolid()) {
        return true;
    }
    return false;
}

public String getName() {
    return name;
}

}

EDIT: Added the full stacktrace

Upvotes: 1

Views: 97

Answers (1)

Samuel &#197;slund
Samuel &#197;slund

Reputation: 3274

I would guess that the addEntity method of the Level class adds the Fireball to a collection. The "tick" method in your Player class is probably overriding a tick method in the "Mob" class that is called from something that loops over the same collection that addEntity wants to add to.

Have a look at the documentation for your iterator, it will tell you that it throws the "ConcurrentModificationException" if someone modifies the collection while it is iterating across it.

I can think of several ways to solve the problem.

  • Add the Fireball to a list of things to be added to your collection after the tick is done.
  • Keep some kind of reference to the iterator that you can call to make the addition. (Feels like a involved solution since you might need to handle "inTick" and "outsideTick" differently.
  • Iterate over the collection using an old-style "getAt" to retrieve the values. This will mean you are responsible for the concurrency yourself.

I hope this helps.

Upvotes: 1

Related Questions