Reputation: 65
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
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