问题
Since I changed from the helper class CharacterControl
to the new BetterCharacterControl
I notice some improvements such as pushing other characters is working but my main character has started sliding over steps and can't climb higher steps.
I must jump the step above which is not the right way of playing, it should be just walking over. The old helper class CharacterControl
had a default way of not sliding, just walking over steps and I think it can be corrected by altering the code where I create the main character.
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(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();
}
The complete code is
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.CharacterControl;
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.FastMath;
import com.jme3.math.Quaternion;
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 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;
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 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 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(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();
}
private void createCharacters() {
CapsuleCollisionShape capsule = new CapsuleCollisionShape(0.05f, 0.05f);
createNinja();
ninjaControl.setViewDirection(new Vector3f(0, 0, 1));
//getPhysicsSpace().add(ninjaControl);
createGoblin();
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);
//goblin = assetManager.loadModel("objects/goblin.j3o");
//goblin.scale(4f, 4f, 4f);
//goblinControl = new BetterCharacterControl(2,3,0.5f);
//goblin.addControl(goblinControl);
//goblinControl.setPhysicsLocation(new Vector3f(60, 3.5f, -60));
//goblin.setLocalTranslation(new Vector3f(150,70.5f, -5));
//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");
//goblin.setLocalTranslation(new Vector3f(51.5f, 3, -55));
//rootNode.attachChild(goblin);
//getPhysicsSpace().add(goblinControl);
Spatial monster = assetManager
.loadModel("objects/creatures/monster/monster.packed.j3o");
Spatial monster2 = assetManager.loadModel("Models/Jaime/Jaime.j3o");
monster2.scale(5f, 5f, 5f);
monster.scale(2f, 2f, 2f);
monster.setLocalTranslation(new Vector3f(53, 3, -55));
monster2.setLocalTranslation(new Vector3f(48, 3, -55));
rootNode.attachChild(monster2);
rootNode.attachChild(monster);
}
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("CharShoot") && 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);
}
}
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;
}
}
You can download a demo of my game but how do I improve the walking?
Update
My followup at the jmonkeyforum also had 0 replies.
回答1:
To do this you change the value of the max slope:
setMaxSlope()
From the jmonkey site:
"How steep the slopes and steps are that the character can climb without considering them
an obstacle. Higher obstacles need to be jumped. Vertical height in world units."
Reading this I believe it works in a way similar to, when moving 1 unit in the world what is the maximum change in height a character can experience. If you set this to the size of the steps (or a larger by <1 for safety) you'r character should be able to walk up steps
Sources: http://hub.jmonkeyengine.org/wiki/doku.php/jme3:advanced:walking_character#charactercontrol Developing for Jmonkey before
来源:https://stackoverflow.com/questions/16782375/how-to-improve-character-control-for-my-3d-game