Kris Purdy
Kris Purdy

Reputation: 65

Array list appears to not work properly in combat logic?

I am making a tower offense game for Android and I am getting some unusual behavior in my combat system. I have a minion that walks across the screen and collides with an enemy unit. They engage in combat, but for some reason the enemy disengages from combat.

When units are spawned they are stored in an array list. I use a lockedOn variable that stores a pointer to the object in the array list that it's currently in combat with. It seems like when a new enemy unit is spawned, it adversely effects the enemy that is currently in combat. I don't know if something is altering the pointer for the object when a new item is added to the array list that the minion is locked onto or what. I have tried everything I can think of but I can't narrow down the issue.

I'm posting the code with a lot of the extra stuff eliminated for ease of viewing. I am including the main game class and a copy of the warrior class. The champion class is identical to the warrior with slightly different values for attack and attack speed.

Main Game Class

public final List<Minion_Warrior> mWarriors;
public final List<Enemy_Champion> eChampions;

this.mWarriors = new ArrayList<Minion_Warrior>();
this.eChampions = new ArrayList<Enemy_Champion>();

public void update(float deltaTime) {

    removeDead();
    updateMinions(deltaTime);
    updateEnemies(deltaTime);
    updateTowers(deltaTime);
}

private void removeDead() {
    int len = mWarriors.size();
    for(int i = 0; i < len; i++) {
        Minion_Warrior warrior = mWarriors.get(i);
        if(warrior.canDie) {
            mWarriors.remove(warrior);
            warrior = null;
            len = mWarriors.size();
        }
    }
    len = eChampions.size();
    for(int i = 0; i < len; i++) {
        Enemy_Champion champion = eChampions.get(i);
        if(champion.canDie) {
            eChampions.remove(champion);
            champion = null;
            len = eChampions.size();
        }
    }
}

private void updateMinions(float deltaTime) {

    updateWarriors(deltaTime);
}

private void updateWarriors(float deltaTime) {
    // For each warrior
    int lenWar = mWarriors.size();
    for(int i = 0; i < lenWar; i++) {
        Minion_Warrior warrior = mWarriors.get(i);

        // If the warrior is alive
        if(warrior.isAlive) {

            // If the warrior has no current target
            if(warrior.lockedOn == null) {

                // Check each champion
                int lenChamp = eChampions.size();
                for(int j = 0; j < lenChamp; j++) {
                    Enemy_Champion champion = eChampions.get(j);

                    // If a champion is alive
                    if(champion.isAlive) {

                        // And the champion does not currently have a target
                        if(champion.lockedOn == null) {

                            // Has the champion collided with the warrior?
                            if(OverlapTester.overlapRectangles(champion.bounds, warrior.bounds)) {
                                warrior.lockedOn = champion;
                                champion.lockedOn = warrior;
                                warrior.state = warrior.MINION_STATE_COMBAT;    
                                champion.state = champion.ENEMY_STATE_COMBAT;
                                break;
                            }
                        }
                    }   
                }
            }
        }

        warrior.update(deltaTime);
    }
}

private void updateEnemies(float deltaTime) {   
    updateChampions(deltaTime);
}

private void updateChampions(float deltaTime) {
    int len = eChampions.size();
    for(int i = 0; i < len; i++) {
        Enemy_Champion champion = eChampions.get(i);
        champion.update(deltaTime);
    }
}

private void updateTowers(float deltaTime) {
    if(spawnEnemies) {          
        if(!tCastleCanDeploy) {
            tCastleTimer += deltaTime;
            if(tCastleTimer > 10) {
                tCastleTimer = 0;
                tCastleCanDeploy = true;
                tCastleQue = 3;
            }
        }
        else if(tCastleCanDeploy && !tCastleWaiting) {
            tCastleDeployTimer += deltaTime;
            if(tCastleDeployTimer > 2) {
                tCastleDeployTimer = 0;
                tCastleWaiting = true;
            }
        }
        else if(tCastleCanDeploy && tCastleWaiting) {
            if(tCastleQue == 3) {
                Enemy_Champion champion = new Enemy_Champion(145, 51 - 0.5f);
                eChampions.add(champion);
                tCastleQue -= 1;
                tCastleWaiting = false;
            }
            else if(tCastleQue == 2) {
                Enemy_Archer archer = new Enemy_Archer(145, 51 - 0.5f);
                eArchers.add(archer);
                tCastleQue -= 1;
                tCastleWaiting = false;
            }
            else if(tCastleQue == 1) {
                Enemy_Mage mage = new Enemy_Mage(145, 51 - 0.5f);
                eMages.add(mage);
                tCastleQue -= 1;
                tCastleWaiting = false;
            }
            else if(tCastleQue == 0) {
                tCastleWaiting = false;
                tCastleCanDeploy = false;
            }
        }
    }
}  

Minion_Warrior Class

public class Minion_Warrior extends DynamicGameObject {
    public static final float MINION_WIDTH = 4.0f;
    public static final float MINION_HEIGHT = 4.0f;
    public static final int MINION_STATE_DEPLOY = 0;
    public static final int MINION_STATE_ROUTE = 1;
    public static final int MINION_STATE_COMBAT = 2;
    public static final int MINION_STATE_DEATH = 3;
    public static final float MINION_VELOCITY = 1.75f;

    public enum Animation {STANDING, WALKING, ATTACKING, DYING}

    int life;
    int attack;
    float attackSpeed;
    float attackTimer;
    boolean canAttack;
    float deathTimer;
    boolean canDie;
    boolean isAlive;
    int state;
    int cost;
    float stateTime;
    public static Enemy_Champion lockedOn;
    Animation animation = Animation.WALKING;
    Rectangle collisionBox;

    public Minion_Warrior(float x, float y) {
        super(x, y, MINION_WIDTH, MINION_HEIGHT);
        life = 100;
        attack = 15;
        attackSpeed = 1.8f;
        attackTimer = 0;
        canAttack = true;
        deathTimer = 0;
        canDie = false;
        isAlive = true;
        state = MINION_STATE_DEPLOY;
        cost = 0;
        stateTime = 0;
        velocity.set(MINION_VELOCITY, 0);
        lockedOn = null;
    }

    public void update(float deltaTime) {
        // Adjust the collision detection box
        //collisionBox = new Rectangle(position.x + 1, position.y - 2, 1.1f, 1);
        //bounds = collisionBox;
        bounds.lowerLeft.set(position).sub(MINION_WIDTH / 2, MINION_HEIGHT / 2);

        switch(state) {

        case MINION_STATE_DEPLOY:
            // Move to lane
            if(position.x < 35 && position.y > World.LANE_1 + 1.5) {
                velocity.set(MINION_VELOCITY, -MINION_VELOCITY);
                position.add(velocity.x * deltaTime, velocity.y * deltaTime);
            } else {
                velocity.set(MINION_VELOCITY, 0);
                position.add(velocity.x * deltaTime, 0);
                state = MINION_STATE_ROUTE;
            }
            break;

        case MINION_STATE_ROUTE:
            canAttack = true;
            position.add(velocity.x * deltaTime, 0);
            break;

        case MINION_STATE_COMBAT:
            // If there is a target
            if(lockedOn.isAlive) {
                // If the minion can not attack
                if(!canAttack) {
                    attackTimer += deltaTime;
                    if(attackTimer > attackSpeed) {
                        attackTimer = 0;
                        canAttack = true;
                    }
                }
                // If the minion can attack
                else if(canAttack) {
                    animation = animation.ATTACKING;
                    canAttack = false;
                    lockedOn.life -= attack;
                }
                checkOpponent();
            } else {
                state = MINION_STATE_ROUTE;
            }
            break;

        case MINION_STATE_DEATH:
            animation = animation.DYING;
            if(!canDie) {
                deathTimer += deltaTime;
                if(deathTimer > 2) {
                    deathTimer = 0;
                    canDie = true;
                }
            }
        }

        stateTime += deltaTime;
    }

    private void checkOpponent() {
        if(lockedOn.life <= 0) {
            lockedOn.state = lockedOn.ENEMY_STATE_DEATH;
            lockedOn.isAlive = false;
            lockedOn.lockedOn = null;

            state = MINION_STATE_ROUTE; 
            animation = animation.WALKING;
            lockedOn = null;
        }
    }
}

Upvotes: 1

Views: 72

Answers (1)

N8sBug
N8sBug

Reputation: 352

Looks like you have lockedOn as a static variable. From reading up on static variables, I believe this means that when you create a new warrior and reset lockedOn you affect all lockedOn's for everyone because the lockedOn is applied to the class instead of an individual object. So, the new one, who isn't lockedOn will cause lockedOn objects to be null and walk away. Please try removing the static from the animation class and let me know if it worked.

This is a good description of what I think is happening, see the code examples on counts

Upvotes: 1

Related Questions