This site requires JavaScript, please enable it in your browser!
Greenfoot back
Arakniode
Arakniode wrote ...

2018/1/30

[Help Needed :)] Changing the Actor target of a troop

Arakniode Arakniode

2018/1/30

#
Hi! I was wondering if what is in the title right now is indeed possible, since it throws a NullPointerException. The situation is: I have troops that are sent onto Resource.class objects. When the troop reaches the resource, it starts damaging it every few acts, by calling a method of the Resource.class object itself. When the health reaches 0 or less, the resource calls a "getWorld().removeObject(this);". The NullPointerException is called at that point. BUT: the resource has been deleted from the world (no longer visible). The problem appeared when I tried to make the troop look for another Resource.class object. Here's my code, in order of what calls what: act() method of Troop1.class (subclass of Troop.class):
public void act()
    {
        // check for resources on its path to go to
        Resource resource = getOneResource(perceptionRadius);
        if(resource != null && !hasChosenTarget){
            hasChosenTarget = true;
            this.currentTarget = resource;
            // set new destination to center of touched resource, with a slight offset (method in Troop.class)
            setNewOffsetDestination(currentTarget.getX(), currentTarget.getY());
        }
        // check if the troop needs to attack its currentTarget
        if(hasReachedCurrentTarget()){
            this.isAttacking = true;
        }
        if(isAttacking){
            attack();
            attackDelay++;
        }
        // move
        moveToDestination(destX, destY);
the attack() method of Troop.class:
protected void attack(){
        if(currentTarget == null){
            isAttacking = false;
            this.currentTarget = getNearestResource();
            setNewOffsetDestination(currentTarget.getX(), currentTarget.getY());
        }
        if(isAttacking == true && attackDelay >= reloadTime){
            currentTarget.removeHealth(attackDamage);
            attackDelay = 0;
        }
    }
the removeHealth() method of Resource.class:
public void removeHealth(int damageReceived){
        this.health -= damageReceived;
        if(health <= 0){
            getWorld().removeObject(this);
        }
    }
finally, the getNearestResource() method of Troop.class (it is supposed to find the next targetResource of the troop):
protected Resource getNearestResource(){
        Resource resource = null;
        for (int i = 0; resource == null; i++){
            List<Resource> touchedResources = getNeighbours(i, true, Resource.class);
            if(!touchedResources.isEmpty()){
                resource = touchedResources.get(0);
            }
        }
        return resource;
    }
thank you very much for your help! :D EDIT 1: the exact exception that is thrown is: java.lang.NullPointerException at Resource.removeHealth(Resource.java:73) at Troop.attack(Troop.java:58) at Troop1.act(Troop1.java:33) at greenfoot.core.Simulation.actActor(Simulation.java:604) at greenfoot.core.Simulation.runOneLoop(Simulation.java:562) at greenfoot.core.Simulation.runContent(Simulation.java:221) at greenfoot.core.Simulation.run(Simulation.java:211) java.lang.NullPointerException at Resource.removeHealth(Resource.java:73) at Troop.attack(Troop.java:58) at Troop1.act(Troop1.java:33) at greenfoot.core.Simulation.actActor(Simulation.java:604) at greenfoot.core.Simulation.runOneLoop(Simulation.java:562) at greenfoot.core.Simulation.runContent(Simulation.java:221) at greenfoot.core.Simulation.run(Simulation.java:211)
danpost danpost

2018/1/30

#
I think if you start out with the following, you should be okay (or at least get by the current issue):
if (currentTarget != null && currentTarget.getWorld() == null) currentTarget = null;
if (currentTarget == null) hasChosenTarget = false;
Arakniode Arakniode

2018/1/30

#
Thanks! I put that at the start of the act() method, and now it works perfectly. But does that mean that when I remove the resource with getWorld().removeObject(this);, the resource still exists, but is not in the world?
Vercility Vercility

2018/1/30

#
Arakniode wrote...
Thanks! I put that at the start of the act() method, and now it works perfectly. But does that mean that when I remove the resource with getWorld().removeObject(this);, the resource still exists, but is not in the world?
Yes. There is no explicit way to delete Variables or references in java, the trash collector will get rid of them automatically when they're not needed anymore
danpost danpost

2018/1/30

#
Arakniode wrote...
does that mean that when I remove the resource with getWorld().removeObject(this);, the resource still exists, but is not in the world?
As long as a valid reference exists on the object, it will not go to the gc. The field 'currentTarget' references the actor (even if removed from the world). Think of when you create an object, before it is added to the world. It must remain or you would never be able to do anything with it. The only thing removing it from the world does is (1) remove its reference from the list of actors the World object keeps; and (2) sets the World field of the actor to 'null'. Only if no references remain on the object is the gc going to collect it.
You need to login to post a reply.