If anybody could show me some of their code regarding moving a character with the keyboard (without adding force, preferably…) I would really appreciate it. I'm looking for WASD (and QE) movement through the world without ever having the object fall over. The reason I want this node to have physics applied to it is so that collision detection is easy and I can jump by simply applying a force directly upwards.
Thanks!
Okay so I've done a lot of experimentation and I found out the reason that this doesn't work is because if you try to make the character move like you normally would (using vector multiplication) you'll face problems because the characters rotation seems to always be changing.
Is there any way to avoid this? This is really a huge problem that's keeping my game from moving on to being a better thing…
Maybe node.clearForces() helps in some cases if you need to stop all movement or something.
You could post a simple example of what you have now and what problems there are.
Wow I figured out what it was. The center of gravity was really low, causing it to spin and spin and spin. Now with a regular center of gravity, and with a bit of custom movement code (which will eventually go into an easy-to-use KeyHandler) it moves, but as it moves it begins to "hop."
Here is the code:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package physicshandlertest;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.NodeHandler;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.shape.Box;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.StaticPhysicsNode;
import com.jmex.physics.util.SimplePhysicsGame;
/**
*
* @author Tyler
*/
public class Main extends SimplePhysicsGame
{
StaticPhysicsNode staticNode;
DynamicPhysicsNode dynamicNode;
/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
Main app = new Main();
app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
app.start();
}
@Override
protected void simpleInitGame()
{
staticNode = getPhysicsSpace().createStaticNode();
dynamicNode = getPhysicsSpace().createDynamicNode();
Box floor = new Box("floorbox", Vector3f.ZERO.clone(), 10, 1, 10);
staticNode.attachChild(floor);
Box player = new Box("playerbox", null, 1, 3, 1);
dynamicNode.attachChild(player);
dynamicNode.setLocalTranslation(0, 5, 0);
staticNode.generatePhysicsGeometry();
dynamicNode.generatePhysicsGeometry();
rootNode.attachChild(staticNode);
rootNode.attachChild(dynamicNode);
KeyBindingManager.getKeyBindingManager().add("forward", KeyInput.KEY_I);
KeyBindingManager.getKeyBindingManager().add("back", KeyInput.KEY_K);
KeyBindingManager.getKeyBindingManager().add("left", KeyInput.KEY_J);
KeyBindingManager.getKeyBindingManager().add("right", KeyInput.KEY_L);
}
protected void simpleUpdate()
{
System.out.println(dynamicNode.getLocalRotation());
if(KeyBindingManager.getKeyBindingManager().isValidCommand("forward", true))
dynamicNode.getLocalTranslation().addLocal(dynamicNode.getLocalRotation().mult(new Vector3f(3f,3f,3f)).mult(tpf));
if(KeyBindingManager.getKeyBindingManager().isValidCommand("back", false))
dynamicNode.getLocalTranslation().z -= 1;
if(KeyBindingManager.getKeyBindingManager().isValidCommand("left", false))
dynamicNode.getLocalTranslation().x -= 1;
if(KeyBindingManager.getKeyBindingManager().isValidCommand("right", false))
dynamicNode.getLocalTranslation().x += 1;
}
}
The block moves around using IJKL instead of WASD, the camera is still WASD.
He begins to hop and eventually falls off the floor. I don't understand why it would hop...
OH and he also falls over... if I could figure out how to stop that, that would be wonderful.
Had a little luck moving a box with this code:
char.setLocalTranslation(char.getLocalTranslation().addLocal(char.getLocalTranslation().mult(3,0,3).mult(tpf))));
That will move him around properly, keeping him on the ground. By doing this:
char.lookAt(cam.getDirection(), cam.getUp();
at every update, the box turns with the camera (so you can still use firstpersonhandler as long as you set the camera to the box's position every time).
Still having some random bouncing and stuff while looking up. My model is really crappy too, that may have something to do with it. Really any help is appreciated... I'm getting hopeless.
Another code snippet for you guys to play around with…
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package physicshandlertest;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.math.Vector3f;
import com.jme.scene.CameraNode;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.StaticPhysicsNode;
import com.jmex.physics.material.Material;
import com.jmex.physics.util.SimplePhysicsGame;
/**
*
* @author Tyler
*/
public class Main extends SimplePhysicsGame
{
StaticPhysicsNode staticNode;
DynamicPhysicsNode dynamicNode;
CameraNode camNode;
/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
Main app = new Main();
app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
app.start();
}
@Override
protected void simpleInitGame()
{
staticNode = getPhysicsSpace().createStaticNode();
dynamicNode = getPhysicsSpace().createDynamicNode();
Box floor = new Box("floorbox", Vector3f.ZERO.clone(), 10, 1, 10);
staticNode.attachChild(floor);
Box player = new Box("playerbox", null, 3, 3, 3);
dynamicNode.attachChild(player);
dynamicNode.setLocalTranslation(0, 5, 0);
dynamicNode.setMaterial(Material.ICE);
staticNode.generatePhysicsGeometry();
dynamicNode.generatePhysicsGeometry();
rootNode.attachChild(staticNode);
rootNode.attachChild(dynamicNode);
dynamicNode.clearForce();
KeyBindingManager.getKeyBindingManager().add("fwd", KeyInput.KEY_I);
KeyBindingManager.getKeyBindingManager().add("back", KeyInput.KEY_K);
KeyBindingManager.getKeyBindingManager().add("left", KeyInput.KEY_J);
KeyBindingManager.getKeyBindingManager().add("right", KeyInput.KEY_L);
}
protected void simpleUpdate()
{
cam.setLocation(new Vector3f(dynamicNode.getLocalTranslation().x, dynamicNode.getLocalTranslation().y + 6, dynamicNode.getLocalTranslation().z+10));
dynamicNode.lookAt(new Vector3f(cam.getDirection().x, 0f, cam.getDirection().z), cam.getUp());
if(KeyBindingManager.getKeyBindingManager().isValidCommand("fwd", true))
dynamicNode.getLocalTranslation().addLocal(dynamicNode.getLocalRotation().mult(new Vector3f(3, 0, 3)).mult(tpf));
}
}
When you use jmephsics you shouldn't set the localTranslation, it will cause jitter.
If possible, only use physics forces to move your player.
Moving a object with force works well for a spacecraft or a hovercraft because the object will continue to glide into the previous direction until the applied force has been removed by friction (inertia i think its called?).
So i guess moving a person with forces will be very hard to make it feel right.
What you can try is either use Materials who generate a lot of friction, or use a FrictionCallback to reduce the added force over a short time.
Here is another example using physics ActionRepeatController (turning) and ActionChangeController (running).
In the example i use setLinearVelocity() to move forward, but thats a bad idea also, because for example gravity has no influence anymore as long as setLinearVelocity is called.
Maybe it gives you some ideas, but its far from being a good example
package physichandlertest;
import org.lwjgl.input.Keyboard;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.controls.GameControl;
import com.jme.input.controls.GameControlManager;
import com.jme.input.controls.binding.KeyboardBinding;
import com.jme.input.controls.controller.ActionChangeController;
import com.jme.input.controls.controller.ActionRepeatController;
import com.jme.input.controls.controller.ControlChangeListener;
import com.jme.math.Vector3f;
import com.jme.scene.CameraNode;
import com.jme.scene.shape.Box;
import com.jme.scene.state.TextureState;
import com.jme.util.TextureManager;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.StaticPhysicsNode;
import com.jmex.physics.callback.FrictionCallback;
import com.jmex.physics.material.Material;
import com.jmex.physics.util.SimplePhysicsGame;
public class Main extends SimplePhysicsGame
{
StaticPhysicsNode staticNode;
DynamicPhysicsNode dynamicNode;
CameraNode camNode;
// force applied to accelerate
private float runSpeed = 100;
// torque applied to torn
private float turnSpeed = 300000;
private boolean isRunningFwd = false;
private boolean isRunningBack = false;
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Main app = new Main();
app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
app.start();
}
@Override
protected void simpleInitGame() {
/* create a big floor with monkeys all over it */
Box floor = new Box("floorbox", Vector3f.ZERO.clone(), 1000, 1, 1000);
floor.setModelBound(new BoundingBox());
floor.updateModelBound();
TextureState ts = display.getRenderer().createTextureState();
Texture t = TextureManager.loadTexture(Main.class.getClassLoader().getResource("jmetest/data/images/Monkey.jpg"));
t.setScale(new Vector3f(10,10,10));
t.setWrap(Texture.WM_WRAP_S_WRAP_T);
ts.setTexture(t);
floor.setRenderState(ts);
staticNode = getPhysicsSpace().createStaticNode();
staticNode.attachChild(floor);
staticNode.generatePhysicsGeometry();
staticNode.setMaterial(Material.WOOD);
// our player as a Box
Box player = new Box("playerbox", null, 3, 3, 3);
dynamicNode = getPhysicsSpace().createDynamicNode();
dynamicNode.attachChild(player);
dynamicNode.setLocalTranslation(0, 15, 0);
dynamicNode.setMaterial(Material.GRANITE);
dynamicNode.generatePhysicsGeometry();
dynamicNode.computeMass();
rootNode.attachChild(dynamicNode);
rootNode.attachChild(staticNode);
// create some obstacles to push around
createObstacles();
// set up the keys
GameControlManager ctrl = new GameControlManager();
GameControl fwd = ctrl.addControl("fwd");
fwd.addBinding(new KeyboardBinding(Keyboard.KEY_W));
GameControl back = ctrl.addControl("back");
back.addBinding(new KeyboardBinding(Keyboard.KEY_S));
GameControl right = ctrl.addControl("right");
right.addBinding(new KeyboardBinding(Keyboard.KEY_D));
GameControl left = ctrl.addControl("left");
left.addBinding(new KeyboardBinding(Keyboard.KEY_A));
ActionChangeController toggleFwd = new ActionChangeController(fwd, new ControlChangeListener() {
public void changed(GameControl control, float oldValue, float newValue, float time) {
isRunningFwd = !isRunningFwd;
if (isRunningFwd == false) {
// we stopped, clear force
dynamicNode.setLinearVelocity(new Vector3f());
}
}
});
dynamicNode.addController(toggleFwd);
ActionChangeController toggleBack = new ActionChangeController(back, new ControlChangeListener() {
public void changed(GameControl control, float oldValue, float newValue, float time) {
isRunningBack = !isRunningBack;
if (isRunningBack == false) {
// we stopped, clear force
dynamicNode.setLinearVelocity(new Vector3f());
}
}
});
dynamicNode.addController(toggleBack);
ActionRepeatController rightAction = new ActionRepeatController(right, 50, new TurnAction(true));
ActionRepeatController leftAction = new ActionRepeatController(left, 50, new TurnAction(false));
dynamicNode.addController(rightAction);
dynamicNode.addController(leftAction);
// create a Camera Node and attach it to our player
CameraNode camNode = new CameraNode("camNode", cam);
dynamicNode.attachChild(camNode);
// set the camera above and behind our player
camNode.setLocalTranslation(0, 6, -10);
// add a friction callback to get rid of the applied force
FrictionCallback friction = new FrictionCallback();
friction.add(dynamicNode, 0, 10);
getPhysicsSpace().addToUpdateCallbacks(friction);
}
private void createObstacles() {
for (int i=0; i < 10; i++) {
Box b = new Box("b", null, 3, 6, 4);
b.setModelBound(new BoundingBox());
b.updateModelBound();
TextureState ts = display.getRenderer().createTextureState();
Texture t = TextureManager.loadTexture(Main.class.getClassLoader().getResource("jmetest/data/texture/wall.jpg"));
ts.setTexture(t);
b.setRenderState(ts);
DynamicPhysicsNode tmp = getPhysicsSpace().createDynamicNode();
tmp.attachChild(b);
tmp.setLocalTranslation(i*10+10, 7, i*50);
tmp.setMaterial(Material.WOOD);
tmp.generatePhysicsGeometry();
tmp.computeMass();
rootNode.attachChild(tmp);
}
}
@Override
protected void simpleUpdate() {
if (isRunningFwd) {
// we run forward set the speed directly
dynamicNode.setLinearVelocity(dynamicNode.getLocalRotation().getRotationColumn(2).mult(runSpeed));
}
if (isRunningBack) {
dynamicNode.setLinearVelocity(dynamicNode.getLocalRotation().getRotationColumn(2).mult(-runSpeed));
}
}
class TurnAction implements Runnable {
private boolean right;
public TurnAction(boolean right) {
this.right = right;
}
public void run() {
dynamicNode.addTorque(new Vector3f(0, (right==true?-1:1)*turnSpeed, 0));
}
}
}
Gah. is that the way everybody makes fps games in jme physics? Has anybody ever made an FPS w/ physics?
I once tried that ( I'll post the code later today )
It didn't really work.
I made a little scene with a single DynamicNode at (0,0,0) with gravity off.
At the start i added a little torque so it would face a bit to the left, I wanted it to face straight ahead (0,0,0,1) again.
Every update i would get the angles ( Quaternion.toAngles() i think ) of the quaternion the character should face and substract the angles of the Quat he was facing. Then i'd add that as torque.
The process was a bit messy because the angles were returned as float[] and torque is handled as vector3f, but it did work, eventually.
When I just applied the small torques barely anything happened. Then I multiplied them with themselves or with a constant, or both, that kind of worked.
Usually it took the character one or two minutes to get to rest on its own accord and often it would overshoot and turn wildly for a few minutes.
I tried clearing Torque when the quats where nearly equal, but then they would never quite face the same direction, if I choose a too small margin for "equality" it didn't work at all.
When I tried to port this to my small character standing on the floor it didn't get much better. The torque was either to small to do anything or would overshoot. Setting the rotation by hand if it nearly fitted made it snap into position really unrealistically ( the margin before it started not get detected at all was about 15
Any suggestions on making the character's DynamicPhysicsNode retain the same rotation (in regards to X and Z) as the camera, using torques?
sorry, i seem to have lost that snippet, and i have no implementation of rotation with physics whatsoever in my code right now.
Poo. This is all very disheartening. I don't think there are any actual examples of FPS games using the Physics engine right now, is that correct?
i'm trying to code a fps which runs as applet (or maybe webstart if this doesn't work) and does the server communication via port 80 … so i can do some shooting @ work
i also don't know how to move my character properly, furthermore my bullets can go through objects (only sometimes). i'm using JODE … but maybe i should switch to JBullet? what are your experiences?
what do the physics guy say? is JODE usable for FPS games? what are requirements for a physic engine for a fast fps game … like quake or unreal? is it somehow comparable to JODE?
thx, sascha
I've just finished off my senior semester #1, so I'll be resuming work on this project. I think I have some ideas–that is to ignore friction (so I can apply the force and just move the object), and on release I'll remove all forces. Additionally I'll try to keep it from moving while just on the ground by setting a force threshold. Perhaps even set it to ignore friction while moving only…
Check this thread:
http://www.jmonkeyengine.com/jmeforum/index.php?topic=10233.0
It shows how to have a fully functional physics character with jumping and walking.
I'm going to wire you three-hundred billion dollars now.