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

2020/3/25

Making an NPC randomly roam an area WITHOUT rotating the image

1
2
LewisEro LewisEro

2020/3/25

#
so here i have my first NPC for the game. it is meant to roam a small area until the player enters a certain range, upon which it should track the player and interact in some way, but that is not the topic of this post. i realise that using rotation would be quite easy in this case, but ive tested it and it looks quite weird in my opinion.. heres the code:
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

public class NPC extends Actor
{
    PC player;
    public void act() 
    {
        roam();//randomly patrol a defined area around this object
        trackPlayer();//track the player as soon as they enter a certain AOE, increase speed
    }  
    public NPC(PC player){
        this.player = player;
    }
    public void roam(){
        if(getObjectsInRange(250, PC.class).isEmpty()){

        }
    }
    public void trackPlayer(){
    
    }
}
the movement would go into the if condition within public void roam(). i have tried using random numbers in conjunction with the setlocation method, but it didnt work very well....
LewisEro LewisEro

2020/3/25

#
of course turn would work if we account for the rotation by counter rotating the image accordingly, but i find that approach somewhat unelegant and cumbersome
LewisEro LewisEro

2020/3/25

#
ive tried this in my roam method, expecting it to get a new random number on each frame and thus making for random movement, but it does not update the random and instead just flies north/west every time
    public void roam(){


        if(getObjectsInRange(250, PC.class).isEmpty()){
            setLocation(getX(), getY() -Greenfoot.getRandomNumber(3) -6);
            setLocation(getX(), getY() +Greenfoot.getRandomNumber(3) -6);
            setLocation(getX() -Greenfoot.getRandomNumber(3) -6, getY());
            setLocation(getX() +Greenfoot.getRandomNumber(3) -3, getY());
        }
    }
LewisEro LewisEro

2020/3/25

#
Ah, i didnt put my random number range in brackets, therein was the problem.... Sorry!
LewisEro LewisEro

2020/3/25

#
    public void roam(){


        if(getObjectsInRange(250, PC.class).isEmpty()){
            setLocation(getX(), getY() -Greenfoot.getRandomNumber(3));
            setLocation(getX(), getY() +Greenfoot.getRandomNumber(3));
            setLocation(getX() -Greenfoot.getRandomNumber(3), getY());
            setLocation(getX() +Greenfoot.getRandomNumber(3), getY());
        }
    }
to get something out of this thread I might add that like this, it does indeed do random movements on the spot, however it is very shakey. any pointers as to how to clean that up? perhaps a timer that tells it to only execute one movement for a second or so before switching might work?
danpost danpost

2020/3/25

#
More realistic might be to have a field maintain the direction of movement, then you could have this:
private int rotation = Greenfoot.getRandomNumber(360);

public void roam()
{
    if (getObjectsInRange(250, PC.class).isEmpty())
    {
        rotation  += 15-Greenfoot.getRandomNumber(31); //  (-15, 15) degree change in direction
        setRotation(rotation);
        move(3);
        setRotation(0);
    }
}
LewisEro LewisEro

2020/3/25

#
Yes, that looks way more natural! thanks a lot. how exactly prevents the image from rotating here? im assuming its line 10. now if there was a way to limit roaming down to an area around the initial spawn point.. ill try to figure it out!
danpost danpost

2020/3/25

#
LewisEro wrote...
how exactly prevents the image from rotating here? im assuming its line 10
Correct.
LewisEro LewisEro

2020/3/25

#
LewisEro wrote...
Yes, that looks way more natural! thanks a lot. how exactly prevents the image from rotating here? im assuming its line 10. now if there was a way to limit roaming down to an area around the initial spawn point.. ill try to figure it out!
So i came up with this:
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * Write a description of class NPC here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class FloatingSkull extends Actor
{
    PC player;
    private int rotation = Greenfoot.getRandomNumber(360);
    private int speed = 1;
    private boolean aggro = false;
    private int[] roamLimit;
    public void act(){
        movementAI();//track the player as soon as they enter a certain AOE, increase speed
    }  
    public FloatingSkull(PC player){
        this.player = player;
    }
    public FloatingSkull(){
        int[] roamLimit = new int[]{getX()+100, getX()-100, getY()+100, getY()-100};       
    }
    public void movementAI(){ //roams until player enters certain AOE
        if(getObjectsInRange(150, PC.class).isEmpty() && aggro == false){
            rotation  += 15-Greenfoot.getRandomNumber(31); //  (-15, 15) degree change in direction
            setRotation(rotation);
            move(speed*2);
            setRotation(0);
            if(getX() > roamLimit[0] || getX() < roamLimit[1] || getY() > roamLimit[2] || getY() < roamLimit[3]){
                move(speed*(-1));
            }
        }
        if(!getObjectsInRange(50, PC.class).isEmpty()){
            move(speed*0);
        }
        else{
            turnTowards(player.getX(), player.getY());         
            move(speed);
            setRotation(0);
        }
        if(!getObjectsInRange(150, PC.class).isEmpty()){
            aggro = true;
        }
    }
}
yes yes i know its not AI haha. so the last if condition using the array is meant to confine the skull's movement by inverting his direction (by doing speed*(-1)). however upon running this gives me an error:"java.lang.NullPointerException at FloatingSkull.movementAI(FloatingSkull.java:31) at FloatingSkull.act(FloatingSkull.java:17)" any ideas?
danpost danpost

2020/3/25

#
Line 23 is executed during the actor's creation -- before it can possibly be placed into a world. The actor has no coordinate values that getX and getY can return at that time.
LewisEro LewisEro

2020/3/25

#
ah, okay.
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * Write a description of class NPC here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class FloatingSkull extends Actor
{
    PC player;
    private int rotation = Greenfoot.getRandomNumber(360);
    private int speed = 1;
    private boolean aggro = false;
    private int[] roamLimit;
    private int roamLimitRange = 50;
    public void act(){
        movementAI();//track the player as soon as they enter a certain AOE, increase speed
    }  
    public FloatingSkull(PC player){
        this.player = player;
    }
    public FloatingSkull(){
        //int[] roamLimit = new int[]{getX()+roamLimitRange, getX()-roamLimitRange, getY()+roamLimitRange, getY()-roamLimitRange};       
    }
    public void movementAI(){
        int[] roamLimit = new int[]{getX()+roamLimitRange, getX()-roamLimitRange, getY()+roamLimitRange, getY()-roamLimitRange};       
            if(getObjectsInRange(150, PC.class).isEmpty() && aggro == false){
                rotation  += 15-Greenfoot.getRandomNumber(31); //  (-15, 15) degree change in direction
                setRotation(rotation);
                move(speed*2);
                setRotation(0);
                if(getX() < roamLimit[0] || getX() > roamLimit[1] || getY() < roamLimit[2] || getY() > roamLimit[3]){
                    move(speed*(-1));
                }
            }
            if(!getObjectsInRange(50, PC.class).isEmpty()){
                move(speed*0);
            }
            else{
                turnTowards(player.getX(), player.getY());         
                move(speed);
                setRotation(0);
            }
            if(!getObjectsInRange(150, PC.class).isEmpty()){
                aggro = true;
            }
    }
}
like this it does run, however the desired effect (that being confining the enemy into a "box" to roam within until the player comes close) is not achieved.
danpost danpost

2020/3/25

#
Now, the problem is that the array is being created anew each and every act cycle. Use the addedToWorld method to set the values of roamLimit, but do not declare a new array in doing so.
LewisEro LewisEro

2020/3/25

#
Sorry, I'm not entirely sure how/where to implement the addedToWorld() method in this case, but I did try this approach to try and circumvent multiple new declarations:


import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * Write a description of class NPC here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class FloatingSkull extends Actor
{
    PC player;
    private int rotation = Greenfoot.getRandomNumber(360);
    private int speed = 1;
    private boolean aggro = false;
    private int[] roamLimit;
    private int roamLimitRange = 50;
    private boolean firstBool = false;
    
    public void act(){
        first();
        movementAI();//track the player as soon as they enter a certain AOE, increase speed
    }  
    public FloatingSkull(PC player){
        this.player = player;
    }
    public void first(){    
            if(!firstBool){
            roamLimit[0] = getX()+roamLimitRange;
            roamLimit[1] = getX()-roamLimitRange;
            roamLimit[2] = getY()+roamLimitRange;
            roamLimit[3] = getY()-roamLimitRange; 
        }
        firstBool = true;
    }    
    public void movementAI(){
        //int[] roamLimit = new int[]{getX()+roamLimitRange, getX()-roamLimitRange, getY()+roamLimitRange, getY()-roamLimitRange};       
            if(getObjectsInRange(200, PC.class).isEmpty() && aggro == false){
                rotation  += 15-Greenfoot.getRandomNumber(31); //  (-15, 15) degree change in direction
                setRotation(rotation);
                move(speed*2);
                setRotation(0);
                if(getX() < roamLimit[0] || getX() > roamLimit[1] || getY() < roamLimit[2] || getY() > roamLimit[3]){
                    move(speed*(-1));
                }
            }
            if(!getObjectsInRange(150, PC.class).isEmpty()){
                move(speed*0);
            }
            else{
                turnTowards(player.getX(), player.getY());         
                move(speed);
                setRotation(0);
            }
            if(!getObjectsInRange(200, PC.class).isEmpty()){
                aggro = true;
            }
    }
}

unfortunately, it leaves me with another nullpoint exception, which i dont quite understand..
danpost danpost

2020/3/25

#
Copy/paste complete error message here please.
LewisEro LewisEro

2020/3/25

#
ah, the issue there i believe was that i did not properly declare my array globally (line 15)
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)

/**
 * Write a description of class NPC here.
 * 
 * @author (your name) 
 * @version (a version number or a date)
 */
public class FloatingSkull extends Actor
{
    PC player;
    private int rotation = Greenfoot.getRandomNumber(360);
    private int speed = 1;
    private boolean aggro = false;
    private int[] roamLimit = new int[4];
    private int roamLimitRange = 50;
    private boolean firstBool = false;
    
    public void act(){
        first();
        movementAI();//track the player as soon as they enter a certain AOE, increase speed
    }  
    public FloatingSkull(PC player){
        this.player = player;
    }
    public void first(){    
            if(!firstBool){
            roamLimit[0] = getX()+roamLimitRange;
            roamLimit[1] = getX()-roamLimitRange;
            roamLimit[2] = getY()+roamLimitRange;
            roamLimit[3] = getY()-roamLimitRange; 
        }
        firstBool = true;
    }    
    public void movementAI(){
        //int[] roamLimit = new int[]{getX()+roamLimitRange, getX()-roamLimitRange, getY()+roamLimitRange, getY()-roamLimitRange};       
            if(getObjectsInRange(200, PC.class).isEmpty() && aggro == false){
                rotation  += 15-Greenfoot.getRandomNumber(31); //  (-15, 15) degree change in direction
                setRotation(rotation);
                move(speed*2);
                setRotation(0);
                if(getX() < roamLimit[0] || getX() > roamLimit[1] || getY() < roamLimit[2] || getY() > roamLimit[3]){
                    move(speed*(-1));
                }
            }
            if(!getObjectsInRange(150, PC.class).isEmpty()){
                move(speed*0);
            }
            else{
                turnTowards(player.getX(), player.getY());         
                move(speed);
                setRotation(0);
            }
            if(!getObjectsInRange(200, PC.class).isEmpty()){
                aggro = true;
            }
    }
}
this runs without the error, but does not achieve the effect. if you're still interested in the error it is this: java.lang.NullPointerException at FloatingSkull.first(FloatingSkull.java:30) at FloatingSkull.act(FloatingSkull.java:22) 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)
There are more replies on the next page.
1
2