Simple NPC system

i’ve programmed a simple system to use NPC’s. It’s very basic, but it will be a nice way to start from.[java]import com.jme3.math.Vector3f;

import com.jme3.math.FastMath;



public class NPC {



//constants

/**Don’t change the direction./

public final int DIRECTION_DONT_CHANGE = 1;

/**Change the direction at random intervals.
/

public final int DIRECTION_CHANGE_AT_RANDOM_INTERVALS = 2;

/**Always change the direction./

public final int DIRECTION_CHANGE_ALWAYS = 3;

/**Changes the NPC’s speed suddenly instaed of accelerating or decellerating.
/

public final int ACCELERATE_SUDDENLY = 1;

/**Smoothly accelerate or decellerate when the speed is changed./

public final int ACCELERATE_SMOOTH = 2;

/**Only move or rotate along the x axis.
/

public final int AXIS_X = 1;

/**Only move or rotate along the y axis./

public final int AXIS_Y = 2;

/**Only move or rotate along the z axis.
/

public final int AXIS_Z = 3;

/**Move or rotate along the x axis and the y axis./

public final int AXIS_XY = 4;

/**Move or rotate along the y axis and the z axis.
/

public final int AXIS_YZ = 5;

/**Move or rotate along the x axis and the z axis./

public final int AXIS_XZ = 6;

/**Move or rotate along all three axes.
/

public final int AXIS_XYZ = 7;

/**Always move the NPC./

public final int MOVE_ALWAYS = 1;

/**Let the NPC start and stop moving at random intervals.
/

public final int MOVE_AT_RANDOM_INTERVALS = 2;



//variables

private String Name;

private Vector3f position = new Vector3f(0,0,0);

private int directionChange = 3;

private int accelerationMode = 1;

private float speed = 0;

private float speed2 = 0;

private int directionAxis = 7;

private boolean moving = false;

private float directionChangeIntervals = 0.1f;

private float accelerationFactor = .1f;

private int moveMode;

private Vector3f direction = new Vector3f(0,0,0);

private boolean directionChanging = true;

private int directionChangeChance = 100;

private int startChance = 10;

private int stopChance = 10;

private float moveSpeed = 10;



public NPC(){



}



public NPC(String name){

Name = name;

}



/**Returns the name set for the NPC./

public String getName(){

return Name;

}



/**Returns when the direction will be changed (always, never or at rondom intervals).
/

public int getDirectionChangeMode(){

return directionChange;

}



/**Returns the acceleration mode (smooth or suddenly)./

public int getAccelerationMode(){

return accelerationMode;

}



/**Returns the location.

When your game uses a complex landscape, at will be better to only check for the

rotation and let the physics engine check for the location. When the NPC only walks on a simple plane or floats in space, the location will do./

public Vector3f getLocation(){

return position;

}



/**Returns the maximum direction that can be added to the direction. This is only used when you activate the changing of directions.
/

public float getDirectionChangeInterval(){

return directionChangeIntervals;

}



/**Returns the axes on which the direction will be changed.
/

public int getDirectionAxis(){

return directionAxis;

}



/**Returns the speed of the NPC./

public float getSpeed(){

return speed;

}



/**Returns the accellerationFactor (only osed when the acceleration is set to ‘smooth’).
/

public float getAccelerationFactor(){

return accelerationFactor;

}



/**Returns the direction of the NPC./

public Vector3f getDirection(){

return direction;

}



/**Returns wether the NPC is moving. only used when ‘move at random intervals’ is activated.
/

public boolean isMoving(){

return moving;

}



/**Returns wether the direction will be changed in the update method. Only used when you activate ‘change direction at random intervals’/

public boolean getDirectionChanging(){

return directionChanging;

}



/**Returns the chance of when the direction will change. A chance of 1 means that it will certainly be changed, a chance of 100 means 1 chanse on 100.
/

public int getDirectionChangeChance(){

return directionChangeChance;

}



/**Returns the chance on which the NPC should start moving. only used when ‘move at random intervals’ is activated./

public int getStartChance(){

return startChance;

}



/**Returns the chance on which the NPC should stop moving. only used when ‘move at random intervals’ is activated.
/

public int getStopChance(){

return stopChance;

}



/*Returns the speed on which the NPC should move/

public float getMoveSpeed(){

return moveSpeed;

}



/**Sets the name of the NPC. This is not used by this class, but it can be usefull in your own code./

public void setName(String name){

Name = name;

}



/**Sets the location.

When your game uses a complex landscape, at will be better to only check for the

rotation and let the physics engine check for the location. When the NPC only walks on a simple plane or floats in space, the location will do./

public void setLocation(Vector3f location){

position = location;

}



/**Sets the location.

When your game uses a complex landscape, at will be better to only check for the

rotation and let the physics engine check for the location. When the NPC only walks on a simple plane or floats in space, the location will do./

public void setLocation(float x,float y,float z){

position.x = x;

position.y = y;

position.z = z;

}



/**Returns the maximum interval which will be added to the direction of the NPC.
/

public void setDirectionChangeInterval(float interval){

directionChangeIntervals = interval;

}



/**Sets the axes on which the direction can change.
/

public void setDirectionAxis(int axis){

directionAxis = axis;

}



/**Sets the speed of the NPC.
/

public void setSpeed(float Speed){

if(accelerationMode == ACCELERATE_SMOOTH){

speed2 = Speed;

}

if(accelerationMode == ACCELERATE_SUDDENLY){

speed = Speed;

}

}



/**Sets weather the NPC is moving. Does not work when ‘move always’ is activated.

When you have activated ‘move at random intervals’, this will automatically be changed at random intervals./

public void setMoving(boolean move){

moving = move;

}



/**Sets the factor on which the NPC should accelerate. Only works when ‘accelerate smooth’ is activated./

public void setAccelerationFactor(float factor){

accelerationFactor = factor;

}



/**Sets the mode used for acceleration.
/

public void setAccelerationMode(int mode){

accelerationMode = mode;

}



/**Sets the direction of the NPC./

public void setDirection(Vector3f Direction){

direction = Direction;

}



/**Sets the direction of the NPC.
/

public void setDirection(float x,float y,float z){

direction.x = x;

direction.y = y;

direction.z = z;

}



/**Sets weather the direction should change in the update method. this is changed automatically when

  • ‘change at random intervals’ is enabled.*/

    public void setDirectionChanging(boolean enable){

    directionChanging = enable;

    }



    /**Sets the chance on which the direction should change.
  • This only works when ‘change at random intervals’ is enabled. A chance of 1 means always, and a chance of 100 means 1 time on 100 times.*/

    public void setDirectionChangeChance(int chance){

    directionChangeChance = chance;

    }



    /**Sets the chance on which the NPC should start moving.
  • This only works when ‘change at random intervals’ is enabled. A chance of 1 means always, and a chance of 100 means 1 time on 100 times.*/

    public void setStartChance(int chance){

    startChance = chance;

    }



    /**Sets the chance on which the NPC should stop moving.
  • This only works when ‘change at random intervals’ is enabled. A chance of 1 means always, and a chance of 100 means 1 time on 100 times./

    public void setStopChance(int chance){

    stopChance = chance;

    }



    /**Sets the speed on which the NPC should move.
    /

    public void setMoveSpeet(float speed){

    moveSpeed = speed;

    }



    /*Sets the mode used for changing the direction/

    public void setDirectionChangeMode(int mode){

    directionChange = mode;

    }



    /**The most important method of the NPC class. This has to be called every step, before you read the variables, to make it work correctely.*/

    public void update(){



    if(moveMode == MOVE_AT_RANDOM_INTERVALS){

    if(FastMath.nextRandomInt(0,startChance+1) == FastMath.ceil(startChance / 2) && !moving){

    moving = true;

    speed2 = moveSpeed;

    } else if(FastMath.nextRandomInt(0,stopChance+1) == FastMath.ceil(stopChance / 2) && moving){

    moving = false;

    speed2 = moveSpeed;

    }

    }



    if(moving || moveMode == MOVE_ALWAYS){ //only do stuff when something needs to be done

    //manage speed

    if (accelerationMode == ACCELERATE_SMOOTH){

    if(speed2 > speed){

    speed += accelerationFactor;

    }

    if(speed2 < speed){

    speed -= accelerationFactor;

    }

    } else{

    speed = speed2;

    }



    if(FastMath.nextRandomInt(0,directionChangeChance+1) == FastMath.ceil(directionChangeChance / 2) && directionChange == DIRECTION_CHANGE_AT_RANDOM_INTERVALS){

    directionChanging = !directionChanging; //change the direction at random intervals

    }



    //manage the changing of directions

    if(directionChange != DIRECTION_DONT_CHANGE && directionChanging){//only change the direction when it has to be changed

    if(directionAxis == AXIS_X || directionAxis == AXIS_XY || directionAxis == AXIS_XZ ||directionAxis == AXIS_XYZ){//x axis

    direction.x += ((FastMath.nextRandomFloat()*2) - 1) * directionChangeIntervals;

    }

    if(directionAxis == AXIS_Y || directionAxis == AXIS_XY || directionAxis == AXIS_YZ ||directionAxis == AXIS_XYZ){//y axis

    direction.y += ((FastMath.nextRandomFloat()*2) - 1) * directionChangeIntervals;

    }

    if(directionAxis == AXIS_Z || directionAxis == AXIS_XZ || directionAxis == AXIS_YZ ||directionAxis == AXIS_XYZ){//z axis

    direction.z += ((FastMath.nextRandomFloat()*2) - 1) * directionChangeIntervals;

    }

    }



    //update the position

    position.addLocal(direction);

    }



    }



    }[/java]Please post feedback and bug reports here.



    when you 'll use it in your project, credits would be nice, but aren’t nessicary.

    ENYOY!

Its not framerate independent, you would have to call update() in constant intervals to make this work, add a tpf variable to the update() call.

tanks for the tip. will this work?[java]public class NPC{

float tpfPrevious;

public void update(float tpf){

if(tpfPrevious == null){

tpfPrevious = tpf;

}

if(tpf != tpfPrevious){

//do the update code

}

tpfPrevious = tpf;

}

}[/java]

nope

normen said:
nope

lol

You should try making the class implement a controllable and extend as a node, u can then attach physics to it and make it a true npc :P
And then u'll have an update method with tpf that u can use in a math equation(as normen said) not like the boolean u did.. cus it wont return previous.. like ever ><

Instead of overriding Spatial, make it a Control. Spatials are visual things, Controls are logical things.

how should i make it a control? i am still a bit new when it comes to JME.

start with

[java]public Class MyControl extends Control{

}[/java]

normen said:
start with
[java]public Class MyControl extends Control{
}[/java]

isn't control an interface? maybe u meant AbstractControl.

No I meant implements instead of extends, you can also extend AbstractControl if you want yeah.

so i now have [java]public class NPC implements Control {[/java]but this isn’t all i should do, is it? is there a manual for programming your own controls?

Read a manual about implementing java interfaces. Just press the lightbulb and select “implement missing methods” and you will see.

yes, but what should be programmed in these methods:[java]public Control cloneForSpatial(Spatial spatial) {

//what should be here?

throw new UnsupportedOperationException(“Not supported yet.”);

}



public void setSpatial(Spatial spatial) {

//i think this should set the spatial on which this control will be applied

}



public void setEnabled(boolean enabled) {

//i think this sets weather JME should execute this control

}



public boolean isEnabled() {

//i think this is a method that returns weather this control is enabled

}



public void render(RenderManager rm, ViewPort vp) {

//i really don’t know what should be here, but i think it’s something for rendering.

throw new UnsupportedOperationException(“Not supported yet.”);

}



public void write(JmeExporter ex) throws IOException {

//i think this is for saving, but i don’t know how to get thi filename that should be used for this

throw new UnsupportedOperationException(“Not supported yet.”);

}



public void read(JmeImporter im) throws IOException {

//i think this is for loading, but again, i don’t know how to get the filename that should be used for this

throw new UnsupportedOperationException(“Not supported yet.”);

}[/java]With that manual, i didn’t ment a manual for implementing an other class, i actually ment a manual abouth how to use these metoods to create your own control.

you dont have to render anyting, just leave that method blank. read/write doesnt have to be there too if you dont want to save this in a j3o, otherwise you’re right

and what abouth that ‘cloneForSpatial(Spatial spatial)’ method?

also used for loading mainly, or if you want to be able to clone the control for another spatial… Just try it, it wont even throw the errors.

it did threw the errors:[patch]java.lang.UnsupportedOperationException: Not supported yet.

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:522)

at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:709)

at com.jme3.renderer.RenderManager.render(RenderManager.java:739)

at com.jme3.app.SimpleApplication.update(SimpleApplication.java:244)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:146)

at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:159)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:208)

at java.lang.Thread.run(Thread.java:662)

BUILD SUCCESSFUL (total time: 5 seconds)[/patch]it looks like i have to put something is the render method.

should i do this?[java] public void render(RenderManager rm, ViewPort vp) {

rm.renderScene(NPCspatial, vp);

}[/java]

EDIT: well, i tried it and it gave me a lot more errors:[patch]java.lang.StackOverflowError

at com.jme3.scene.Spatial.checkCulling(Spatial.java:228)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:508)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)

at mygame.NPC.render(NPC.java:328)

at com.jme3.scene.Spatial.runControlRender(Spatial.java:523)

at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:516)



[/patch]

^this was abouth 1/5 of the errors.

Note that it has printed the same error many times.

Well, the render method is called, so just remove the throwing of an error :roll:

IT WORKED! But the NPC always tries to run away :slight_smile:

i’ll update the code as soon as i got this tested well.

sorry that i haven’t said this before, but thank you for your great help, normen :wink: