6
\$\begingroup\$

You can download the Dangerworld game in pre-alpha version and review the code. The particular issues I have are

  1. making the character walk over steps instead of sliding
  2. NPC AI how to make the enemy walk along a wall

package adventure;

import com.jme3.system.AppSettings;
import java.io.File;

import com.jme3.renderer.queue.RenderQueue;
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.BetterCharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
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.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 PyramidLevel extends SimpleApplication implements ActionListener,
        AnimEventListener {
    private Node gameLevel;
    private static boolean useHttp = false;
    private BulletAppState bulletAppState;
    private AnimChannel channel;
    private AnimControl control;
    // character
    private BetterCharacterControl goblinControl;
    private BetterCharacterControl ninjaControl;
    private BetterCharacterControl jamieControl;
    private BetterCharacterControl monsterControl;
    private Node ninjaNode;
    boolean rotate = false;
    private Vector3f walkDirection = new Vector3f(0, 0, 0);
    private Vector3f viewDirection = new Vector3f(1, 0, 0);
    private boolean leftStrafe = false, rightStrafe = false, forward = false,
            backward = false, leftRotate = false, rightRotate = false;
    private Node goblinNode;
    private Node jamieNode;
    private Node monsterNode;
    Spatial goblin;
    RigidBodyControl terrainPhysicsNode;

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

    ChaseCamera chaseCam;
    private boolean walkMode = true;
    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;
        }
        PyramidLevel app = new PyramidLevel();
        AppSettings settings = new AppSettings(true);
        settings.setTitle("Dungeon World");
        settings.setSettingsDialogImage("Interface/splash.png");
        app.setSettings(settings);
        app.start();
    }

    @Override
    public void simpleInitApp() {
        this.setDisplayStatView(false);
        bulletAppState = new BulletAppState();
        bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
        stateManager.attach(bulletAppState);
        bulletAppState.setDebugEnabled(false);
        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();
        setupAnimationController();
        setupChaseCamera();
        setupFilter();
    }
    private void createCharacters() {

        createNinja();
        createGoblin();
        createJamie();
        createOto();
        createMonster();

    }
    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("attack", new MouseButtonTrigger(
                MouseInput.BUTTON_LEFT));

        inputManager.addMapping("attack2", new MouseButtonTrigger(
                MouseInput.BUTTON_RIGHT));

        inputManager.addListener(this, "CharLeft");
        inputManager.addListener(this, "CharRight");
        inputManager.addListener(this, "CharUp");
        inputManager.addListener(this, "CharDown");
        inputManager.addListener(this, "CharSpace");
        inputManager.addListener(this, "attack");
        inputManager.addListener(this, "attack2");
    }

    private void createNinja() {
        ninjaNode = (Node) assetManager
                .loadModel("Models/Ninja/Ninja.mesh.xml");
        ninjaNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        ninjaNode.setLocalScale(0.06f);
        ninjaNode.setLocalTranslation(new Vector3f(55, 3.3f, -60));
        ninjaControl = new BetterCharacterControl(1.7f, 4f, 0.5f);//(2, 4, 0.5f);
        ninjaControl.setJumpForce(new Vector3f(6, 6, 6));
        ninjaNode.addControl(ninjaControl);
        rootNode.attachChild(ninjaNode);
        bulletAppState.getPhysicsSpace().add(ninjaControl);
        getPhysicsSpace().add(ninjaControl);
        animationControl = ninjaNode.getControl(AnimControl.class);
        animationChannel = animationControl.createChannel();
    }

    private void createGoblin() {
        goblinNode = (Node) assetManager.loadModel("objects/goblin.j3o");
        goblinNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        goblinNode.setLocalScale(4f);
        goblinNode.setLocalTranslation(new Vector3f(51.5f, 3.3f, -60));
        goblinControl = new BetterCharacterControl(2, 4, 0.5f);
        goblinControl.setJumpForce(new Vector3f(6, 6, 6));

        goblinNode.addControl(goblinControl);
        rootNode.attachChild(goblinNode);
        bulletAppState.getPhysicsSpace().add(goblinControl);
        getPhysicsSpace().add(goblinControl);
        animationControl = goblinNode.getControl(AnimControl.class);
        animationChannel = animationControl.createChannel();

        for (String anim : animationControl.getAnimationNames())
            System.out.println("goblin can:" + anim);

        animationChannel.setAnim("idleA");

    }

    private void createJamie() {
        jamieNode = (Node) assetManager.loadModel("Models/Jaime/Jaime.j3o");
        jamieNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        jamieNode.setLocalScale(5f);
        jamieNode.setLocalTranslation(new Vector3f(48f, 3f, -55));
        jamieControl = new BetterCharacterControl(2, 4, 0.5f);
        jamieControl.setJumpForce(new Vector3f(6, 6, 6));

        jamieNode.addControl(jamieControl);
        rootNode.attachChild(jamieNode);
        bulletAppState.getPhysicsSpace().add(jamieControl);
        getPhysicsSpace().add(jamieControl);
        animationControl = jamieNode.getControl(AnimControl.class);
        animationChannel = animationControl.createChannel();

        for (String anim : animationControl.getAnimationNames())
            System.out.println("jamie can:" + anim);

        animationChannel.setAnim("Idle");

    }

    private void createMonster() {
        monsterNode = (Node) assetManager
                .loadModel("objects/creatures/monster/monster.packed.j3o");
        monsterNode.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        monsterNode.setLocalScale(2f);
        monsterNode.setLocalTranslation(new Vector3f(53f, 3f, -55));
        monsterControl = new BetterCharacterControl(2, 4, 0.5f);
        monsterControl.setJumpForce(new Vector3f(6, 6, 6));

        monsterNode.addControl(monsterControl);
        rootNode.attachChild(monsterNode);
        bulletAppState.getPhysicsSpace().add(monsterControl);
        getPhysicsSpace().add(monsterControl);
        // animationControl = monsterNode.getControl(AnimControl.class);
        // animationChannel = animationControl.createChannel();

        // for (String anim : animationControl.getAnimationNames())
        // System.out.println("goblin can:"+anim);
        // animationChannel.setAnim("walk");

    }

    private void createOto() {
        BlenderKey blenderKey = new BlenderKey("Models/Oto/Oto.mesh.xml");
        Spatial man = (Spatial) assetManager.loadModel(blenderKey);
        man.setLocalTranslation(new Vector3f(69, 15, -60));
        man.setShadowMode(ShadowMode.CastAndReceive);
        rootNode.attachChild(man);

    }



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

    }

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

    }

    @Override
    public void simpleUpdate(float tpf) {
        // goblinControl.setWalkDirection(goblin.getLocalRotation()
        // .mult(Vector3f.UNIT_Z).multLocal(0.4f));
        Vector3f camDir = cam.getDirection().clone().multLocal(8f);
        Vector3f camLeft = cam.getLeft().clone().multLocal(8f);
        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 (!ninjaControl.isOnGround()) {
            airTime = airTime + tpf;
        } else {
            airTime = 0;
        }
        if (walkDirection.length() == 0) {
            if (!"Idle1".equals(animationChannel.getAnimationName())) {
                animationChannel.setAnim("Idle1", 1f);
            }
        } else {
            ninjaControl.setViewDirection(walkDirection.negate());
            if (airTime > .3f) {
                if (!"stand".equals(animationChannel.getAnimationName())) {
                    animationChannel.setAnim("Idle1");
                }
            } else if (!"Walk".equals(animationChannel.getAnimationName())) {
                animationChannel.setAnim("Walk", 1f);
            }
        }
        ninjaControl.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();
            ninjaControl.jump();
        } else if (binding.equals("attack") && value) {
            // bulletControl();
            Vector3f origin = cam.getWorldCoordinates(
                    inputManager.getCursorPosition(), 0.0f);
            Vector3f direction = cam.getWorldCoordinates(
                    inputManager.getCursorPosition(), 0.0f);
            // 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);
        } else if (binding.equals("attack2-") && value) {
            // bulletControl();
            Vector3f origin2 = cam.getWorldCoordinates(
                    inputManager.getCursorPosition(), 0.0f);
            Vector3f direction2 = cam.getWorldCoordinates(
                    inputManager.getCursorPosition(), 0.0f);
            // direction.subtractLocal(origin).normalizeLocal();
            // character.setWalkDirection(location);
            System.out.println("origin" + origin2);
            System.out.println("direction" + direction2);
            // character.setViewDirection(direction);
            animationChannel.setAnim("SideKick");
            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) {
    }

    public Node getGameLevel() {
        return gameLevel;
    }

    public void setGameLevel(Node gameLevel) {
        this.gameLevel = gameLevel;
    }

    public static boolean isUseHttp() {
        return useHttp;
    }

    public static void setUseHttp(boolean useHttp) {
        PyramidLevel.useHttp = useHttp;
    }

    public BulletAppState getBulletAppState() {
        return bulletAppState;
    }

    public void setBulletAppState(BulletAppState bulletAppState) {
        this.bulletAppState = bulletAppState;
    }

    public AnimChannel getChannel() {
        return channel;
    }

    public void setChannel(AnimChannel channel) {
        this.channel = channel;
    }

    public AnimControl getControl() {
        return control;
    }

    public void setControl(AnimControl control) {
        this.control = control;
    }

    public BetterCharacterControl getGoblincharacter() {
        return goblinControl;
    }

    public void setGoblincharacter(BetterCharacterControl goblincharacter) {
        this.goblinControl = goblincharacter;
    }

    public BetterCharacterControl getCharacterControl() {
        return ninjaControl;
    }

    public void setCharacterControl(BetterCharacterControl characterControl) {
        this.ninjaControl = characterControl;
    }

    public Node getCharacterNode() {
        return ninjaNode;
    }

    public void setCharacterNode(Node characterNode) {
        this.ninjaNode = characterNode;
    }

    public boolean isRotate() {
        return rotate;
    }

    public void setRotate(boolean rotate) {
        this.rotate = rotate;
    }

    public Vector3f getWalkDirection() {
        return walkDirection;
    }

    public void setWalkDirection(Vector3f walkDirection) {
        this.walkDirection = walkDirection;
    }

    public Vector3f getViewDirection() {
        return viewDirection;
    }

    public void setViewDirection(Vector3f viewDirection) {
        this.viewDirection = viewDirection;
    }

    public boolean isLeftStrafe() {
        return leftStrafe;
    }

    public void setLeftStrafe(boolean leftStrafe) {
        this.leftStrafe = leftStrafe;
    }

    public boolean isRightStrafe() {
        return rightStrafe;
    }

    public void setRightStrafe(boolean rightStrafe) {
        this.rightStrafe = rightStrafe;
    }

    public boolean isForward() {
        return forward;
    }

    public void setForward(boolean forward) {
        this.forward = forward;
    }

    public boolean isBackward() {
        return backward;
    }

    public void setBackward(boolean backward) {
        this.backward = backward;
    }

    public boolean isLeftRotate() {
        return leftRotate;
    }

    public void setLeftRotate(boolean leftRotate) {
        this.leftRotate = leftRotate;
    }

    public boolean isRightRotate() {
        return rightRotate;
    }

    public void setRightRotate(boolean rightRotate) {
        this.rightRotate = rightRotate;
    }

    public Node getModel() {
        return goblinNode;
    }

    public void setModel(Node model) {
        this.goblinNode = model;
    }

    public Spatial getGoblin() {
        return goblin;
    }

    public void setGoblin(Spatial goblin) {
        this.goblin = goblin;
    }

    public RigidBodyControl getTerrainPhysicsNode() {
        return terrainPhysicsNode;
    }

    public void setTerrainPhysicsNode(RigidBodyControl terrainPhysicsNode) {
        this.terrainPhysicsNode = terrainPhysicsNode;
    }

    public AnimChannel getAnimationChannel() {
        return animationChannel;
    }

    public void setAnimationChannel(AnimChannel animationChannel) {
        this.animationChannel = animationChannel;
    }

    public AnimChannel getShootingChannel() {
        return shootingChannel;
    }

    public void setShootingChannel(AnimChannel shootingChannel) {
        this.shootingChannel = shootingChannel;
    }

    public AnimControl getAnimationControl() {
        return animationControl;
    }

    public void setAnimationControl(AnimControl animationControl) {
        this.animationControl = animationControl;
    }

    public float getAirTime() {
        return airTime;
    }

    public void setAirTime(float airTime) {
        this.airTime = airTime;
    }

    public boolean isLeft() {
        return left;
    }

    public void setLeft(boolean left) {
        this.left = left;
    }

    public boolean isRight() {
        return right;
    }

    public void setRight(boolean right) {
        this.right = right;
    }

    public boolean isUp() {
        return up;
    }

    public void setUp(boolean up) {
        this.up = up;
    }

    public boolean isDown() {
        return down;
    }

    public void setDown(boolean down) {
        this.down = down;
    }

    public boolean isAttack() {
        return attack;
    }

    public void setAttack(boolean attack) {
        this.attack = attack;
    }

    public ChaseCamera getChaseCam() {
        return chaseCam;
    }

    public void setChaseCam(ChaseCamera chaseCam) {
        this.chaseCam = chaseCam;
    }

    public boolean isWalkMode() {
        return walkMode;
    }

    public void setWalkMode(boolean walkMode) {
        this.walkMode = walkMode;
    }

    public FilterPostProcessor getFpp() {
        return fpp;
    }

    public void setFpp(FilterPostProcessor fpp) {
        this.fpp = fpp;
    }

    public Spatial getSceneModel() {
        return sceneModel;
    }

    public void setSceneModel(Spatial sceneModel) {
        this.sceneModel = sceneModel;
    }

    public RigidBodyControl getLandscape() {
        return landscape;
    }

    public void setLandscape(RigidBodyControl landscape) {
        this.landscape = landscape;
    }

}
\$\endgroup\$
  • 5
    \$\begingroup\$ Is that all one huge god class? \$\endgroup\$ – Hovercraft Full Of Eels Jun 2 '13 at 1:01
  • \$\begingroup\$ @HovercraftFullOfEels Yes, this is the level class it's just one class for one level. The API already has classes for charactercontrol and so, so I've done classes for personalities e.g. "GoblinPersonality" to distinguish them from the character and the control which are already defined in the API. It can be good to modularize the project more into cohesive classes and smaller unit, you're probably right if that's what you're thinking that my project can benefit from modularizing more into classes so I could have a class that is SceneCreator.class which creates the game scene. \$\endgroup\$ – Niklas Rosencrantz Jun 2 '13 at 5:16
11
\$\begingroup\$

Overall, your code looks quite good. There is something however which you can simplify a lot.

Consider these two code-parts:

if (!ninjaControl.isOnGround()) {
    airTime = airTime + tpf;
} else {
    airTime = 0;
}

if (binding.equals("CharLeft")) {
    if (value) {
        left = true;
    } else {
        left = false;
    }
} else if (binding.equals("CharRight")) {
    ...

All this follows the pattern

if (condition) { value = a; } else { value = b; }

which can be simplified using the Ternary operator to

value = (condition ? a : b);

Using this operator, we can write:

airTime = (!ninjaControl.isOnGround() ? airTime + tpf : 0);

if (binding.equals("CharLeft")) {
    left = (value ? true : false);
} else if (binding.equals("CharRight")) {
    ...

However, since we set left to true if value is true and to false if value is false, we are essentially setting it to the same value as value. So this series of if-else statements can be shortened to:

if (binding.equals("CharLeft")) {
    left = value;
} else if (binding.equals("CharRight")) {
    right = value;
} else if (binding.equals("CharUp")) {
    up = value;
} else if (binding.equals("CharDown")) {
    down = value;
}

Besides this, my only suggestion would be to split up methods when they become too long, there are some of your methods that are on the edge of what I would call "too long" so this can be something to think about.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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