Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

My scene has a main character who is a ninja and an enemy which is a goblin and one monster but the monster has no animation so we can wait with him: enter image description here

How can I make the goblin actually move instead of just walking (animating) on the spot? I asked in the jme3 forum but I could not understand the answer: http://jmonkeyengine.org/groups/graphics/forum/topic/making-a-computer-controlled-character-move My code is

package adventure;

import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Image;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.animation.LoopMode;
import com.jme3.app.SimpleApplication;
import com.jme3.asset.BlenderKey;
import com.jme3.asset.plugins.HttpZipLocator;
import com.jme3.asset.plugins.ZipLocator;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.objects.PhysicsCharacter;
import com.jme3.input.ChaseCamera;
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.material.MaterialList;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.ogre.OgreMeshKey;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.input.MouseInput;
public class Q3World extends SimpleApplication implements ActionListener,
        AnimEventListener, Playable {
    private Node gameLevel;
    private PhysicsCharacter player;
    private static boolean useHttp = false;
    private static World world;
    private static Person person;
    private static Player dplayer;
    private BulletAppState bulletAppState;
    private AnimChannel channel;
    private AnimControl control;
    // character
    CharacterControl character;
    CharacterControl goblincharacter;
    Node model;
    // temp vectors
    Vector3f walkDirection = new Vector3f();
    Spatial goblin;
    RigidBodyControl terrainPhysicsNode;

    // animation
    AnimChannel animationChannel;
    AnimChannel shootingChannel;
    AnimControl animationControl;
    float airTime = 0;
    // camera
    boolean left = false, right = false, up = false, down = false, attack=false;
    ChaseCamera chaseCam;

    FilterPostProcessor fpp;
    private Spatial sceneModel;

    private RigidBodyControl landscape;

    public static void main(String[] args) {

        File file = new File("quake3level.zip");
        if (!file.exists()) {
            useHttp = true;
        }
        Q3World app = new Q3World();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        bulletAppState = new BulletAppState();
        bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
        stateManager.attach(bulletAppState);
        setupKeys();
        DirectionalLight dl = new DirectionalLight();
        dl.setColor(ColorRGBA.White.clone().multLocal(2));
        dl.setDirection(new Vector3f(-1, -1, -1).normalize());
        rootNode.addLight(dl);
        AmbientLight am = new AmbientLight();
        am.setColor(ColorRGBA.White.mult(2));
        rootNode.addLight(am);

        if (useHttp) {
            assetManager
                    .registerLocator(
                            "http://jmonkeyengine.googlecode.com/files/quake3level.zip",
                            HttpZipLocator.class);
        } else {
            assetManager.registerLocator("quake3level.zip", ZipLocator.class);
        }

        // create the geometry and attach it
        MaterialList matList = (MaterialList) assetManager
                .loadAsset("Scene.material");
        OgreMeshKey key = new OgreMeshKey("main.meshxml", matList);
        gameLevel = (Node) assetManager.loadAsset(key);
        gameLevel.setLocalScale(0.1f);
        gameLevel.addControl(new RigidBodyControl(0));
        getPhysicsSpace().addAll(gameLevel);
        rootNode.attachChild(gameLevel);
        getPhysicsSpace().addAll(gameLevel);
        createCharacters();
        setupChaseCamera();
        setupAnimationController();
        setupFilter();
    }

    private void setupFilter() {
        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
        BloomFilter bloom = new BloomFilter(BloomFilter.GlowMode.Objects);
        fpp.addFilter(bloom);
        viewPort.addProcessor(fpp);
    }

    private PhysicsSpace getPhysicsSpace() {
        return bulletAppState.getPhysicsSpace();
    }

    private void setupKeys() {
        inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));
        inputManager.addListener(this, "wireframe");
        inputManager.addMapping("CharLeft", new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping("CharRight", new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping("CharUp", new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping("CharDown", new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping("CharSpace",
                new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager
                .addMapping("CharShoot",  new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
        inputManager.addListener(this, "CharLeft");
        inputManager.addListener(this, "CharRight");
        inputManager.addListener(this, "CharUp");
        inputManager.addListener(this, "CharDown");
        inputManager.addListener(this, "CharSpace");
        inputManager.addListener(this, "CharShoot");
    }

    private void createCharacters() {
        CapsuleCollisionShape capsule = new CapsuleCollisionShape(0.05f, 0.05f);
        character = new CharacterControl(capsule, 2);
        character.setJumpSpeed(20f);
        model = (Node) assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
        model.scale(0.05f, 0.05f, 0.05f);
        model.addControl(character);
        character.setPhysicsLocation(new Vector3f(55, 15, -60));
        model.setShadowMode(ShadowMode.CastAndReceive);
        character.setViewDirection(new Vector3f(1, 0, 0));
        rootNode.attachChild(model);
        getPhysicsSpace().add(character);
        BlenderKey blenderKey = new BlenderKey("Models/Oto/Oto.mesh.xml");
        Spatial man = (Spatial) assetManager.loadModel(blenderKey);
        man.setLocalTranslation(new Vector3f(67, 15, -60));
        man.setShadowMode(ShadowMode.CastAndReceive);
        rootNode.attachChild(man);

        goblin = assetManager.loadModel("objects/goblin.j3o");

        goblin.scale(4f, 4f, 4f);
        //goblin.setLocalTranslation(60, 3.5f, -60);
        goblincharacter = new CharacterControl(capsule, 2);
        goblin.addControl(goblincharacter);
        goblincharacter.setPhysicsLocation(new Vector3f(60, 3.5f, -60));
        //goblincharacter.setViewDirection(new Vector3f(1, 0, 0));
        //character.setWalkDirection(new Vector3f(1, 0, 0));



        control = goblin.getControl(AnimControl.class);
        control.addListener(this);
        channel = control.createChannel();

        for (String anim : control.getAnimationNames())
            System.out.println("goblin can:"+anim);
        channel.setAnim("walk");

        rootNode.attachChild(goblin);
        getPhysicsSpace().add(goblincharacter);
        Spatial monster = assetManager.loadModel("objects/creatures/monster/monster.packed.j3o");
        monster.scale(2f, 2f, 2f);
        monster.setLocalTranslation(new Vector3f(53, 3, -55));
        rootNode.attachChild(monster);

    }

    private void setupChaseCamera() {
        flyCam.setEnabled(false);
        chaseCam = new ChaseCamera(cam, model, inputManager);
        chaseCam.setDefaultDistance(27);



    }

    private void setupAnimationController() {
        animationControl = model.getControl(AnimControl.class);
        animationControl.addListener(this);
        animationChannel = animationControl.createChannel();
        //shootingChannel = animationControl.createChannel();

    }

    @Override
    public void simpleUpdate(float tpf) {

        Vector3f modelDirection = goblin.getLocalRotation().toRotationMatrix().getColumn(2);
        //goblin.setLocalTranslation(modelDirection.add(new Vector3f(1, 0, -0))); 

        Vector3f camDir = cam.getDirection().clone().multLocal(0.1f);
        Vector3f camLeft = cam.getLeft().clone().multLocal(0.1f);
        camDir.y = 0;
        camLeft.y = 0;
        walkDirection.set(0, 0, 0);
        if (left) {
            walkDirection.addLocal(camLeft);
        }
        if (right) {
            walkDirection.addLocal(camLeft.negate());
        }
        if (up) {
            walkDirection.addLocal(camDir);
        }
        if (down) {
            walkDirection.addLocal(camDir.negate());
        }
        //if (attack) {
            //animationChannel.setAnim("Attack1");
            //animationChannel.setLoopMode(LoopMode.DontLoop);
        //}
        if (!character.onGround()) {
            airTime = airTime + tpf;
        } else {
            airTime = 0;
        }
        if (walkDirection.length() == 0) {
            if (!"Idle1".equals(animationChannel.getAnimationName())) {
                animationChannel.setAnim("Idle1", 1f);
            }
        } else {            
            character.setViewDirection(walkDirection.negate());
            if (airTime > .3f) {
                if (!"stand".equals(animationChannel.getAnimationName())) {
                    animationChannel.setAnim("Idle1");
                }
            } else if (!"Walk".equals(animationChannel.getAnimationName())) {
                animationChannel.setAnim("Walk", 1f);
            }
        }
        character.setWalkDirection(walkDirection);
    }

    /*Ninja can: Walk
Ninja can: Kick
Ninja can: JumpNoHeight
Ninja can: Jump
Ninja can: Spin
Ninja can: Attack1
Ninja can: Idle1
Ninja can: Attack3
Ninja can: Idle2
Ninja can: Attack2
Ninja can: Idle3
Ninja can: Stealth
Ninja can: Death2
Ninja can: Death1
Ninja can: HighJump
Ninja can: SideKick
Ninja can: Backflip
Ninja can: Block
Ninja can: Climb
Ninja can: Crouch*/

    public void onAction(String binding, boolean value, float tpf) {
        if (binding.equals("CharLeft")) {
            if (value) {
                left = true;
            } else {
                left = false;
            }
        } else if (binding.equals("CharRight")) {
            if (value) {
                right = true;
            } else {
                right = false;
            }
        } else if (binding.equals("CharUp")) {
            if (value) {
                up = true;
            } else {
                up = false;
            }
        } else if (binding.equals("CharDown")) {
            if (value) {
                down = true;
            } else {
                down = false;
            }
        } else if (binding.equals("CharSpace")) {
            character.jump();
        } else if (binding.equals("CharShoot") && value) {
            //bulletControl();
            Vector3f origin    = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.0f);
            Vector3f direction = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.3f);
            //direction.subtractLocal(origin).normalizeLocal();
            //character.setWalkDirection(location);
            System.out.println("origin"+origin);
            System.out.println("direction"+direction);
            character.setViewDirection(direction);
            animationChannel.setAnim("Attack3");
            animationChannel.setLoopMode(LoopMode.DontLoop);
        }
    }

    public void onAnimCycleDone(AnimControl control, AnimChannel channel,
            String animName) {
        if (channel == shootingChannel) {
            channel.setAnim("Idle1");
        }
    }

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

    // Load an image from the net, making sure it has already been
    // loaded when the method returns
    public Image loadPicture(String imageName) {
        return null;
    }

    // Load and play a sound from /usr/local/hacks/sounds/
    public void playSound(String name) {
        URL u = null;
        try {
            u = new URL("file:" + "/usr/local/hacks/sounds/" + name + ".au");
        } catch (MalformedURLException e) {
        }
        AudioClip a = Applet.newAudioClip(u);
        a.play();
    }
}

I want the goblin to move around and then I want it to be able to fight the ninja. The monster should be able to kill but that can wait until later.

Update

I narrowed down the problem so that I only need the directions for the relative vectors in the goblin's direction now and forward is working and I don't understand why the others don't work, I'm getting unexpected behavior from the goblin in all directions except forward:

    goblinWalkDirection.set(0, 0, 0);

    if (goblinleft) {
        System.out.println("goblin left");
        goblinWalkDirection.addLocal(goblin.getLocalRotation()
                .mult(Vector3f.UNIT_X).multLocal(0.09f));
    }
    if (goblinright) {
        System.out.println("goblin right");
        goblinWalkDirection.addLocal(goblin.getLocalRotation()
                .mult(Vector3f.UNIT_X).multLocal(0.09f));
    }
    if (goblinup) {
        System.out.println("goblin forward");
        // move goblin forward
        goblinWalkDirection.addLocal(goblin.getLocalRotation()
                .mult(Vector3f.UNIT_Z).multLocal(0.09f));
    }
    if (goblindown) {
        System.out.println("goblin backwards");
        goblinWalkDirection.addLocal(goblin.getLocalRotation()
                .mult(Vector3f.UNIT_Z).multLocal(0.09f).negate());
    }
    if (goblinWalkDirection.length() == 0) {
        if (!"idleA".equals(animationChannel.getAnimationName())) {
            goblinChannel.setAnim("idleA", 1f);
        }
    } else {
        goblincharacter.setViewDirection(goblinWalkDirection);
        if (!"walk".equals(goblinChannel.getAnimationName())) {
            goblinChannel.setAnim("walk", 1f);
        }
    }
    goblincharacter.setWalkDirection(goblinWalkDirection);

Update 2

Now the goblin can walk around if I press the corresponding keys and the animations are also correct. I just think that the Vectors that set the directions are only correct in the orward case and that I need research for which vectors to put in the other cases for the other directions the goblin can take. But anyhow it is now basically possible to manage movement of the goblin which is a step farther than comments, my coding effort could control both the ninja and the goblin. enter image description here

    goblinWalkDirection.set(0, 0, 0);

    if (goblinleft) {
        System.out.println("goblin left");
        goblinWalkDirection.addLocal(goblin.getLocalRotation()
                .mult(Vector3f.UNIT_X).multLocal(0.09f));
    }
    if (goblinright) {
        System.out.println("goblin right");
        goblinWalkDirection.addLocal(goblin.getLocalRotation()
                .mult(Vector3f.UNIT_X).multLocal(0.09f));
    }
    if (goblinup) {
        System.out.println("goblin forward");
        // move goblin forward
        goblinWalkDirection.addLocal(goblin.getLocalRotation()
                .mult(Vector3f.UNIT_Z).multLocal(0.09f));
    }
    if (goblindown) {
        System.out.println("goblin backwards");
        goblinWalkDirection.addLocal(goblin.getLocalRotation()
                .mult(Vector3f.UNIT_Z).multLocal(0.09f).negate());
    }

    if (goblinWalkDirection.length() == 0) {
        if (!"idleA".equals(goblinChannel.getAnimationName())) {
            goblinChannel.setAnim("idleA", 1f);
        }
    } else {
        goblincharacter.setViewDirection(goblinWalkDirection);
        if (airTime > .3f) {
            if (!"stand".equals(goblinChannel.getAnimationName())) {
                goblinChannel.setAnim("Idle1");
            }
        } else if (!"walk".equals(goblinChannel.getAnimationName())) {
            goblinChannel.setAnim("walk", 1f);
        }
    }

Update 3

I've been rewriting and refactoring and I've upgraded the player control to the new class bettercharactercontrol and put it on codereview so that I can get help there. I still have an issue trying to make an NPC do something more intelligent than just looping.

share|improve this question
4  
Not sure why people aren't commenting. Your question is too broad. You post all your code and expect us to tell you what to do. They are asking you to read the tutorials. You need to slow down and read the tutorials. You're getting ahead of yourself. Read the tutorials then come back with specific implementation questions. –  Byte56 Aug 10 '12 at 14:29
2  
^^ What he said. The kind folks on that forum gave you links/suggestions to tutorials, please go through them carefully. –  Phill.Zitt Aug 10 '12 at 16:27
    
I was looking for this: goblincharacter.setWalkDirection(goblin.getLocalRotation().mult(Vector3f.UNIT_Z‌​).multLocal(0.4f)); which moves the goblin forward. I was not sure whether to use the spatial or the character object. Now I can move the gobline forawrd but I need to make a thread or something to stop him from infinitely going forward. I'm working on making the implementation more MVC since now it is exactly like a C program with no seperation of model, view and controller. I'm now researching how to make the goblin "move around" preferrably in a random way and make turns at walls,tricky. –  Niklas rtz Aug 11 '12 at 8:16
3  
You do not want to make a thread... I checked the forum answers you linked to, and they are absolutely right, as are other commenters. Start with the tutorials, I think you are getting ahead of yourself a little bit. –  Mikael Högström Aug 11 '12 at 14:57
    
I ran all the tutorials weeks ago and the answer is not in the tutorials as they say. They say it says in the tutorial how to computer control a character but does it really? No. –  Niklas rtz Aug 12 '12 at 18:15

1 Answer 1

up vote 2 down vote accepted

Nick, I think you shouldn't start from 'moving goblin right/left' but from understanding how the AI works. Just sit down, close your eyes and imagine the fight with the goblin - how would you like it to look like?

Then split his behavior into pieces - the same way how you do with your 3D code. What you want to do is to give him small brain and some states of mind. The goblin has sight and can hear, he can be looking for the enemy, attacking, fleeing, etc. And you should code all those situations. I know it's not a simple task but the AI is not simple at all.

And the most important - if you don't do this the correct way, from start to end but you try to find some shortcuts, like "make goblin dodge and I'm happy" you'll get into more troubles, just later (like with the holes in Alien head:)). When you later scale your game up, and create 'real' levels, you can find out that every goblin runs on you at the very beginning, or get many other bad situations. And then it wouldn't be so easy to solve them.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.