New to the jungle

I’m making this topic for jMonkey users, who are new to this egnine and need help, just like me.
I won’t ask anything before running through the documentation and forum topics, so I can avoid asking something that’s already have been told, and I hope my example will be followed XD

Hey all!

I am wokring on a third-peson RPG game. I’m making it for my bachelor’s degree thesis.

My question would be:
How do I add weapons into the hand of a chracter?
I can put a sword into the character’s hand in Blender, but I’d like if the character could switch between weapons.

I could make characters in Blender with every possibel weapon in the game, then load the right one when the user select a weapon, but this would be a very ugly solution.

Can anyone tell me a solution?

Normally it is solved by adding empty bone/node in your character model with specific name (for example Weapon_slot). Then, in jme3 after loading your model without any weapon, you locate the node with such name, which will be empty and add your weapon model which you have created separately.

Of course, when designing animations in blender, you will want to put some weapon in that slot (longest/biggest possible probably) to avoid funny animations, just remove it before exporting (or mark as non-exportable if it is possible in blender).

You will probably end up with more than one of such ‘virtual slots’ - for example NWN had extra ‘castout’ and ‘casthead’ slots (for starting spell effects from hands or head specifically). And obviously, you will need different animations for 1h weapon, versus 1h+shield, versus 2h versus 2x1h, versus unarmed etc…

I would seriously suggest reusing some existing resources - NWN or Oblivion/Skyrim models together with animations to bootstrap your game. If it is master thesis in 3d/game programming, you can probably end there - final product will be a lot nicer and you are not going to sell it anyway. Of course, it master thesis is about modelling/animating itself… then you cannot do that, but then you should use Unity or UnrealEngine, to avoid having to fight with core programming.

Don’t try to code 3d engine and model/animate at same time.

3 Likes

how much work have u done already?

abies, thank you for the fast answer! I’m not much for a 3D artist, I’m more of a programmer, so I think I’ll use models from other games, but I’ll ask my consulent if it’s okay.

eraslt, the game is still in the planning phase, apart from some sketches, there isn’t any concrete process yet.

Please speak with your teacher and try to convince him that attributed usage of these resources is ok. If he is against that on legal reasons, as a fallback you can use certain amount of community-created NWN content - but it will be a lot harder, as some of them are importing original models, so you would need to proof each single model before use. If he is against usage of ANY 3rd party models… then be sure if he is ok with using jme3 in first place… and java… and operating system written by somebody else… and CPU produced by somebody else…

4 Likes

that post made me smile :slight_smile:

1 Like

Hey all again:D

I have a model of a man with animated armature, and a model of a sword in my assets. I’d like to give the sword into the guy’s hand. I know how to make nodes from bones in the Scene exporer, but I don’t know how to reach that node from the code. Can anyone help me?:smiley:

Thanks in advance!

Look for something like skeletonControl.getAttachmentNode() ?

2 Likes

Heyho!

I got another problem in my game:D
When I kill a draugr(hostile NPC), it dies, but will keep moving in the direction it was moving before. For example I kill a draugr runing thorwards me, then the death animation plays, but the dead body will just keep sliding in a direction. If I kill it while it’s standing in one place, the body will stay there.

Here is the code that makes the draugr move from the DraugrNode class:
[java]
public void update(ThirdPersonPlayerNode player) {

    walk = true;
    

    float x = player.getWorldTranslation().getX() - this.getWorldTranslation().getX();
    float z = player.getWorldTranslation().getZ() - this.getWorldTranslation().getZ();
    
    
    if (walk && !dead) {
        walkDirection.set(0, 0, 0);
        Vector3f aim = player.getWorldTranslation();
        Vector3f dist = aim.subtract(this.model.getWorldTranslation());
        if (dist.length() > 1) {
            dist.normalizeLocal();
            lookRotation.lookAt(dist, Vector3f.UNIT_Y);
            this.model.setLocalRotation(lookRotation);
            walkDirection.addLocal(x, 0, z);
            
        } else {
            walk = false;
            attack = true;
        }
    }

    characterControl.setWalkDirection(walkDirection.normalize().multLocal(walkSpeed));
    
    
    handleAnimations();
}

[/java]

Here is the fucntion for dying from the DraugrNode class:
[java]
public void die(){
this.dead = true;
this.animChannel.setAnim(“Death”);
this.animChannel.setLoopMode(LoopMode.DontLoop);

}

[/java]

Here is the simpleUpdate method in the Main class, where I call the update of the draugr:
(count is the number of draugrs on the map, draugrs is a list with draugr nodes)
[java]
public void simpleUpdate(float tpf) {

    playerNode.update();

    for (int i = 1; i < count; i++) {
        if (!draugrs.get(i - 1).dead) {
            draugrs.get(i - 1).update(playerNode);
        }
        
        if (draugrs.get(i - 1).dead) {
            draugrs.get(i-1).detachChildNamed(Integer.toString(i));
        }
        
    }
}

[/java]

I know it look kinda messy and hard to udnerstand cuz I’m not that good at explaining, but I’d be very happy for any kind of help. I’m stuck at this for a half day now.
Thank you for the help in advance!

The animation doesn’t move the model, it just animates it. You have something else moving the model and you aren’t tying that movement to the animation.

In other words you also need to stop the movement when you switch the animation over.

1 Like

the CharacterControl stores the the last walk direction it was given, and uses that for proceeding ticks. You need to explicity set it to 0 (it only needs to be done once, but can be done each frame as well). Moving [java]walkDirection.set(0, 0, 0); [/java]outside your if block, looks like it would work

1 Like

Thank you very much!
With your advice I managed to make it work as it supposed to be.
I addad
[java]
characterControl.setWalkDirection(walkDirection.set(0, 0, 0));
[/java]
to my public void die() method, and now it works properly.

Thank you again!

Heyho guys!

I’m stuck again, and I’m running out of time:D

I use 2 maps in my game. When I load one then I detach the other from the physical space and the root node, and it works like that. I have some other stuff on both levels, but the same solution works for them.
The problem is that I have a method to make some enemies. With a for iteration it generates as many opponents as many I tell it, but I can’t seem to find a way to get rid of them when I go to the other map, or if I get rid of them, I can’t load them again when I go back to the first map.

The code for Main class got kinda huge, so I will copy here only the revelant parts:
[java]
public class Main extends SimpleApplication implements ActionListener {

private RigidBodyControl scene, wall;
private BulletAppState bulletAppState;
public List<Spatial> draugrModels = new ArrayList<Spatial>();
public List<DraugrNode> draugrs = new ArrayList<DraugrNode>();
public List<NonPlayer> draugrObjects = new ArrayList<NonPlayer>();
public int count;
public Node shotables = new Node("shotables");
public Sword sword;
public Weapon bareHand = new Weapon("Hand", "", 0);
public Armour bareHelmet = new Armour("Helmet", "", 0);
public Armour bareShield = new Armour("Shield", "", 0);
public Armour bareBodyA = new Armour("Body", "", 0);
public Armour bareLegA = new Armour("Leg", "", 0);
public Armour bareFootA = new Armour("Foot", "", 0);
public Armour bareArmA = new Armour("Arm", "", 0);
public Armour bareHandA = new Armour("Hand", "", 0);
//...

public static void main(String[] args) {
}

@Override
public void simpleInitApp() {
}

public void hpHud() {
}

public void hud() {
}

private void initKeys() {
}

public void makeEnemy(int count) {

    for (int i = 1; i < count; i++) {
        float x = (-30) + (int) (Math.random() * ((30 - (-30)) + 1));
        float z = (-30) + (int) (Math.random() * ((30 - (-30)) + 1));

        Box b = new Box(Vector3f.ZERO, 0.3f, 0.8f, 0.3f);
        Geometry geom = new Geometry(Integer.toString(i), b);
        geom.setMaterial(assetManager.loadMaterial("Materials/Transparent.j3m"));
        geom.setLocalTranslation(0, -0.7f, 0);



        draugrModels.add(assetManager.loadModel("Models/Draugr/draugr.mesh.j3o"));
        draugrObjects.add(new NonPlayer(true, "Draugr", "Type", 1, 6));
        draugrObjects.get(i - 1).handWeapon = bareHand;
        draugrObjects.get(i - 1).bodyA = bareBodyA;
        draugrObjects.get(i - 1).helmet = bareHelmet;
        draugrObjects.get(i - 1).shield = bareShield;
        draugrs.add(new DraugrNode(draugrModels.get(i - 1)));
        draugrs.get(i - 1).getCharacterControl().setPhysicsLocation(new Vector3f(x, 2f, z));
        Spatial swordModel = assetManager.loadModel("Models/Sword/Sword.mesh.j3o");
        sword = new Sword(swordModel);
        draugrs.get(i - 1).hand.attachChild(swordModel);
        draugrs.get(i - 1).attachChild(geom);

        shotables.attachChild(draugrs.get(i - 1));
        rootNode.attachChild(shotables);
        bulletAppState.getPhysicsSpace().add(draugrs.get(i - 1));
    }


}

//...


public void loadTest() {
    interactive.detachAllChildren();
    viewPort.setBackgroundColor(ColorRGBA.LightGray);
    sceneModel = assetManager.loadModel("Scenes/TestWalkScene.j3o");
    sceneModel.scale(2, 2, 2);
    CollisionShape sceneShape = CollisionShapeFactory.createMeshShape((Node) sceneModel);
    scene = new RigidBodyControl(sceneShape, 0);
    sceneModel.addControl(scene);
    rootNode.attachChild(sceneModel);
    bulletAppState.getPhysicsSpace().add(scene);
    toTown();

// makeEnemy(count);
}

public void loadTown() {
    interactive.detachAllChildren();
    viewPort.setBackgroundColor(ColorRGBA.LightGray);
    sceneModel = assetManager.loadModel("Scenes/TownScene.j3o");
    sceneModel.scale(2, 2, 2);
    CollisionShape sceneShape = CollisionShapeFactory.createMeshShape((Node) sceneModel);
    scene = new RigidBodyControl(sceneShape, 0);
    sceneModel.addControl(scene);
    rootNode.attachChild(sceneModel);
    bulletAppState.getPhysicsSpace().add(scene);
    loadShop();
    makeTownGuard();
    toTest();
}

//...


@Override
public void simpleUpdate(float tpf) {

//...

    if (draugrs.size() > 0) {
        for (int i = 1; i < count; i++) {
            if (!draugrs.get(i - 1).dead) {
                draugrs.get(i - 1).update(playerNode);
            }
            if (draugrs.get(i - 1).dead) {
                draugrs.get(i - 1).detachChildNamed(Integer.toString(i));
            }
            if (draugrs.get(i - 1).attack && !draugrs.get(i - 1).dead) {
                Vector3f aim = playerNode.getWorldTranslation();
                Vector3f dist = aim.subtract(draugrs.get(i - 1).getWorldTranslation());
                if (dist.length() < 1.1f && player.hitPoints > 0) {
                    player.calcDefensePoints();
                    draugrObjects.get(i - 1).calcAttackPoints();
                    int damage = draugrObjects.get(i - 1).attack - player.defense;
                    if (damage > 0) {
                        player.hitPoints -= damage;
                        if (player.hitPoints < 1) {
                            playerNode.die();
                        }
                    }
                    System.out.println("Player: " + player.hitPoints);
                }
            }
        }
    }

}

//...

public void onAction(String name, boolean keyPressed, float tpf) {

    //...

    if (name.equals("Use") && !keyPressed) {

        CollisionResults results = new CollisionResults();
        Ray ray = new Ray(cam.getLocation(), cam.getDirection());
        ray.setLimit(10f);

        interactive.collideWith(ray, results);
        System.out.println("----- Collisions? " + results.size() + "-----");
        for (int i = 0; i < results.size(); i++) {
            float dist = results.getCollision(i).getDistance();
            Vector3f pt = results.getCollision(i).getContactPoint();
            String hit = results.getCollision(i).getGeometry().getName();
            System.out.println("* Collision #" + i);
            System.out.println("  You shot " + hit + " at " + pt + ", " + dist + " wu away.");
        }
        if (results.size() > 0) {

//...

            if (results.getCollision(1).getGeometry().getName().equals("Town")) { //That's how I go to town(don't want enemies there)
                
                rootNode.detachChild(sceneModel);
                bulletAppState.getPhysicsSpace().remove(scene);
                loadTown();
            }
            if (results.getCollision(1).getGeometry().getName().equals("Test")) { //That's how I go to the "battlefield" (want eneimes there)
                bulletAppState.getPhysicsSpace().remove(wall);
                rootNode.detachChild(sceneModel);
                bulletAppState.getPhysicsSpace().remove(scene);
                loadTest();
            }

//...

        }
    }

//...

}

}
[/java]

It would make me really happy if anyone could help me out!

You want to store the same enemys? eg if i kill one switch to other switch back tis still dead? In that case I would :

  1. point to the Saveable interface in jme and just save the whole node of the leves including enemies ect.
  2. just use a node for each level, and deatach/attach when necessary, so actually always both levels exist but only one is visible and updated

Or just remove/cleanup
In that case just store all enemys in an arraylist and make a clean method that loops over and removes all from the scenegraph, (and when done clears itself)

1 Like

It’s working! Thank you a lot!:smiley:

Heyho guys!

I wanted to put low poly pine trees in my game. The idea was to place some planes in a star shape in Blender, then make it transparent, and then add the pine tree texture to it. In Blender, the pine tree looked as I espected it to look, but in jMonkeyEngine, the parts that should have been transparent, were black. Any idea how can I make it work? Or any alternatives for creating low poly trees?

Thank you in advance!

This sort of question comes up every couple weeks or so.

In order, things to look at:
-Transparent bucket on the spatials
-alphaDiscardThreshold on the materials

The issue is that even though the parts are transparent, they are being rendered to the depth buffer and preventing anything behind them from being rendered.

Heyho!

I’ve got some problem with animation. My goal is pretty simple. I want my zombies walk while they are alive, and die when they are not (Random phylosophical question: how alive a zombie can be?:D).
Anyways, when they die, they do stop walking, but they just refuse to fall on the ground as they are supposed to do.

Here is my piece of code:


private void handleAnimations() {
        if (attacking) {
            //waiting for attack animation to finish
        } else if (attack) {

            animChannel.setAnim(attackAnim, .3f);
            animChannel.setLoopMode(LoopMode.DontLoop);
            animChannel.setSpeed(6);
            attack = false;
            attacking = true;
        } else if (characterControl.onGround()) {
            if (walk && !dead) {
                if (!animChannel.getAnimationName().equals(walkAnim)) {
                    animChannel.setAnim(walkAnim, .3f);
                    animChannel.setLoopMode(LoopMode.Loop);
                    animChannel.setSpeed(5);
                }
            } else if (dead) {
                this.animChannel.setAnim(deathAnim);
                this.animChannel.setLoopMode(LoopMode.DontLoop);
                characterControl.setWalkDirection(walkDirection.set(0, 0, 0));
            }
        }
   }

The problem is, that the walk and attack animations stop, but death animation is not playing.
The String constant deathAnim is correct, I checked it, and when the zombies’ HP hits 0, the boolean field dead will be true. I checked in debugger mode, it realy goes into that else clause, but still the zombies will just stand, and won’t fall:/

Hm is that called each frame, or only on change?