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

2023/4/7

How to make a damage and kill system with one bullet and multiple different health values for actors

Deepfist Deepfist

2023/4/7

#
So currently I have a working system for my player.class, where if the bullet hits the player, then the bullet removes 1 health from the player.class. This works fine and when the health from the player == 0, then the player will kill (remove) himself with dead(); However, I also want to add this to my enemies, at first this worked fine as the enemies.class was only 1 class and the PlayerBullet.class was only designed to kill the enemies.class. I wanted to make subclasses for the enemies.class with different health values. Now when a PlayerBullet hits an enemy, it gives an error. This is because it is forced to remove itself. Now, instead of giving an error, whenever I reset my game, the health value of the enemy I hit is zero, which means that it will kill itself imidiately, incase my dead(); code is active. If it isn't active then the bullet will just pass trough it without doing anything The code for my EnemyBullet (against player)
import greenfoot.*;  
public class EnemyBullet extends Projectile
{
    public void act()
    {
        Hit();
        //move(Speed); //Speed is een static int van Projectile
    }
public void Hit() {
       Player player = (Player)getOneIntersectingObject(Player.class);
       if(player != null && player.Playerhealth != 0) {
           player.Playerhealth--;
           getWorld().removeObject(this);
       }
      }
}
This works just fine as it is only against the Player.class This is the Current code for the PlayerBullet (against enemies) It isn't finished as it wouldn't really work for me
import greenfoot.*; 
public class PlayerBullet extends Projectile
{
    public void act()
    {
        move(Speed);
        Hit();
    }
    public void Hit() {
       Shooter shot = (Shooter)getOneIntersectingObject(Shooter.class);
       Sniper snip = (Sniper)getOneIntersectingObject(Sniper.class);
       Stalker stal = (Stalker)getOneIntersectingObject(Stalker.class);
       if(stal != null && stal.StalkerHealth != 0) {
           stal.StalkerHealth--;
           getWorld().removeObject(this);
       }
       if(snip != null && snip.SniperHealth != 0) {
           snip.SniperHealth--;
           getWorld().removeObject(this);
       }
       if(shot != null && shot.ShooterHealth != 0) {
           shot.ShooterHealth--;
           getWorld().removeObject(this);
       }
        }
    }
The code is exactly the same, except for the names. Yet it doesn't work. Speed is just movement and not important I'd also like to add that the health values are static and they come from the class Enemies. Stalker, Shooter and Sniper are subclasses of Enemies.
Deepfist Deepfist

2023/4/7

#
This is the dead(); code from the Player.class, this works and is exactely the same for the enemy classes, difference is that the enemy classes die the moment I press Run
public void Dead() {
        if(HealthShield == true && isTouching(EnemyBullet.class)) {
            Playerhealth++;
            HealthShield = false;
        }
      else {
        if (Playerhealth == 0 && HealthShield == false) {
          getWorld().removeObject(this);
        }
      }
The healthshield is just a powerup and not of importance this is the code for the enemies
import greenfoot.*;  
public class Sniper extends Enemies
{
    public void act() {
        Dead();
    }
    
    public void Dead() {
        if(SniperHealth <=0) {
            getWorld().removeObject(this);
        }
    }
}
Note that after reaching 0 health the first time, it just dies on starting run. if I remove this code and add it back, then it won't die on run until I hit it Oh also if danpost is reading this, my apologies for not responding to response you gave me for my first discussion, I didn't really understand it and I felt a bit lazy so I didn't do anything for this. Today I changed the code after looking online a bit and this is what I came up with. I believe you meant this code with that response
danpost danpost

2023/4/7

#
Deepfist wrote...
the health values are static and they come from the class Enemies. Stalker, Shooter and Sniper are subclasses of Enemies.
A static variable cannot retain more than one value for the entire project. That is, with a static health value in Enemies, for example, all enemies will have the same health value at all times. For each enemy to retain their own health value, you cannot use static. Without static, it will be an instance variable (a field for each instance) as opposed to a class variable (one field for all instances). I do not think (removing static from those fields will cause to much of a problem as you access the fields through the actor and not the class (at least from what I see). You can add constructors to each subclass of Enemies to set the initial health of its type. For example:
public class Sniper extends Enemies
{
    public Sniper()
    {
        enemyHealth = 100;
        // etc.
    }
    // etc.
where the health field, enemyHealth, is only declared in the Enemies class. That is, no health fields should be declared in any subclass of Enemies:
public class Enemies extends Actor
{
    public int enemyHealth;
    // etc.
You will need to replace SniperHealth, StalkerHealth and the others with enemyHealth throughout the project. Also, be aware that static fields retain their values even during project resetting. It takes a re-compiling of the project to reset them. So, make sure you initialize any and ALL appropriate static fields you have during the initial world construction.
Deepfist Deepfist

2023/4/8

#
Alright this worked. Thx for the help
Deepfist Deepfist

2023/4/8

#
Nevermind, I have multiple problems which look like each other, and it has to do with getWorld().removeObject(this); Whenever my bullet hits and actor, it will delete itself with that code. If the enemy has less then the desired Health (EnemyHealth for all of them), then it will also delete itself, the bullet also gets deleted by itself. However, if I add a code where the bullet checks if it is at the world edge and then deletes itself, then whenever I hit an enemy, I get an error. If my enemy gets deleted whilst it has a code which wants to damage the player class, then it will also give an error. I also tried to add a turnTowards code for the enemy. It turns towards the player, but if the player is deleted by bullets or something else, then it will also (again) give an error. This is the code for the bullet that works
import greenfoot.*; 
public class PlayerBullet extends Projectile
{
    public void act()
    {
        move(Speed);
        Hit();
        
    }
    public void Hit() {
       Enemies enem = (Enemies)getOneIntersectingObject(Enemies.class);
       if(enem != null && enem.EnemiesHealth != 0) {
           enem.EnemiesHealth--;
           getWorld().removeObject(this);
       }
       
    }
This is the code for the bullet that fails
import greenfoot.*; 
public class PlayerBullet extends Projectile
{
    public void act()
    {
        move(Speed);
        Hit();
        Dead();
    }
    public void Hit() {
       Enemies enem = (Enemies)getOneIntersectingObject(Enemies.class);
       if(enem != null && enem.EnemiesHealth != 0) {
           enem.EnemiesHealth--;
           getWorld().removeObject(this);
       }
       
    }
    public void Dead() {
        if(atWorldEdge()) {
            getWorld().removeObject(this);
        }
    }
The problem is most likely the same for all the actors in this game, so if this gets solved, then I can also implement it for the other actors (if it is the same problem). This is the error message
java.lang.IllegalStateException: Actor has been removed from the world.
	at greenfoot.Actor.failIfNotInWorld(Actor.java:722)
	at greenfoot.Actor.getX(Actor.java:169)
	at Animal.atWorldEdge(Animal.java:10)
	at PlayerBullet.Dead(PlayerBullet.java:19)
	at PlayerBullet.act(PlayerBullet.java:8)
	at greenfoot.core.Simulation.actActor(Simulation.java:567)
	at greenfoot.core.Simulation.runOneLoop(Simulation.java:530)
	at greenfoot.core.Simulation.runContent(Simulation.java:193)
	at greenfoot.core.Simulation.run(Simulation.java:183)
Caused by: greenfoot.ActorRemovedFromWorld
	at greenfoot.World.removeObject(World.java:466)
	at PlayerBullet.Hit(PlayerBullet.java:14)
	at PlayerBullet.act(PlayerBullet.java:7)
	... 4 more
danpost danpost

2023/4/8

#
Deepfist wrote...
Nevermind, I have multiple problems which look like each other, and it has to do with getWorld().removeObject(this);
That is a common problem that, with some understanding, can be easily resolved. Once the problem line is executed, none of the following actions can require the actor be in the world. The getWorld method will return a null value, which therefore cannot have any methods executed on it; and any collision code or coordinate access code, which require the actor be in the world, by the way, will cause an error. There are actually several ways to avoid the error, which I will briefly describe here: (1) have actors that get hit by bullets remove the bullet on collisions; (the preferred way) (2) over-use the check (not preferred):
if (getWorld() != null) {  /** some code */ }
or the line:
if (getWorld() == null) return;
(3) use one combination of checks for all ways the actor can be removed (2nd preferred way):
public void act() {
    move(Speed);
    if (Hit() || isAtEdge()) {
        getWorld().removeObject(this);
    }
}

private boolean Hit() {
    Enemies enem = (Enemies)getOneIntersectingObject(Enemies.class);
    if (enem != null && enem.EnemiesHealth > 0) {
        enem.EnemiesHealth--;
        return true;
    }
    return false
}
The Animal class is a somewhat pointless class since the isAtEdge, isTouching and removeTouching methods have been added to the Actor class. These methods are synonymous respectively with the atWorldEdge, canSee and eat methods.
Deepfist Deepfist

2023/4/9

#
I didn't really know about public booleans and returning true or false, but it seems to work for the killing and I also know how to implement it for more stuff now, so thx with that. One issue that I still don't know how to fix though has to do with my followAngle code. This code follows the location of the player. it works fine until the player dies whilst the actor with this code is still alive. if there are no actors with this code alive, then the player can die just fine. This is the code I use for following the player.class
public void followAngle() {
        Player play = (Player)getWorld().getObjects(Player.class).get(0);
        turnTowards(play.getX(),play.getY());
      }
I saw this in a previous post and it does work, but if the player dies then it just sends an error. I probably need to implement something that checks if the player is alive, but I don't know what code I could use for that
danpost danpost

2023/4/9

#
Deepfist wrote...
This code follows the location of the player. it works fine until the player dies whilst the actor with this code is still alive. if there are no actors with this code alive, then the player can die just fine. This is the code I use for following the player.class << Code Omitted >> I saw this in a previous post and it does work, but if the player dies then it just sends an error. I probably need to implement something that checks if the player is alive, but I don't know what code I could use for that
Adding in at line 2:
public void followAngle() {
    if (getWorld().getObjects(Player.class).isEmpty()) return;
    Player play = (Player)getWorld().getObjects(Player.class).get(0);
    turnTowards(play.getX(),play.getY());
}
Deepfist Deepfist

2023/4/9

#
Well that was easier than expected, thx
You need to login to post a reply.