AI Code Not Working

So my friend is good at math and we attempted to write up this AI NPC movement code to move the zombies around in my game (Including pathfinding and wandering). I worked out all of the code errors so it runs but it does not do anything yet. I looked at the Hello tutorials for the Update Loop and based what I did on that. I have done other research but I haven’t found anything very helpful to me. I put the following code in the Update Loop:

[java] //Move zombies
if (Math.sqrt(Math.pow((player.getPhysicsLocation().x - zombieNode.getChild(“Zombie1”).getLocalTranslation().x),2)+Math.pow((player.getPhysicsLocation().y -zombieNode.getChild(“Zombie1”).getLocalTranslation().y),2)+Math.pow((player.getPhysicsLocation().z-zombieNode.getChild(“Zombie1”).getLocalTranslation().z),2)) < 50) //distance formula (plug in whatever number for 50)
{
//Pathfind
double direction = (-(Math.toDegrees(Math.atan((player.getPhysicsLocation().z -zombieNode.getChild(“Zombie1”).getLocalTranslation().z)/(player.getPhysicsLocation().x-zombieNode.getChild(“Zombie1”).getLocalTranslation().x)))));
pathfindX += (int) (2 * Math.cos(Math.toRadians(direction)));
pathfindZ += 2 * Math.sin(Math.toRadians(direction));
zombieNode.getChild(“Zombie1”).move(pathfindX * tpf, 0, pathfindZ * tpf);
} else {
//Wander
int xLocation;
int zLocation;
xLocation = -512 + (int)(Math.random() * ((512 - -512) + 1));
zLocation = -512 + (int)(Math.random() * ((512 - -512) + 1));
for (double i = Math.random() * 500 + 100; i > 0; i -= 1)
{
int direction = (int)(-(Math.toDegrees(Math.atan((zLocation-zombieNode.getChild(“Zombie1”).getLocalTranslation().z)/(xLocation-zombieNode.getChild(“Zombie1”).getLocalTranslation().x))))+ Math.random() * 90 - 45);
wanderX += 2Math.cos(Math.toRadians(direction));
wanderZ += 2
Math.sin(Math.toRadians(direction));
zombieNode.getChild(“Zombie1”).move(wanderX * tpf, 0,wanderZ * tpf);
}
}[/java]

I would appreciate some help reviewing the code but first I’ll explain the way it is supposed to work.

First, the code gets the distance between the player and the zombie using x and z coordinates. If it is less than 50 wu away, it will pathfind. If the player is more than 50 wu away, it will wander. While pathfinding, the zombie gets the player’s position and moves towards it. While wandering, a random position is chosen and the zombie wanders towards it.

These are a few things that I think could be wrong: One, I may not be accessing the positions of the player and the zombie the correct way. Two, the equations themselves may not be working right. Three, I may not be moving the zombie in the correct way (.move).

If there is anything you could suggest adding to the code that would be wonderful.

My overall question (I forgot to write it in the main post) is is there anything that is easy to see that is obviously wrong? Would this code even work IF I had it written correctly?

The explanation is fine except for the part where it doesn’t say what it’s doing with the code above vs what it should do (roam towards the player).

So… what does the code above actually make the zombies do?

@madjack At the moment the code does absolutely nothing. I took exactly what is listed above from the simpleUpdate() loop in my code.

EDIT: I know the first If statement is being tested because if I’m in game and shoot the zombie (which in turn removes it from zombieNode), the game crashes and gives me a NullPointerException on the line the if statement is on. I believe this is because zombieNode.getChild(“Zombie1”) does not exist.

Probably not the best answer but, I wrote some code for the Ludum dare which seems to me to be a simplified way of doing what you call the ‘pathfinding’ (just moving the zombie toward the player). I have no idea if your code works, or even if this is a better solution, but I do know that the following code works, and it doesn’t take much hassling:

[java]
float distance = zombie.getLocalTranslation().distance(playerLocation) / 3; //<< this int can be changed to make the zombie move faster or slower
zombie.setLocalTranslation(zombie.getLocalTranslation().interpolate(playerLocation, tpf / distance));[/java]

You could also but in a random Vector instead of playerLocation for wandering

@javagame I tried that code and changed it to this:

[java]float distance = zombieNode.getChild(“Zombie1”).getLocalTranslation().distance(player.getPhysicsLocation()) / 3; //<< this int can be changed to make the zombie move faster or slower
zombieNode.getChild(“Zombie1”).setLocalTranslation(zombieNode.getChild(“Zombie1”).getLocalTranslation().interpolate(player.getPhysicsLocation(), tpf / distance));[/java]

That however did not work. I have no idea why. I also tried replacing the .setLocalTranslation() with .move() with no results

@javagame I created a new file like this:

[java]package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;

/** Sample 4 - how to trigger repeating actions from the main event loop.

  • In this example, you use the loop to make the player character

  • rotate continuously. */
    public class Main extends SimpleApplication {

    public static void main(String[] args){
    Main app = new Main();
    app.start();
    }

    protected Geometry box;
    private CharacterControl player;

    @Override
    public void simpleInitApp() {
    /** this blue box is our player character */
    Box b = new Box(.5f, .5f, .5f);
    box = new Geometry(“blue cube”, b);
    Material mat = new Material(assetManager,
    “Common/MatDefs/Misc/Unshaded.j3md”);
    mat.setColor(“Color”, ColorRGBA.Blue);
    box.setMaterial(mat);
    rootNode.attachChild(box);

     CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
     player = new CharacterControl(capsuleShape, 0.05f);
     player.setPhysicsLocation(new Vector3f(-10, 10, -10));
    

    }

    /* Use the main event loop to trigger repeating actions. */
    @Override
    public void simpleUpdate(float tpf) {
    float distance = rootNode.getChild(“blue cube”).getLocalTranslation().distance(player.getPhysicsLocation()) / 1; //<< this int can be changed to make the zombie move faster or slower
    rootNode.getChild(“blue cube”).setLocalTranslation(rootNode.getChild(“blue cube”).getLocalTranslation().interpolate(player.getPhysicsLocation(), tpf / distance));
    }
    }[/java]

The code you gave me worked in this file but the cube moved towards the upper left corner of my screen and away from my player. I messed around with the code a little bit as well as looked at the documentation but I still couldn’t figure anything out. There is one part that I still do not fully understand and that is .interpolate(). Anyways, the cube is moving towards the upperleft corner of my screen and away from my player and I do not know why.

Your player is in the upper left corner, you need to set it up so that it can move around as in the tutorials and set your cam to the player’s location. Instead, if you don’t need a player and just want to test with a cam then just use cam.getLocation() rather than the player’s location.

@Connor14 said: There is one part that I still do not fully understand and that is .interpolate().

Interpolate is finding the point in between two points, whereas distance is finding the distance between them. This would be more like half of that distance, in a way.

For example, in the following image, the blue dot is in between the two red dots, if you interpolate the red dots you get the blue point

By the way, I’m not saying all of the complex maths you originally had is bad, it may be better for you if you want to learn how to use it, if it suits your project better, or if you just like math. However, Vector3f already has preset methods like distance() and interpolate() that does it all for you, so in most cases its just better and easier to use them because to be honest if there’s a bug in your code its probably in those long formulas, so no need to re-invent the wheel.

Also, once you have it working you may also want to do use the .lookAt method on the zombie spatial, set it to look at the player’s location, as otherwise once you have your finished textured zombie models running around they’re going to be moving toward you but looking in a completely different direction all the time.

@javagame I changed my code in the way that you suggested and it worked for the blue cube example. I incorporated it into my zombie game but it doesn’t work. I think it might have something to do with the fact that I am using a height map with uneven terrain and that the cube is a solid shape (my game has physics) but I am not sure if that really matters.

@Connor14 said: I incorporated it into my zombie game but it doesn't work. I think it might have something to do with the fact that I am using a height map with uneven terrain and that the cube is a solid shape

Hmm then it may be the case that your original code works! Are the zomblez themselves physics objects? Maybe only attach the zombies to the physics space if they are within, say 5 wu, and if not then detatch them? I dunno, I thought most games didn’t actually have collision for their zombies, except with the player, and they avoided walking into buildings and other stuff because it was incorportated into the AI… Or, maybe they do use capsule shape like players, yes this would be easier and would seem more realistic… I dunno I guess it’s up to you to create your own solution, I’ve never gone into much more depth than this with AI so I’m probably not too much good here.

@javagame I got the code working for 1 zombie. Here is what I did:

[java]float distance2 = zombiePHY.getPhysicsLocation().distance(cam.getLocation()) / 10;
zombiePHY.setPhysicsLocation(zombiePHY.getPhysicsLocation().interpolate(cam.getLocation(), tpf / distance2));[/java]

It moves the entire physical object to the player. However, if I spawn more than 1 zombie, only one of them moves. Is there a way to move all of the zombies at the same time using the lines above?

Loop over all zombies.
Move each zombie.

…there is no other way.

@javagame said: Hmm then it may be the case that your original code works! Are the zomblez themselves physics objects? Maybe only attach the zombies to the physics space if they are within, say 5 wu, and if not then detatch them? I dunno, I thought most games didn't actually have collision for their zombies, except with the player, and they avoided walking into buildings and other stuff because it was incorportated into the AI... Or, maybe they do use capsule shape like players, yes this would be easier and would seem more realistic... I dunno I guess it's up to you to create your own solution, I've never gone into much more depth than this with AI so I'm probably not too much good here.

Depends, for most mmo’s you are right,
however many shooter or general purpose engines make no distinction fro an character from where it is steered.

@pspeed said: Loop over all zombies. Move each zombie.

…there is no other way.

I understand what you are saying though I’m not sure how to do it. I have a loop set up to create any number of zombie geometries (right now the zombies are cubes for testing purposes) each with a different name - Zombie1, Zombie2, Zombie3, etc. Is there a way to get the physics of a particular child in order to move it?

I know I can call zombieNode.getChild(“Zombie1”); to get and set things based on that geometry but as far as I know, there is no way to get and set the physics for that particular child.

What are you naming them in your first loop?

Either put the zombie spatials in an array or loop through them as this

node.getChild(“zombie” + String.valueOf(i)).setLocation();
Where i is index, or whatever its called, of the loop

@javagame said: What are you naming them in your first loop?

Either put the zombie spatials in an array or loop through them as this

node.getChild(“zombie” + String.valueOf(i)).setLocation();
Where i is index, or whatever its called, of the loop

This is the simple loop that I am using.

[java]public void spawnZombies() {

  int MinX = -512;
  int MaxX = 512;
  int randomX;

  int MinY = 0;
  int MaxY = 100;
  int randomY;

  int MinZ = -512;
  int MaxZ = 512;
  int randomZ;

  numberZombies = 1; 

  while(numberZombies &lt;= 2) {
      randomX = MinX + (int)(Math.random() * ((MaxX - MinX) + 1));
      randomY = MinY + (int)(Math.random() * ((MaxY - MinY) + 1));
      randomZ = MinZ + (int)(Math.random() * ((MaxZ - MinZ) + 1));

      String zombieName = "Zombie" + numberZombies;
      Vector3f vt = new Vector3f(randomX, randomY, randomZ);
      makeZombie(zombieName, vt);

      System.out.println(zombieName + " Created");
      
      zombieNode.getChild(zombieName).setUserData(zombieName + "Health", 2);
      
      //Used only for testing ===
      int health = zombieNode.getChild(zombieName).getUserData(zombieName + "Health");
      System.out.println("Health: " + health);
      //=========================
      
      numberZombies = numberZombies + 1;

  }
}[/java]

And this is the makeZombie method

[java]public void makeZombie(String name, Vector3f loc) {
zombieGEO = new Geometry(name, zombie);
zombieMat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
zombieMat.setColor(“Color”, ColorRGBA.randomColor());
zombieGEO.setMaterial(zombieMat);

  zombieGEO.setLocalTranslation(loc);
  zombiePHY = new RigidBodyControl(2f);
  zombieGEO.addControl(zombiePHY);
  bulletAppState.getPhysicsSpace().add(zombiePHY);
  
  zombieNode.attachChild(zombieGEO);
  rootNode.attachChild(zombieNode);
}[/java]

These are methods I use to spawn the zombie cubes. I need to get the physics of a specific cube so I can move it.

@Connor14

There are a few fundamental problems straight off, first of all, while loops are generally used when you don’t know how long you are going to have to repeat for, for example

[java]while(alive){
//do stuff
}[/java]

For what you’re doing we normally use for loops,
[java]for(int i = 0;i <= 2; i++){
//do stuff
}[/java]

Why? Well, you can set up a for loop using a while loops as you have, but its generally considered better coding standard, you don’t have to create the numberZombies variable outside of the loop which is ever present in that method, and you don’t have to increment it, the for loop does it for you. The ‘i’ variable i used on that example is cleaned up automatically when the loops over. Not much difference, I know, I always used to use while loops too but its just better practice to use for. I know you didn’t come here for a coding lesson so I’ll try to hurry up.

numberZombies = numberZombies + 1;

There are two much faster ways of doing this which save time, first off:
[java]numberZombies++;[/java] would just add 1 to the variable, its just the same thing only faster to type, or, if ever you want to add any number:
[java]numberZombies += 1;[/java] or
[java]numberZombies += 2;[/java] or
[java]numberZombies += 42;[/java] or
[java]numberZombies *= 2;[/java] etc, you see my point? It saves a surprising amount of time, when I go back to languages that don’t have this and I have to type the whole thing out like you do, I get grouchy.

Moving on, the rest of it looks, not too bad at a glance I guess you’re going to put the Minx, MinY and MinZ values into the vt eventually? At first you might wonder why its better to code like all other coders and not make up your own style, but if you carry on with coding you will probs end up having to work with other people, of course everyone has different styles when it goes deeper, but they might start to get annoyed at you for coding strange :slight_smile: So yeh, just loop through every zombie in the update loop and move its position. You don’t need zombiePHY to move the zombie, the physics object is added as a control to zombieGEO, so just get the spatial from zombie node as you were and call setLocalTranslation() on it. Anything else, come back here of course :slight_smile:

EDIT: Sorry you were right about the physics, it doesn’t work they way I thought

@javagame Thank you for the input. I do know about for loops but I’ve gotten myself into the habit of using while loops because I tend to forget the order for the 3 arguments.

Anyways, I have tried using .setLocalTranslation() before and it did not work so I used the physics method instead. I will try using .setLocalTranslation() again to make sure.

EDIT: As I thought, the .setLocalTranslation() did not work. I think it is because the cube / zombie has physics. Here is the code:

[java]float distance2 = zombieNode.getChild(“Zombie1”).getLocalTranslation().distance(cam.getLocation()) / 10;
zombieNode.getChild(“Zombie1”).setLocalTranslation(zombieNode.getLocalTranslation().interpolate(cam.getLocation(), tpf / distance2));[/java]

@Connor14 said: @javagame Thank you for the input. I do know about for loops but I've gotten myself into the habit of using while loops because I tend to forget the order for the 3 arguments.

Anyways, I have tried using .setLocalTranslation() before and it did not work so I used the physics method instead. I will try using .setLocalTranslation() again to make sure.

EDIT: As I thought, the .setLocalTranslation() did not work. I think it is because the cube / zombie has physics. Here is the code:

[java]float distance2 = zombieNode.getChild(“Zombie1”).getLocalTranslation().distance(cam.getLocation()) / 10;
zombieNode.getChild(“Zombie1”).setLocalTranslation(zombieNode.getLocalTranslation().interpolate(cam.getLocation(), tpf / distance2));[/java]

Read your code again, you are using the node’s location instead of the Zombie’s,

[java]zombieNode.getChild(“Zombie1”).setLocalTranslation(zombieNode.getLocalTranslation().interpolate(cam.getLocation(), tpf / distance2)); [/java]

should be

[java]zombieNode.getChild(“Zombie1”).setLocalTranslation(zombieNode.getChild(“Zombie1”).getLocalTranslation().interpolate(cam.getLocation(), tpf / distance2)); [/java]