Installing a GhostControl

Hello community,

I’m a newbie in terms of the jMonkeyEngine and have some problems with my first tries.

In my FPS-game there is a simple map and some enemys (ninjas) are around.
While there is peace, the enemies “patrol” around in kind of a square.
That works fine.

Now I try to realize something like a “aggro radius”. When the player comes too close, the enemy will/should attack.
I’ve tried to use a GhostControl for that (in form of a big “CapsuleCollisionShape”) that is around the enemy and waits for a collision with the player. Then, the enemy will attack.
But - as I see in the debug mode - I’m not able to place this GhostControl so that it follows the enemy, even if I add the control to the “BulletAppState”. Because I have no real expierence with this engine I am not able to solve this problem myself :frowning:

I would be very thankful if you guys can give me some hints :wink:

My Code:

Class: Main.java
[java]
package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;

public class Main extends SimpleApplication implements ActionListener
{
private Spatial sceneModel;
private BulletAppState bulletAppState;
private RigidBodyControl landscape;
private CharacterControl player;
private Vector3f walkDirection = new Vector3f();
private boolean keyLeft = false, keyRight = false, keyUp = false, keyDown = false;
private Vector3f camDir = new Vector3f();
private Vector3f camLeft = new Vector3f();

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

public void simpleInitApp()
{
    bulletAppState = new BulletAppState();
    stateManager.attach(bulletAppState);
    bulletAppState.getPhysicsSpace().enableDebug(assetManager);

    initCam();
    initKeys();
    initLight();
    initScene();
    initPlayer();

    Ninja ninja = new Ninja(this, new Vector3f(30f, 3.5f, -50f));
    bulletAppState.getPhysicsSpace().add(ninja);

    Ninja ninjab = new Ninja(this, new Vector3f(30f, 3.5f, -50f));
    bulletAppState.getPhysicsSpace().add(ninjab);

    Ninja ninjac = new Ninja(this, new Vector3f(30f, 3.5f, -50f));
    bulletAppState.getPhysicsSpace().add(ninjac);

    Ninja ninjad = new Ninja(this, new Vector3f(30f, 3.5f, -50f));
    bulletAppState.getPhysicsSpace().add(ninjad);

    Ninja ninja2 = new Ninja(this, new Vector3f(120f, 3.5f, -130f));
    bulletAppState.getPhysicsSpace().add(ninja2);
}

private void initCam()
{
    flyCam.setMoveSpeed(100);
    flyCam.setZoomSpeed(0.0f);
}

private void initLight()
{
    AmbientLight al = new AmbientLight();
    al.setColor(ColorRGBA.White.mult(1.3f));
    rootNode.addLight(al);

    DirectionalLight dl = new DirectionalLight();
    dl.setColor(ColorRGBA.White);
    dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal());
    rootNode.addLight(dl);
}

private void initKeys()
{
    inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
    inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
    inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
    inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
    inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
    inputManager.addListener(this, "Left");
    inputManager.addListener(this, "Right");
    inputManager.addListener(this, "Up");
    inputManager.addListener(this, "Down");
    inputManager.addListener(this, "Jump");
}

public void initScene()
{
    viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));

    sceneModel = assetManager.loadModel("Scenes/Terrain.j3o");
    sceneModel.setLocalScale(2f);
    CollisionShape sceneShape = CollisionShapeFactory.createMeshShape((Node) sceneModel);
    landscape = new RigidBodyControl(sceneShape, 0);
    sceneModel.addControl(landscape);
    rootNode.attachChild(sceneModel);
    bulletAppState.getPhysicsSpace().add(landscape);
}

public void initPlayer()
{
    CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1f, 3f, 1);
    player = new CharacterControl(capsuleShape, 0.05f);
    player.setJumpSpeed(12);
    player.setFallSpeed(30);
    player.setGravity(30);
    player.setPhysicsLocation(new Vector3f(0, 10, 0));
    bulletAppState.getPhysicsSpace().add(player);

    player.warp(new Vector3f(0.0f, 20.0f, 0.0f));
}

public void onAction(String binding, boolean isPressed, float tpf)
{
    if (binding.equals("Left"))
    {
        keyLeft = isPressed;
    } else if (binding.equals("Right"))
    {
        keyRight = isPressed;
    } else if (binding.equals("Up"))
    {
        keyUp = isPressed;
    } else if (binding.equals("Down"))
    {
        keyDown = isPressed;
    } else if (binding.equals("Jump"))
    {
        if (isPressed)
        {
            player.jump();
        }
    }
}

@Override
public void simpleUpdate(float tpf)
{
    camDir.set(cam.getDirection()).multLocal(0.6f);
    camLeft.set(cam.getLeft()).multLocal(0.4f);
    walkDirection.set(0, 0, 0);
    if (keyLeft)
    {
        walkDirection.addLocal(camLeft);
    }
    if (keyRight)
    {
        walkDirection.addLocal(camLeft.negate());
    }
    if (keyUp)
    {
        walkDirection.addLocal(camDir);
    }
    if (keyDown)
    {
        walkDirection.addLocal(camDir.negate());
    }
    walkDirection.setY(0.0f);
    if (player.onGround())
    {
        player.setWalkDirection(walkDirection.mult(0.5f));
    }
    cam.setLocation(player.getPhysicsLocation());
}

}
[/java]

Class: Ninja.java
[java]
package mygame;

import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.LoopMode;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.control.GhostControl;
import com.jme3.math.Vector3f;
import java.util.Random;

public class Ninja extends NPC
{
private Main main;
private float patrolRadian = 20f;
private float walkSpeed = 3.5f;
private Vector3f centerPoint;
private Vector3f nextPatPoint;
private boolean isWaiting = true;
private float waitCount = 0;
private float waitDelay = 15;
private boolean isWalking = false;
private float walkTargetDistance = 0;
private float walkStartPointX, walkStartPointY, walkStartPointZ;

private GhostControl actionRadius;  //Test

public Ninja(Main mainArg, Vector3f centerPointArg)
{
    super(mainArg, "Models/Ninja/Ninja.mesh.xml", 1, 1f, 6f, 80);
    main = mainArg;
    setScale(new Vector3f(0.03f, 0.03f, 0.03f));
    centerPoint = centerPointArg;
    
    //Test***
    actionRadius = new GhostControl(new CapsuleCollisionShape(10, 5));
    addControl(actionRadius);
    //***Test
    
    setPhysicsLocation(centerPoint);
    idle();
}

@Override
public void update(float tpf)
{
    super.update(tpf);
    if (isWaiting)
    {
        waitCount += tpf;
        if (waitCount >= waitDelay)
        {
            waitCount = 0;
            isWaiting = false;
            patrol();
        }
    } else if (isWalking)
    {
        if (getWorldTranslation().subtract(new Vector3f(walkStartPointX, walkStartPointY, walkStartPointZ)).length() > walkTargetDistance)
        {
            isWalking = false;
            isWaiting = true;
            setWalkDirection(Vector3f.ZERO);
            idle();
        }
    }
}

public void walk()
{
    animate(0, "Walk", LoopMode.Cycle);
}

private void idle()
{
    switch (new Random().nextInt(3))
    {
        case 0:
            animate(0, "Idle1", LoopMode.Cycle);
            break;
        case 1:
            animate(0, "Idle2", LoopMode.Cycle);
            break;
        case 2:
            animate(0, "Idle3", LoopMode.Cycle);
            break;
    }
}

public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName)
{
}

public void onAnimChange(AnimControl control, AnimChannel channel, String animName)
{
}

private void patrol()
{
    Random random = new Random();
    float fac1 = 1f;
    float fac2 = 1f;

    if (random.nextBoolean())
    {
        fac1 = -1f;
    }
    if (random.nextBoolean())
    {
        fac2 = -1f;
    }

    this.nextPatPoint = centerPoint.add(new Vector3f(random.nextFloat() * patrolRadian * fac1, 0.0f, random.nextFloat() * patrolRadian * fac2)).setY(0);

    walkStartPointX = getWorldTranslation().getX();
    walkStartPointY = getWorldTranslation().getY();
    walkStartPointZ = getWorldTranslation().getZ();
    walkTargetDistance = nextPatPoint.subtract(getWorldTranslation()).length();
    isWalking = true;
    walk();
    setViewDirection((nextPatPoint).subtract(getWorldTranslation()).negate());
    setWalkDirection((nextPatPoint).subtract(getWorldTranslation()).normalizeLocal().mult(walkSpeed));
}

}
[/java]

Class NPC.java

[java]
package mygame;

import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.animation.LoopMode;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.control.GhostControl;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control;
import java.util.ArrayList;

public abstract class NPC extends BetterCharacterControl implements AnimEventListener
{
private Main main;
private Spatial node;
private AnimControl animControl;
private ArrayList<AnimChannel> animChannels = new ArrayList<AnimChannel>();

public NPC(Main mainArg, String modelPathArg, int numberAnimChannels, float radiusArg, float heightArg, float weightArg)
{
    super(radiusArg, heightArg, weightArg);
    main = mainArg;
    
    node = main.getAssetManager().loadModel(modelPathArg);
    node.addControl(this);

    animControl = node.getControl(AnimControl.class);
    animControl.addListener(this);

    for (int i = 0; i &lt; numberAnimChannels; i++)
    {
        animChannels.add(animControl.createChannel());
    }
    main.getRootNode().attachChild(node);
}

@Override
public void setSpatial(Spatial e)
{
    super.setSpatial(e);
    if (e != null)
    {
        node = e;
    }
}

protected void animate(int channelNumber, String move, LoopMode mode)
{
    animChannels.get(0).reset(true);
    animChannels.get(channelNumber).setAnim(move, 0.3f);
    animChannels.get(channelNumber).setLoopMode(mode);
}

public void setPosition(Vector3f newPos)
{
    node.setLocalTranslation(newPos);
}

public void setScale(Vector3f newScale)
{
    node.setLocalScale(newScale);
}

public void setRotation(Quaternion newRotation)
{
    node.setLocalRotation(newRotation);
}

public void move(Vector3f offset)
{
    node.move(offset);
}

public Vector3f getWorldTranslation()
{
    return node.getWorldTranslation();
}

public void lookAt(Vector3f lookPoint)
{
    node.lookAt(lookPoint, new Vector3f(0, 1, 0));
}

public void addControl(Control controlArg)
{
    node.addControl(controlArg);
}

}
[/java]

To run this, you will need to include the test-library (“jME3-testdata.jar”). Also, you will need a basic terrain/Scene (named “Terrain.j3o”).

The parts in the code marked with “Test”-comments are the “critical” parts.

I hope that here is somebody who wants to help me :wink:

Thanks!

stayawayknight

PS: I’m sorry for the (bad) language!