My 3rd person floats in the air and the camera should zoom out:
This is the program
package adventure;
import java.applet.Applet;
import com.jme3.math.Quaternion;
import com.jme3.math.FastMath;
import java.applet.AudioClip;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.TextArea;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import com.jme3.material.RenderState.FaceCullMode;
import javax.swing.JFrame;
import javax.swing.JPanel;
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.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.effect.shapes.EmitterSphereShape;
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.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.scene.shape.Sphere.TextureMode;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeCanvasContext;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.SkyFactory;
public class MountainWorld extends SimpleApplication implements ActionListener,
PhysicsCollisionListener, AnimEventListener, Playable {
private static World world;
private static Person person;
private static Player dplayer;
private static TextArea textarea;
private BulletAppState bulletAppState;
private AnimChannel channel;
private AnimControl control;
// character
CharacterControl character;
Node model;
// temp vectors
Vector3f walkDirection = new Vector3f();
// terrain
TerrainQuad terrain;
RigidBodyControl terrainPhysicsNode;
// Materials
Material matRock;
Material matBullet;
// animation
AnimChannel animationChannel;
AnimChannel shootingChannel;
AnimControl animationControl;
float airTime = 0;
// camera
boolean left = false, right = false, up = false, down = false;
ChaseCamera chaseCam;
// bullet
Sphere bullet;
SphereCollisionShape bulletCollisionShape;
// explosion
ParticleEmitter effect;
// brick wall
Box brick;
float bLength = 0.8f;
float bWidth = 0.4f;
float bHeight = 0.4f;
FilterPostProcessor fpp;
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
AppSettings settings = new AppSettings(true);
settings.setWidth(850);
settings.setHeight(440);
MountainWorld canvasApplication = new MountainWorld();
canvasApplication.setSettings(settings);
canvasApplication.createCanvas(); // create canvas!
JmeCanvasContext ctx = (JmeCanvasContext) canvasApplication
.getContext();
ctx.setSystemListener(canvasApplication);
Dimension dim = new Dimension(640, 480);
ctx.getCanvas().setPreferredSize(dim);
JFrame window = new JFrame("Mountain World");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout()); // a panel
world = new DungeonWorld(canvasApplication);
person = new Person(world, "You", null);
dplayer = new Player(world, person);
Commands commands = new Commands(person);
textarea = new TextArea("", 10, 60,
TextArea.SCROLLBARS_VERTICAL_ONLY);
textarea.append("You are in a mountain. The trolls live here.\n");
textarea.setEditable(false);
panel.add("West", ctx.getCanvas());
panel.add("East", commands);
panel.add("South", textarea);
window.add(panel);
window.pack();
window.setVisible(true);
canvasApplication.startCanvas();
}
});
}
#Override
public void simpleInitApp() {
bulletAppState = new BulletAppState();
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
stateManager.attach(bulletAppState);
setupKeys();
prepareBullet();
prepareEffect();
createLight();
createSky();
createTerrain();
createWall();
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_RETURN));
inputManager
.addMapping("CharShoot", new KeyTrigger(KeyInput.KEY_SPACE));
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 createWall() {
float xOff = -144;
float zOff = -40;
float startpt = bLength / 4 - xOff;
float height = 6.1f;
brick = new Box(Vector3f.ZERO, bLength, bHeight, bWidth);
brick.scaleTextureCoordinates(new Vector2f(1f, .5f));
for (int j = 0; j < 15; j++) {
for (int i = 0; i < 4; i++) {
Vector3f vt = new Vector3f(i * bLength * 2 + startpt, bHeight
+ height, zOff);
addBrick(vt);
}
startpt = -startpt;
height += 1.01f * bHeight;
}
}
private void addBrick(Vector3f ori) {
Geometry reBoxg = new Geometry("brick", brick);
reBoxg.setMaterial(matBullet);
reBoxg.setLocalTranslation(ori);
reBoxg.addControl(new RigidBodyControl(1.5f));
reBoxg.setShadowMode(ShadowMode.CastAndReceive);
this.rootNode.attachChild(reBoxg);
this.getPhysicsSpace().add(reBoxg);
}
private void prepareBullet() {
bullet = new Sphere(32, 32, 0.4f, true, false);
bullet.setTextureMode(TextureMode.Projected);
bulletCollisionShape = new SphereCollisionShape(0.4f);
matBullet = new Material(getAssetManager(),
"Common/MatDefs/Misc/Unshaded.j3md");
matBullet.setColor("Color", ColorRGBA.Green);
// matBullet.setColor("m_GlowColor", ColorRGBA.Green);
getPhysicsSpace().addCollisionListener(this);
}
private void prepareEffect() {
int COUNT_FACTOR = 1;
float COUNT_FACTOR_F = 1f;
effect = new ParticleEmitter("Flame", Type.Triangle, 32 * COUNT_FACTOR);
effect.setSelectRandomImage(true);
effect.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f,
(float) (1f / COUNT_FACTOR_F)));
effect.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
effect.setStartSize(1.3f);
effect.setEndSize(2f);
effect.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
effect.setParticlesPerSec(0);
effect.setGravity(0, -5, 0);
effect.setLowLife(.4f);
effect.setHighLife(.5f);
effect.setInitialVelocity(new Vector3f(0, 7, 0));
effect.setVelocityVariation(1f);
effect.setImagesX(2);
effect.setImagesY(2);
Material mat = new Material(assetManager,
"Common/MatDefs/Misc/Particle.j3md");
mat.setTexture("Texture",
assetManager.loadTexture("Effects/Explosion/flame.png"));
effect.setMaterial(mat);
// effect.setLocalScale(100);
rootNode.attachChild(effect);
}
private void createLight() {
Vector3f direction = new Vector3f(-0.1f, -0.7f, -1).normalizeLocal();
DirectionalLight dl = new DirectionalLight();
dl.setDirection(direction);
dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
rootNode.addLight(dl);
}
private void createSky() {
rootNode.attachChild(SkyFactory.createSky(assetManager,
"Textures/Sky/Bright/BrightSky.dds", false));
}
private void createTerrain() {
matRock = new Material(assetManager,
"Common/MatDefs/Terrain/TerrainLighting.j3md");
matRock.setBoolean("useTriPlanarMapping", false);
matRock.setBoolean("WardIso", true);
matRock.setTexture("AlphaMap",
assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
Texture heightMapImage = assetManager
.loadTexture("Textures/Terrain/splat/mountains512.png");
Texture grass = assetManager
.loadTexture("Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap", grass);
matRock.setFloat("DiffuseMap_0_scale", 64);
Texture dirt = assetManager
.loadTexture("Textures/Terrain/splat/dirt.jpg");
dirt.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_1", dirt);
matRock.setFloat("DiffuseMap_1_scale", 16);
Texture rock = assetManager
.loadTexture("Textures/Terrain/splat/road.jpg");
rock.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_2", rock);
matRock.setFloat("DiffuseMap_2_scale", 128);
Texture normalMap0 = assetManager
.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
normalMap0.setWrap(WrapMode.Repeat);
Texture normalMap1 = assetManager
.loadTexture("Textures/Terrain/splat/dirt_normal.png");
normalMap1.setWrap(WrapMode.Repeat);
Texture normalMap2 = assetManager
.loadTexture("Textures/Terrain/splat/road_normal.png");
normalMap2.setWrap(WrapMode.Repeat);
matRock.setTexture("NormalMap", normalMap0);
matRock.setTexture("NormalMap_1", normalMap2);
matRock.setTexture("NormalMap_2", normalMap2);
AbstractHeightMap heightmap = null;
try {
heightmap = new ImageBasedHeightMap(heightMapImage.getImage(),
0.25f);
heightmap.load();
} catch (Exception e) {
e.printStackTrace();
}
terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
TerrainLodControl control = new TerrainLodControl(terrain, cameras);
terrain.addControl(control);
terrain.setMaterial(matRock);
terrain.setLocalScale(new Vector3f(2, 2, 2));
terrainPhysicsNode = new RigidBodyControl(
CollisionShapeFactory.createMeshShape(terrain), 0);
terrain.addControl(terrainPhysicsNode);
rootNode.attachChild(terrain);
getPhysicsSpace().add(terrainPhysicsNode);
}
private void createCharacters() {
CapsuleCollisionShape capsule = new CapsuleCollisionShape(3f, 4f);
character = new CharacterControl(capsule, 0.01f);
model = (Node) assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
model.setLocalScale(0.15f);
//ninja.setViewDirection( new Vector3f( 1, 0, 1 ) );
//model.rotate(0.0f, -3.0f, 0.0f);
model.addControl(character);
character.setPhysicsLocation(new Vector3f(-140f, 9.9f, -9f));
character.setViewDirection(new Vector3f(1, 0, 0));
rootNode.attachChild(model);
getPhysicsSpace().add(character);
//BlenderKey blenderKey = new BlenderKey(
// "objects/creatures/alien/alien.mesh.xml");
//Node alien = (Node) assetManager.loadModel(blenderKey);
//alien.setLocalTranslation(new Vector3f(-145, 21, -10));
//rootNode.attachChild(alien);
BlenderKey blenderKey2 = new BlenderKey(
"objects/creatures/minotaur/minotaur.mesh.xml");
Spatial man = (Spatial) assetManager.loadModel(blenderKey2);
man.setLocalTranslation(new Vector3f(-140, 15, -10));
rootNode.attachChild(man);
}
private void setupChaseCamera() {
flyCam.setEnabled(false);
chaseCam = new ChaseCamera(cam, model, inputManager);
}
private void setupAnimationController() {
animationControl = model.getControl(AnimControl.class);
animationControl.addListener(this);
animationChannel = animationControl.createChannel();
//shootingChannel = animationControl.createChannel();
//shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "uparm.right"));
//shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "arm.right"));
//shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "hand.right"));
}
#Override
public void simpleUpdate(float tpf) {
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 (!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);
if (airTime > .3f) {
if (!"stand".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("stand");
}
} else if (!"Walk".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("Walk", 0.7f);
}
}
character.setWalkDirection(walkDirection);
}
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();
}
}
private void bulletControl() {
shootingChannel.setAnim("Dodge", 0.1f);
shootingChannel.setLoopMode(LoopMode.DontLoop);
Geometry bulletg = new Geometry("bullet", bullet);
bulletg.setMaterial(matBullet);
bulletg.setShadowMode(ShadowMode.CastAndReceive);
bulletg.setLocalTranslation(character.getPhysicsLocation().add(
cam.getDirection().mult(5)));
RigidBodyControl bulletControl = new BombControl(bulletCollisionShape,
1);
bulletControl.setCcdMotionThreshold(0.1f);
bulletControl.setLinearVelocity(cam.getDirection().mult(80));
bulletg.addControl(bulletControl);
rootNode.attachChild(bulletg);
getPhysicsSpace().add(bulletControl);
}
public void collision(PhysicsCollisionEvent event) {
if (event.getObjectA() instanceof BombControl) {
final Spatial node = event.getNodeA();
effect.killAllParticles();
effect.setLocalTranslation(node.getLocalTranslation());
effect.emitAllParticles();
} else if (event.getObjectB() instanceof BombControl) {
final Spatial node = event.getNodeB();
effect.killAllParticles();
effect.setLocalTranslation(node.getLocalTranslation());
effect.emitAllParticles();
}
}
public void onAnimCycleDone(AnimControl control, AnimChannel channel,
String animName) {
if (channel == shootingChannel) {
channel.setAnim("stand");
}
}
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 green person to stay on the ground but I can't seem to change the physicslocation to appropriate. Do you have any idea? I took this program and just switched the character from oto to ninja:
http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/test/jme3test/bullet/TestWalkingChar.java
Do you have any idea how I can make the camera zoom out so that the whole green person is displayed and change the location of the green person so that he is on the ground?
I've noticed that I can change the location of the main char with this
character.setPhysicsLocation(new Vector3f(-140f, 9.9f, -9f));
but when I put in lower y coordinates the main char drops through the ground and then floats way up in the sky like he ended up on the top of everything when he went through the ground. I could run the example with Oto perfectly, it's changing the main char to ninja that doesn't work for me.
Update
When using setlocaltranslationinstead of setphysicallocationmy main char gets located in the air:
Update 2
After scaling the main char:
model.scale(0.25f);
as suggested in the answer, the rendering is much better:
Update 2
After some more manipulation, it looks more like the way I want:
private void createCharacters() {
CapsuleCollisionShape capsule = new CapsuleCollisionShape(3f, 4f);
character = new CharacterControl(capsule, 0.01f);
model = (Node) assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
model.setLocalScale(0.15f);
// ninja.setViewDirection( new Vector3f( 1, 0, 1 ) );
// model.rotate(0.0f, -3.0f, 0.0f);
float scale = 0.25f;
model.scale(scale, scale, scale);
model.addControl(character);
character.setPhysicsLocation(new Vector3f(-141f, 10.5f, -9f));
model.setShadowMode(ShadowMode.CastAndReceive);
// model.setLocalTranslation(0, 50, 0);
character.setViewDirection(new Vector3f(1, 0, 0));
rootNode.attachChild(model);
getPhysicsSpace().add(character);
// BlenderKey blenderKey = new BlenderKey(
// "objects/creatures/alien/alien.mesh.xml");
// Node alien = (Node) assetManager.loadModel(blenderKey);
// alien.setLocalTranslation(new Vector3f(-145, 21, -10));
// rootNode.attachChild(alien);
BlenderKey blenderKey2 = new BlenderKey("Models/Oto/Oto.mesh.xml");
Spatial man = (Spatial) assetManager.loadModel(blenderKey2);
man.setLocalTranslation(new Vector3f(-140, 10, -10));
// man.setShadowMode(ShadowMode.CastAndReceive);
rootNode.attachChild(man);
}
I suspect there are 3 things that could make your character appear to be hovering:
Your character is hovering. In this case you'd want to use character.setLocalTranslation(0, -20, 0) to adjust where your character is located (may have to adjust the -20 to some other negative number)
Your character's scale is too big compared to the ground's scale (it sounds like this is a problem you want to fix anyways). To fix this, you'd want to use character.scale(.25) (where the .25 can be adjusted to whatever number works for you
You don't have shadows on the ground. This really links your character to the ground and gives the feeling that they're connected in some way. For this, you'll probably want to use object.setShadowMode(RenderQueue.ShadowMode.Receive) for all your terrain objects, and character.setShadowMode(RenderQueue.ShadowMode.CastAndReceive) on your character object.
The JMonkeyEngine javadoc's are pretty good - If you haven't already I would suggest looking there to help figure out what you're capable of doing with your engine. http://jmonkeyengine.org/javadoc/index.html
Related
I am trying to recreate the mini game shooting gallery from Zelda: A link to the past with a triangle polygon that acts as the player and a circle that acts as the projectile and when the player presses space the projectile moves up at a quick speed, but my setOnKeyPressed method does not work, what am i missing?
Method spoken about
scene.setOnKeyPressed(new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
Command movement = keyCommands.get(event.getCode());
if (movement != null){
movement.execute(player);
}
Command shoot = keyCommandsBall.get(event.getCode());
if (shoot != null){
shoot.execute(ball);
}
}
});
This is my main method
package videogameclass;
import java.util.HashMap;
import java.util.Map;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import static javafx.scene.paint.Color.color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
/**
*
* #author
*/
public class VideoGameClass extends Application {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
launch(args);
}
#Override
public void start(Stage stage) throws Exception
{
stage.setTitle("Target practice");
Group root = new Group();
Scene scene = new Scene(root, 320, 240);
stage.setScene(scene);
Circle circle1 = new Circle(20,20,10);
Sprite c = new Sprite(circle1);
circle1.setFill(Color.RED);
root.getChildren().add(c.getNode());
Circle circle2 = new Circle(80, 20, 10);
Sprite c2 = new Sprite(circle2);
circle2.setFill(Color.RED);
root.getChildren().add(c2.getNode());
Circle circle3 = new Circle(140, 20, 10);
Sprite c3 = new Sprite(circle3);
circle3.setFill(Color.RED);
root.getChildren().add(c3.getNode());
Rectangle rectangle = new Rectangle(15,40,25,10);
Sprite rec = new Sprite(rectangle);
rectangle.setFill(Color.BLACK);
root.getChildren().add(rec.getNode());
Rectangle rectangle2 = new Rectangle(75,40,25,10);
Sprite rec2 = new Sprite(rectangle2);
rectangle2.setFill(Color.BLACK);
root.getChildren().add(rec2.getNode());
Rectangle rectangle3 = new Rectangle(135,40,25,10);
Sprite rec3 = new Sprite(rectangle3);
rectangle3.setFill(Color.BLACK);
root.getChildren().add(rec3.getNode());
Rectangle rectangle4 = new Rectangle(195,40,25,10);
Sprite rec4 = new Sprite(rectangle4);
rectangle4.setFill(Color.BLACK);
root.getChildren().add(rec4.getNode());
Polygon t = new Polygon();
Sprite player = new Sprite(t);
root.getChildren().add(player.getNode());
t.getPoints().addAll(new Double[]{
25.0, 20.0,
45.0, 20.0,
35.0, 5.0,
25.0,20.0,
});
t.setTranslateX(125);
t.setTranslateY(200);
Circle circle4 = new Circle(t.getTranslateX()+35,t.getTranslateY(),5);
Sprite ball = new Sprite(circle4);
root.getChildren().add(ball.getNode());
Command moveLeft = new Command()
{
#Override
public void execute(Sprite sprite)
{
sprite.setVelocity(-2, 0);
//sprite.update();
}
};
Command moveRight = new Command()
{
#Override
public void execute(Sprite sprite)
{
sprite.setVelocity(2, 0);
//sprite.update();
}
};
Command moveUp = new Command(){
#Override
public void execute(Sprite sprite){
sprite.setVelocity(0,-2);
}
};
Command stopMoving = new Command()
{
#Override
public void execute(Sprite sprite)
{
sprite.setVelocity(0,0);
}
};
Map<KeyCode,Command> keyCommands = new HashMap<>();
keyCommands.put(KeyCode.LEFT, moveLeft);
keyCommands.put(KeyCode.RIGHT, moveRight);
Map<KeyCode,Command> keyCommandsBall = new HashMap<>();
keyCommands.put(KeyCode.SPACE, moveUp);
Map<KeyCode,Command> keyCommands2 = new HashMap<>();
keyCommands.put(KeyCode.F19, moveLeft);
keyCommands.put(KeyCode.F18, moveRight);
AnimationTimer animation = new AnimationTimer()
{
#Override
public void handle(long now){
c.setVelocity(1.0, 0);
c2.setVelocity(1.0,0);
c3.setVelocity(1.0,0);
ball.setVelocity(0, 0);
rec.setVelocity(-2.0,0);
rec2.setVelocity(-2.0,0);
rec3.setVelocity(-2.0,0);
rec4.setVelocity(-2.0,0);
c.update();
c2.update();
c3.update();
ball.update();//projectile
rec.update();
rec2.update();
rec3.update();
rec4.update();
player.update();
//move sprites
//check and handle collisions
if(c3.getNode().getTranslateX() > scene.getWidth() - 120){
c3.getNode().setTranslateX(-140);
}
if(c2.getNode().getTranslateX() > scene.getWidth() - 75){
c2.getNode().setTranslateX(c3.getNode().getTranslateX()-20);
}
if(c.getNode().getTranslateX() > scene.getWidth() - 10){
c.getNode().setTranslateX(c2.getNode().getTranslateX()-20);
}
if (rec.getNode().getTranslateX() < -40){
rec.getNode().setTranslateX(300);
}
if(rec2.getNode().getTranslateX() < -100){
rec2.getNode().setTranslateX(rec.getNode().getTranslateX()+30);
}
if(rec3.getNode().getTranslateX() < -135){
rec3.getNode().setTranslateX(rec2.getNode().getTranslateX()+30);
}
if(rec4.getNode().getTranslateX() < -225){
rec4.getNode().setTranslateX(rec3.getNode().getTranslateX()+30);
}
if(ball.getNode().getBoundsInParent().intersects(rec.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
if(ball.getNode().getBoundsInParent().intersects(rec2.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
if(ball.getNode().getBoundsInParent().intersects(rec3.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
if(ball.getNode().getBoundsInParent().intersects(rec4.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
if(ball.getNode().getBoundsInParent().intersects(c.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
if(ball.getNode().getBoundsInParent().intersects(c2.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
if(ball.getNode().getBoundsInParent().intersects(c3.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
}//end of handle
};
scene.setOnKeyPressed(new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
Command movement = keyCommands.get(event.getCode());
if (movement != null){
movement.execute(player);
}
Command shoot = keyCommandsBall.get(event.getCode());
if (shoot != null){
shoot.execute(ball);
}
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
Command movement = keyCommands2.get(event.getCode());
Command stop = stopMoving;
if (movement == null){
stop.execute(player);
}
}
});
animation.start();
stage.show();
}
}
This is my Sprite class
package videogameclass;
import javafx.scene.Node;
/**
*
* #author
*/
public class Sprite {
private final Node node;
private double dx, dy;
public Sprite(Node node){
this.node = node;
}
public Node getNode(){
return node;
}
public void setVelocity(double dx, double dy){
this.dx = dx;
this.dy = dy;
}
public double getVelocityX(){
return dx;
}
public double getVelocityY(){
return dy;
}
public void update(){
this.node.setTranslateX(this.node.getTranslateX()+dx);
this.node.setTranslateY(this.node.getTranslateY()+dy);
}
}
This is my Command method
package videogameclass;
public interface Command
{
public void execute(Sprite sprite);
}
Because there is nothing added to keyCommandsBall map.
I am not sure if this is a typo mistake, but I can sense this part of the code is wrong. All the commands added to keyCommands map only.
Map<KeyCode,Command> keyCommands = new HashMap<>();
keyCommands.put(KeyCode.LEFT, moveLeft);
keyCommands.put(KeyCode.RIGHT, moveRight);
Map<KeyCode,Command> keyCommandsBall = new HashMap<>();
keyCommands.put(KeyCode.SPACE, moveUp);
Map<KeyCode,Command> keyCommands2 = new HashMap<>();
keyCommands.put(KeyCode.F19, moveLeft);
keyCommands.put(KeyCode.F18, moveRight);
Update:
I felt a bit interesting in this game so I tried to put my hands in this. Indeed this was quite fun when playing ;)
But few suggestions for you from your code:
Try to reuse to code by either keeping things in loop or by calling a common method. That way you can reduce the code drastically.
Try to create constants for the fixed sizes and positions. That way you accidentally don't get messed up with some wrong values. (Though I have not included that in my example)
When providing a minimal example, try to keep all the code in a single class with all imports (even if it has multiple classes or interfaces). The main aim should be that others should directly copy the class and should be able to run the program.
Below is the output of the code that I worked on. (There is still so much scope to optimize this)
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
public class VideoGameClass extends Application {
private List<Sprite> circles = new ArrayList<>();
private List<Sprite> rectangles = new ArrayList<>();
private List<Sprite> all = new ArrayList<>();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Group root = new Group();
Scene scene = new Scene(root, 320, 240, Color.BLACK);
stage.setTitle("Target practice");
stage.setScene(scene);
Sprite c1 = createCircle(20, 20);
Sprite c2 = createCircle(80, 20);
Sprite c3 = createCircle(140, 20);
Sprite rec1 = createRectangle(15);
Sprite rec2 = createRectangle(75);
Sprite rec3 = createRectangle(135);
Sprite rec4 = createRectangle(195);
Polygon t = new Polygon();
t.getPoints().addAll(new Double[]{
25.0, 20.0,
45.0, 20.0,
35.0, 5.0,
25.0, 20.0,
});
t.setFill(Color.WHITE);
t.setTranslateX(125);
t.setTranslateY(200);
Sprite player = new Sprite(t);
all.add(player);
Sprite ball = new Sprite(new Circle(t.getTranslateX() + 35, t.getTranslateY(), 5, Color.WHITE));
all.add(ball);
all.stream().map(Sprite::getNode).forEach(n -> root.getChildren().add(n));
Command moveLeft = sprite -> sprite.setVelocity(-2, 0);
Command moveRight = sprite -> sprite.setVelocity(2, 0);
Command moveUp = sprite -> sprite.setVelocity(0, -2);
Command stopMoving = sprite -> sprite.setVelocity(0, 0);
Map<KeyCode, Command> keyCommands = new HashMap<>();
keyCommands.put(KeyCode.LEFT, moveLeft);
keyCommands.put(KeyCode.RIGHT, moveRight);
Map<KeyCode, Command> keyCommandsBall = new HashMap<>();
keyCommandsBall.put(KeyCode.SPACE, moveUp);
Map<KeyCode, Command> keyCommands2 = new HashMap<>();
keyCommands.put(KeyCode.F11, moveLeft);
keyCommands.put(KeyCode.F12, moveRight);
AnimationTimer animation = new AnimationTimer() {
#Override
public void handle(long now) {
circles.forEach(c -> c.setVelocity(1.0, 0));
rectangles.forEach(r -> r.setVelocity(-2.0, 0));
all.forEach(Sprite::update);
//check and handle collisions
if (c3.getNode().getTranslateX() > scene.getWidth() - 120) {
c3.getNode().setTranslateX(-140);
}
if (c2.getNode().getTranslateX() > scene.getWidth() - 75) {
c2.getNode().setTranslateX(c3.getNode().getTranslateX() - 20);
}
if (c1.getNode().getTranslateX() > scene.getWidth() - 10) {
c1.getNode().setTranslateX(c2.getNode().getTranslateX() - 20);
}
if (rec1.getNode().getTranslateX() < -40) {
rec1.getNode().setTranslateX(300);
}
if (rec2.getNode().getTranslateX() < -100) {
rec2.getNode().setTranslateX(rec1.getNode().getTranslateX() + 30);
}
if (rec3.getNode().getTranslateX() < -135) {
rec3.getNode().setTranslateX(rec2.getNode().getTranslateX() + 30);
}
if (rec4.getNode().getTranslateX() < -225) {
rec4.getNode().setTranslateX(rec3.getNode().getTranslateX() + 30);
}
// Check for collision and stop the ball
Stream.of(circles, rectangles).flatMap(List::stream).forEach(sprite -> {
if (ball.getNode().getBoundsInParent().intersects(sprite.getNode().getBoundsInParent())) {
ball.setVelocity(0, 0);
}
});
}//end of handle
};
scene.setOnKeyPressed(event -> {
Command movement = keyCommands.get(event.getCode());
if (movement != null) {
movement.execute(player);
}
Command shoot = keyCommandsBall.get(event.getCode());
if (shoot != null) {
shoot.execute(ball);
}
});
scene.setOnKeyReleased(event -> {
Command movement = keyCommands2.get(event.getCode());
Command stop = stopMoving;
if (movement == null) {
stop.execute(player);
}
// Bring back the ball to the player
if(event.getCode() == KeyCode.Z){
ball.setVelocity(0, 0);
Circle ballNode = (Circle) ball.getNode();
ballNode.setTranslateX(0);
ballNode.setTranslateY(0);
ballNode.setCenterX(player.getNode().getTranslateX() + 35);
ballNode.setCenterY(player.getNode().getTranslateY());
}
});
animation.start();
stage.show();
}
private Sprite createCircle(double centerX, double centerY) {
Sprite circle = new Sprite(new Circle(centerX, centerY, 10, Color.RED));
circles.add(circle);
all.add(circle);
return circle;
}
private Sprite createRectangle(double x) {
Rectangle r = new Rectangle(x, 40, 25, 10);
r.setFill(Color.YELLOW);
Sprite rectangle = new Sprite(r);
rectangles.add(rectangle);
all.add(rectangle);
return rectangle;
}
class Sprite {
private final Node node;
private double dx, dy;
public Sprite(Node node) {
this.node = node;
}
public Node getNode() {
return node;
}
public void setVelocity(double dx, double dy) {
this.dx = dx;
this.dy = dy;
}
public void update() {
this.node.setTranslateX(this.node.getTranslateX() + dx);
this.node.setTranslateY(this.node.getTranslateY() + dy);
}
}
interface Command {
void execute(Sprite sprite);
}
}
Plotting in Matlab is very easy and straightforward. For example:
figure('Position_',[100,80,1000,600])
plot(x,y1,'-.or','MarkerSize',0.2,'MarkerFaceColor','r','LineWidth',2)
xlabel('Matrix1')
ylabel('Matrix2')
grid on
hold on
axis([-1,1,0,var1*1.2])
plot(x,y2,'-k','MarkerSize',0.5,'MarkerFaceColor','k','LineWidth',4)
title('My plot')
figuresdir = 'dir';
saveas(gcf,strcat(figuresdir, 'plotimage'), 'bmp');
I found, however, that plotting in Java is more difficult and I have to use packages like JMathPlot or JFreeChart. However, I find it difficult to merge plots and print them to a file using these packages.
Is there an easy way to make plots in Java that uses (about) the same syntax as Matlab does?
Well, Matlab is designed specifically to make things such as plotting as easy as possible. Other languages simply don't have the same kind of support for quick-and-easy plots.
Therefore I decided to write a little Matlab-style charting class based on JFreeChart, just for you:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Stroke;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYTitleAnnotation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleEdge;
public class MatlabChart {
Font font;
JFreeChart chart;
LegendTitle legend;
ArrayList<Color> colors;
ArrayList<Stroke> strokes;
XYSeriesCollection dataset;
public MatlabChart() {
font = JFreeChart.DEFAULT_TITLE_FONT;
colors = new ArrayList<Color>();
strokes = new ArrayList<Stroke>();
dataset = new XYSeriesCollection();
}
public void plot(double[] x, double[] y, String spec, float lineWidth, String title) {
final XYSeries series = new XYSeries(title);
for (int i = 0; i < x.length; i++)
series.add(x[i],y[i]);
dataset.addSeries(series);
FindColor(spec,lineWidth);
}
public void RenderPlot() {
// Create chart
JFreeChart chart = null;
if (dataset != null && dataset.getSeriesCount() > 0)
chart = ChartFactory.createXYLineChart(null,null,null,dataset,PlotOrientation.VERTICAL,true, false, false);
else
System.out.println(" [!] First create a chart and add data to it. The plot is empty now!");
// Add customization options to chart
XYPlot plot = chart.getXYPlot();
for (int i = 0; i < colors.size(); i++) {
plot.getRenderer().setSeriesPaint(i, colors.get(i));
plot.getRenderer().setSeriesStroke(i, strokes.get(i));
}
((NumberAxis)plot.getDomainAxis()).setAutoRangeIncludesZero(false);
((NumberAxis)plot.getRangeAxis()).setAutoRangeIncludesZero(false);
plot.setBackgroundPaint(Color.WHITE);
legend = chart.getLegend();
chart.removeLegend();
this.chart = chart;
}
public void CheckExists() {
if (chart == null) {
throw new IllegalArgumentException("First plot something in the chart before you modify it.");
}
}
public void font(String name, int fontSize) {
CheckExists();
font = new Font(name, Font.PLAIN, fontSize);
chart.getTitle().setFont(font);
chart.getXYPlot().getDomainAxis().setLabelFont(font);
chart.getXYPlot().getDomainAxis().setTickLabelFont(font);
chart.getXYPlot().getRangeAxis().setLabelFont(font);
chart.getXYPlot().getRangeAxis().setTickLabelFont(font);
legend.setItemFont(font);
}
public void title(String title) {
CheckExists();
chart.setTitle(title);
}
public void xlim(double l, double u) {
CheckExists();
chart.getXYPlot().getDomainAxis().setRange(l, u);
}
public void ylim(double l, double u) {
CheckExists();
chart.getXYPlot().getRangeAxis().setRange(l, u);
}
public void xlabel(String label) {
CheckExists();
chart.getXYPlot().getDomainAxis().setLabel(label);
}
public void ylabel(String label) {
CheckExists();
chart.getXYPlot().getRangeAxis().setLabel(label);
}
public void legend(String position) {
CheckExists();
legend.setItemFont(font);
legend.setBackgroundPaint(Color.WHITE);
legend.setFrame(new BlockBorder(Color.BLACK));
if (position.toLowerCase().equals("northoutside")) {
legend.setPosition(RectangleEdge.TOP);
chart.addLegend(legend);
} else if (position.toLowerCase().equals("eastoutside")) {
legend.setPosition(RectangleEdge.RIGHT);
chart.addLegend(legend);
} else if (position.toLowerCase().equals("southoutside")) {
legend.setPosition(RectangleEdge.BOTTOM);
chart.addLegend(legend);
} else if (position.toLowerCase().equals("westoutside")) {
legend.setPosition(RectangleEdge.LEFT);
chart.addLegend(legend);
} else if (position.toLowerCase().equals("north")) {
legend.setPosition(RectangleEdge.TOP);
XYTitleAnnotation ta = new XYTitleAnnotation(0.50,0.98,legend,RectangleAnchor.TOP);
chart.getXYPlot().addAnnotation(ta);
} else if (position.toLowerCase().equals("northeast")) {
legend.setPosition(RectangleEdge.TOP);
XYTitleAnnotation ta = new XYTitleAnnotation(0.98,0.98,legend,RectangleAnchor.TOP_RIGHT);
chart.getXYPlot().addAnnotation(ta);
} else if (position.toLowerCase().equals("east")) {
legend.setPosition(RectangleEdge.RIGHT);
XYTitleAnnotation ta = new XYTitleAnnotation(0.98,0.50,legend,RectangleAnchor.RIGHT);
chart.getXYPlot().addAnnotation(ta);
} else if (position.toLowerCase().equals("southeast")) {
legend.setPosition(RectangleEdge.BOTTOM);
XYTitleAnnotation ta = new XYTitleAnnotation(0.98,0.02,legend,RectangleAnchor.BOTTOM_RIGHT);
chart.getXYPlot().addAnnotation(ta);
} else if (position.toLowerCase().equals("south")) {
legend.setPosition(RectangleEdge.BOTTOM);
XYTitleAnnotation ta = new XYTitleAnnotation(0.50,0.02,legend,RectangleAnchor.BOTTOM);
chart.getXYPlot().addAnnotation(ta);
} else if (position.toLowerCase().equals("southwest")) {
legend.setPosition(RectangleEdge.BOTTOM);
XYTitleAnnotation ta = new XYTitleAnnotation(0.02,0.02,legend,RectangleAnchor.BOTTOM_LEFT);
chart.getXYPlot().addAnnotation(ta);
} else if (position.toLowerCase().equals("west")) {
legend.setPosition(RectangleEdge.LEFT);
XYTitleAnnotation ta = new XYTitleAnnotation(0.02,0.50,legend,RectangleAnchor.LEFT);
chart.getXYPlot().addAnnotation(ta);
} else if (position.toLowerCase().equals("northwest")) {
legend.setPosition(RectangleEdge.TOP);
XYTitleAnnotation ta = new XYTitleAnnotation(0.02,0.98,legend,RectangleAnchor.TOP_LEFT);
chart.getXYPlot().addAnnotation(ta);
}
}
public void grid(String xAxis, String yAxis) {
CheckExists();
if (xAxis.equalsIgnoreCase("on")){
chart.getXYPlot().setDomainGridlinesVisible(true);
chart.getXYPlot().setDomainMinorGridlinesVisible(true);
chart.getXYPlot().setDomainGridlinePaint(Color.GRAY);
} else {
chart.getXYPlot().setDomainGridlinesVisible(false);
chart.getXYPlot().setDomainMinorGridlinesVisible(false);
}
if (yAxis.equalsIgnoreCase("on")){
chart.getXYPlot().setRangeGridlinesVisible(true);
chart.getXYPlot().setRangeMinorGridlinesVisible(true);
chart.getXYPlot().setRangeGridlinePaint(Color.GRAY);
} else {
chart.getXYPlot().setRangeGridlinesVisible(false);
chart.getXYPlot().setRangeMinorGridlinesVisible(false);
}
}
public void saveas(String fileName, int width, int height) {
CheckExists();
File file = new File(fileName);
try {
ChartUtilities.saveChartAsJPEG(file,this.chart,width,height);
} catch (IOException e) {
e.printStackTrace();
}
}
public void FindColor(String spec, float lineWidth) {
float dash[] = {5.0f};
float dot[] = {lineWidth};
Color color = Color.RED; // Default color is red
Stroke stroke = new BasicStroke(lineWidth); // Default stroke is line
if (spec.contains("-"))
stroke = new BasicStroke(lineWidth);
else if (spec.contains(":"))
stroke = new BasicStroke(lineWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f);
else if (spec.contains("."))
stroke = new BasicStroke(lineWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 2.0f, dot, 0.0f);
if (spec.contains("y"))
color = Color.YELLOW;
else if (spec.contains("m"))
color = Color.MAGENTA;
else if (spec.contains("c"))
color = Color.CYAN;
else if (spec.contains("r"))
color = Color.RED;
else if (spec.contains("g"))
color = Color.GREEN;
else if (spec.contains("b"))
color = Color.BLUE;
else if (spec.contains("k"))
color = Color.BLACK;
colors.add(color);
strokes.add(stroke);
}
}
With this, you can plot in Java with syntax very close to Matlab:
public class Demo {
public static void main(String[] args) {
// Create some sample data
double[] x = new double[100]; x[0] = 1;
double[] y1 = new double[100]; y1[0] = 200;
double[] y2 = new double[100]; y2[0] = 300;
for(int i = 1; i < x.length; i++){
x[i] = i+1;
y1[i] = y1[i-1] + Math.random()*10 - 4;
y2[i] = y2[i-1] + Math.random()*10 - 6;
}
// JAVA: // MATLAB:
MatlabChart fig = new MatlabChart(); // figure('Position',[100 100 640 480]);
fig.plot(x, y1, "-r", 2.0f, "AAPL"); // plot(x,y1,'-r','LineWidth',2);
fig.plot(x, y2, ":k", 3.0f, "BAC"); // plot(x,y2,':k','LineWidth',3);
fig.RenderPlot(); // First render plot before modifying
fig.title("Stock 1 vs. Stock 2"); // title('Stock 1 vs. Stock 2');
fig.xlim(10, 100); // xlim([10 100]);
fig.ylim(200, 300); // ylim([200 300]);
fig.xlabel("Days"); // xlabel('Days');
fig.ylabel("Price"); // ylabel('Price');
fig.grid("on","on"); // grid on;
fig.legend("northeast"); // legend('AAPL','BAC','Location','northeast')
fig.font("Helvetica",15); // .. 'FontName','Helvetica','FontSize',15
fig.saveas("MyPlot.jpeg",640,480); // saveas(gcf,'MyPlot','jpeg');
}
}
Now we can compare the final JFreeChart figure to same Matlab figure that we get from this code:
figure('Position',[100 100 640 480]); hold all;
plot(x,y1,'-r','LineWidth',2);
plot(x,y2,':k','LineWidth',3);
title('Stock 1 vs. Stock 2');
xlim([10 100]);
ylim([200 300]);
xlabel('Days');
ylabel('Price');
grid on;
legend('AAPL','BAC','Location','northeast');
saveas(gcf,'MyPlot','jpeg');
Result Java (with the MatlabChart() class):
Result Matlab:
The MatlabChart() class I wrote has support for some of the basic plotting syntax in Matlab. You can indicate line styles (:,-,.), change line colors (y,m,c,r,g,b,w,k), change the LineWidth and change the position of the legend (northoutside,eastoutside,soutoutside, westoutside,north,east,south,west,northeast,southeast,southwest,northwest). You can also turn the grid on for the x and y-axis independently. For example: grid("off","on"); turns the x-axis grid off and turns the y-axis grid on.
That should make plotting in Java a lot easier for those used to plotting in Matlab :)
The code I use I just took from an example and it does build a wall to my scene:
/** This loop builds a wall out of individual bricks. */
public void initWall() {
float startpt = brickLength / 4;
float height = 0;
for (int j = 0; j < 15; j++) {
for (int i = 0; i < 6; i++) {
Vector3f vt =
new Vector3f(i * brickLength * 2 + startpt, brickHeight + height, 10);
makeBrick(vt);
}
startpt = -startpt;
height += 2 * brickHeight;
}
}
/** This method creates one individual physical brick. */
public void makeBrick(Vector3f loc) {
/** Create a brick geometry and attach to scene graph. */
Geometry brick_geo = new Geometry("brick", box);
brick_geo.setMaterial(wall_mat);
rootNode.attachChild(brick_geo);
/** Position the brick geometry */
brick_geo.setLocalTranslation(loc);
/** Make brick physical with a mass > 0.0f. */
brick_phy = new RigidBodyControl(2f);
/** Add physical brick to physics space. */
brick_geo.addControl(brick_phy);
bulletAppState.getPhysicsSpace().add(brick_phy);
}
Now I wonder if I should use this code to build up an entire labyrinth / dungeon or if this is best modelled outside the program eg. in blender instead like a .scene and then read into the program? The entire program is
package adventure;
import java.applet.Applet;
import com.jme3.math.Quaternion;
import com.jme3.math.FastMath;
import java.applet.AudioClip;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.TextArea;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import com.jme3.material.RenderState.FaceCullMode;
import javax.swing.JFrame;
import javax.swing.JPanel;
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.TextureKey;
import com.jme3.asset.plugins.ZipLocator;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.effect.shapes.EmitterSphereShape;
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.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.scene.shape.Sphere.TextureMode;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeCanvasContext;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.SkyFactory;
public class MountainWorld extends SimpleApplication implements ActionListener,
PhysicsCollisionListener, AnimEventListener, Playable {
/** Prepare Materials */
Material wall_mat;
Material stone_mat;
Material floor_mat;
/** Prepare geometries and physical nodes for bricks and cannon balls. */
private RigidBodyControl brick_phy;
private static final Box box;
private RigidBodyControl ball_phy;
private static final Sphere sphere;
private RigidBodyControl floor_phy;
private static final Box floor;
/** dimensions used for bricks and wall */
private static final float brickLength = 0.48f;
private static final float brickWidth = 0.24f;
private static final float brickHeight = 0.12f;
static {
/** Initialize the cannon ball geometry */
sphere = new Sphere(32, 32, 0.4f, true, false);
sphere.setTextureMode(TextureMode.Projected);
/** Initialize the brick geometry */
box = new Box(Vector3f.ZERO, brickLength, brickHeight, brickWidth);
box.scaleTextureCoordinates(new Vector2f(1f, .5f));
/** Initialize the floor geometry */
floor = new Box(Vector3f.ZERO, 100f, 0.1f, 50f);
floor.scaleTextureCoordinates(new Vector2f(3, 6));
}
private static World world;
private static Person person;
private static Player dplayer;
private static TextArea textarea;
private BulletAppState bulletAppState;
private AnimChannel channel;
private AnimControl control;
// character
CharacterControl character;
Node model;
// temp vectors
Vector3f walkDirection = new Vector3f();
// terrain
TerrainQuad terrain;
RigidBodyControl terrainPhysicsNode;
// Materials
Material matRock;
Material matBullet;
// animation
AnimChannel animationChannel;
AnimChannel shootingChannel;
AnimControl animationControl;
float airTime = 0;
// camera
boolean left = false, right = false, up = false, down = false;
ChaseCamera chaseCam;
// bullet
Sphere bullet;
SphereCollisionShape bulletCollisionShape;
// explosion
ParticleEmitter effect;
// brick wall
Box brick;
float bLength = 0.8f;
float bWidth = 0.4f;
float bHeight = 0.4f;
FilterPostProcessor fpp;
private Spatial sceneModel;
private RigidBodyControl landscape;
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
AppSettings settings = new AppSettings(true);
settings.setWidth(850);
settings.setHeight(440);
MountainWorld canvasApplication = new MountainWorld();
canvasApplication.setSettings(settings);
canvasApplication.createCanvas(); // create canvas!
JmeCanvasContext ctx = (JmeCanvasContext) canvasApplication
.getContext();
ctx.setSystemListener(canvasApplication);
Dimension dim = new Dimension(640, 480);
ctx.getCanvas().setPreferredSize(dim);
JFrame window = new JFrame("Dungeon World");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout()); // a panel
world = new DungeonWorld(canvasApplication);
person = new Person(world, "You", null);
dplayer = new Player(world, person);
Commands commands = new Commands(person);
textarea = new TextArea("", 10, 60,
TextArea.SCROLLBARS_VERTICAL_ONLY);
textarea.append("You are in a dungeon. The horrible shrieks of the undead chill your bones.\n");
textarea.setEditable(false);
panel.add("West", ctx.getCanvas());
panel.add("East", commands);
panel.add("South", textarea);
window.add(panel);
window.pack();
window.setVisible(true);
canvasApplication.startCanvas();
}
});
}
#Override
public void simpleInitApp() {
bulletAppState = new BulletAppState();
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
stateManager.attach(bulletAppState);
setupKeys();
//prepareBullet();
//prepareEffect();
createLight();
//createSky();
initMaterials();
initFloor();
initWall();
//createTerrain();
//createWall();
createCharacters();
setupChaseCamera();
setupAnimationController();
setupFilter();
}
/** This loop builds a wall out of individual bricks. */
public void initWall() {
float startpt = brickLength / 4;
float height = 0;
for (int j = 0; j < 15; j++) {
for (int i = 0; i < 6; i++) {
Vector3f vt =
new Vector3f(i * brickLength * 2 + startpt, brickHeight + height, 10);
makeBrick(vt);
}
startpt = -startpt;
height += 2 * brickHeight;
}
}
/** This method creates one individual physical brick. */
public void makeBrick(Vector3f loc) {
/** Create a brick geometry and attach to scene graph. */
Geometry brick_geo = new Geometry("brick", box);
brick_geo.setMaterial(wall_mat);
rootNode.attachChild(brick_geo);
/** Position the brick geometry */
brick_geo.setLocalTranslation(loc);
/** Make brick physical with a mass > 0.0f. */
brick_phy = new RigidBodyControl(2f);
/** Add physical brick to physics space. */
brick_geo.addControl(brick_phy);
bulletAppState.getPhysicsSpace().add(brick_phy);
}
/** Make a solid floor and add it to the scene. */
public void initFloor() {
Geometry floor_geo = new Geometry("Floor", floor);
floor_geo.setMaterial(floor_mat);
floor_geo.setLocalTranslation(0, -0.1f, 0);
this.rootNode.attachChild(floor_geo);
/* Make the floor physical with mass 0.0f! */
floor_phy = new RigidBodyControl(0.0f);
floor_geo.addControl(floor_phy);
bulletAppState.getPhysicsSpace().add(floor_phy);
}
/** Initialize the materials used in this scene. */
public void initMaterials() {
wall_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg");
key.setGenerateMips(true);
Texture tex = assetManager.loadTexture(key);
wall_mat.setTexture("ColorMap", tex);
stone_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
key2.setGenerateMips(true);
Texture tex2 = assetManager.loadTexture(key2);
stone_mat.setTexture("ColorMap", tex2);
floor_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg");
key3.setGenerateMips(true);
Texture tex3 = assetManager.loadTexture(key3);
tex3.setWrap(WrapMode.Repeat);
floor_mat.setTexture("ColorMap", tex3);
}
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_RETURN));
inputManager
.addMapping("CharShoot", new KeyTrigger(KeyInput.KEY_SPACE));
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 createWall() {
float xOff = -144;
float zOff = -40;
float startpt = bLength / 4 - xOff;
float height = 6.1f;
brick = new Box(Vector3f.ZERO, bLength, bHeight, bWidth);
brick.scaleTextureCoordinates(new Vector2f(1f, .5f));
for (int j = 0; j < 15; j++) {
for (int i = 0; i < 4; i++) {
Vector3f vt = new Vector3f(i * bLength * 2 + startpt, bHeight
+ height, zOff);
addBrick(vt);
}
startpt = -startpt;
height += 1.01f * bHeight;
}
}
private void addBrick(Vector3f ori) {
Geometry reBoxg = new Geometry("brick", brick);
reBoxg.setMaterial(matBullet);
reBoxg.setLocalTranslation(ori);
reBoxg.addControl(new RigidBodyControl(1.5f));
reBoxg.setShadowMode(ShadowMode.CastAndReceive);
this.rootNode.attachChild(reBoxg);
this.getPhysicsSpace().add(reBoxg);
}
private void prepareBullet() {
bullet = new Sphere(32, 32, 0.4f, true, false);
bullet.setTextureMode(TextureMode.Projected);
bulletCollisionShape = new SphereCollisionShape(0.4f);
matBullet = new Material(getAssetManager(),
"Common/MatDefs/Misc/Unshaded.j3md");
matBullet.setColor("Color", ColorRGBA.Green);
// matBullet.setColor("m_GlowColor", ColorRGBA.Green);
getPhysicsSpace().addCollisionListener(this);
}
private void prepareEffect() {
int COUNT_FACTOR = 1;
float COUNT_FACTOR_F = 1f;
effect = new ParticleEmitter("Flame", Type.Triangle, 32 * COUNT_FACTOR);
effect.setSelectRandomImage(true);
effect.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f,
(float) (1f / COUNT_FACTOR_F)));
effect.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
effect.setStartSize(1.3f);
effect.setEndSize(2f);
effect.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
effect.setParticlesPerSec(0);
effect.setGravity(0, -5, 0);
effect.setLowLife(.4f);
effect.setHighLife(.5f);
effect.setInitialVelocity(new Vector3f(0, 7, 0));
effect.setVelocityVariation(1f);
effect.setImagesX(2);
effect.setImagesY(2);
Material mat = new Material(assetManager,
"Common/MatDefs/Misc/Particle.j3md");
mat.setTexture("Texture",
assetManager.loadTexture("Effects/Explosion/flame.png"));
effect.setMaterial(mat);
// effect.setLocalScale(100);
rootNode.attachChild(effect);
}
private void createLight() {
Vector3f direction = new Vector3f(-0.1f, -0.7f, -1).normalizeLocal();
DirectionalLight dl = new DirectionalLight();
dl.setDirection(direction);
dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
rootNode.addLight(dl);
}
private void createSky() {
rootNode.attachChild(SkyFactory.createSky(assetManager,
"Textures/Sky/Bright/BrightSky.dds", false));
}
private void createTerrain2() {
matRock = new Material(assetManager,
"Common/MatDefs/Terrain/TerrainLighting.j3md");
matRock.setBoolean("useTriPlanarMapping", false);
matRock.setBoolean("WardIso", true);
matRock.setTexture("AlphaMap",
assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
Texture heightMapImage = assetManager
.loadTexture("Textures/Terrain/splat/mountains512.png");
Texture grass = assetManager
.loadTexture("Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap", grass);
matRock.setFloat("DiffuseMap_0_scale", 64);
Texture dirt = assetManager
.loadTexture("Textures/Terrain/splat/dirt.jpg");
dirt.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_1", dirt);
matRock.setFloat("DiffuseMap_1_scale", 16);
Texture rock = assetManager
.loadTexture("Textures/Terrain/splat/road.jpg");
rock.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_2", rock);
matRock.setFloat("DiffuseMap_2_scale", 128);
Texture normalMap0 = assetManager
.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
normalMap0.setWrap(WrapMode.Repeat);
Texture normalMap1 = assetManager
.loadTexture("Textures/Terrain/splat/dirt_normal.png");
normalMap1.setWrap(WrapMode.Repeat);
Texture normalMap2 = assetManager
.loadTexture("Textures/Terrain/splat/road_normal.png");
normalMap2.setWrap(WrapMode.Repeat);
matRock.setTexture("NormalMap", normalMap0);
matRock.setTexture("NormalMap_1", normalMap2);
matRock.setTexture("NormalMap_2", normalMap2);
AbstractHeightMap heightmap = null;
try {
heightmap = new ImageBasedHeightMap(heightMapImage.getImage(),
0.25f);
heightmap.load();
} catch (Exception e) {
e.printStackTrace();
}
terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
TerrainLodControl control = new TerrainLodControl(terrain, cameras);
terrain.addControl(control);
terrain.setMaterial(matRock);
terrain.setLocalScale(new Vector3f(2, 2, 2));
terrainPhysicsNode = new RigidBodyControl(
CollisionShapeFactory.createMeshShape(terrain), 0);
terrain.addControl(terrainPhysicsNode);
rootNode.attachChild(terrain);
getPhysicsSpace().add(terrainPhysicsNode);
}
private void createTerrain() {
assetManager.registerLocator("town.zip", ZipLocator.class);
sceneModel = assetManager.loadModel("main.scene");
//sceneModel = assetManager.loadModel("Scenes/ManyLights/Main.scene");
sceneModel.setLocalScale(2f);
//initFloor();
// We set up collision detection for the scene by creating a
// compound collision shape and a static RigidBodyControl with mass
// zero.
CollisionShape sceneShape = CollisionShapeFactory
.createMeshShape((Node) sceneModel);
landscape = new RigidBodyControl(sceneShape, 0);
sceneModel.addControl(landscape);
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
rootNode.attachChild(sceneModel);
}
private void createCharacters() {
CapsuleCollisionShape capsule = new CapsuleCollisionShape(0.0f, 0.0f);
character = new CharacterControl(capsule, 0.01f);
model = (Node) assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
float scale = 0.25f;
model.scale(0.05f, 0.05f, 0.05f);
model.addControl(character);
character.setPhysicsLocation(new Vector3f(0, -0.05f, 0));
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(-10, 4.5f, -10));
man.setShadowMode(ShadowMode.CastAndReceive);
rootNode.attachChild(man);
}
private void setupChaseCamera() {
flyCam.setEnabled(false);
chaseCam = new ChaseCamera(cam, model, inputManager);
}
private void setupAnimationController() {
animationControl = model.getControl(AnimControl.class);
animationControl.addListener(this);
animationChannel = animationControl.createChannel();
// shootingChannel = animationControl.createChannel();
// shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "uparm.right"));
// shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "arm.right"));
// shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "hand.right"));
}
#Override
public void simpleUpdate(float tpf) {
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 (!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);
if (airTime > .3f) {
if (!"stand".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("stand");
}
} else if (!"Walk".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("Walk", 0.7f);
}
}
character.setWalkDirection(walkDirection);
}
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();
}
}
private void bulletControl() {
shootingChannel.setAnim("Dodge", 0.1f);
shootingChannel.setLoopMode(LoopMode.DontLoop);
Geometry bulletg = new Geometry("bullet", bullet);
bulletg.setMaterial(matBullet);
bulletg.setShadowMode(ShadowMode.CastAndReceive);
bulletg.setLocalTranslation(character.getPhysicsLocation().add(
cam.getDirection().mult(5)));
RigidBodyControl bulletControl = new BombControl(bulletCollisionShape,
1);
bulletControl.setCcdMotionThreshold(0.1f);
bulletControl.setLinearVelocity(cam.getDirection().mult(80));
bulletg.addControl(bulletControl);
rootNode.attachChild(bulletg);
getPhysicsSpace().add(bulletControl);
}
public void collision(PhysicsCollisionEvent event) {
if (event.getObjectA() instanceof BombControl) {
final Spatial node = event.getNodeA();
effect.killAllParticles();
effect.setLocalTranslation(node.getLocalTranslation());
effect.emitAllParticles();
} else if (event.getObjectB() instanceof BombControl) {
final Spatial node = event.getNodeB();
effect.killAllParticles();
effect.setLocalTranslation(node.getLocalTranslation());
effect.emitAllParticles();
}
}
public void onAnimCycleDone(AnimControl control, AnimChannel channel,
String animName) {
if (channel == shootingChannel) {
channel.setAnim("stand");
}
}
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();
}
}
In your example you have about 100+ bricks - just imagine how many of them you'll need for full dungeon and how it will kill your physics performance :). It's almost impossible do create playable game with this approach.
You should create some static mesh and divide it into corridors, rooms, etc. so you could draw only visible part of your level. If you really want to create destructible environment you should still use static mesh and dynamically replace parts of it with the bricks when the wall should be destroyed.
I'm making a simple 3D scene with a character and a floor but the character is hovering, or falling through the scene if I put the y coordinate to a negative.
private void createCharacters() {
CapsuleCollisionShape capsule = new CapsuleCollisionShape(3f, 4f);
character = new CharacterControl(capsule, 0.01f);
model = (Node) assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
float scale = 0.25f;
model.scale(0.05f, 0.05f, 0.05f);
model.addControl(character);
character.setPhysicsLocation(new Vector3f(0, -0.15f, 0));
model.setShadowMode(ShadowMode.CastAndReceive);
character.setViewDirection(new Vector3f(1, 0, 0));
rootNode.attachChild(model);
getPhysicsSpace().add(character);
}
The above code makes the character fall through the scene. Changing it just a little bit to this makes the character hover:
character.setPhysicsLocation(new Vector3f(0, -0.09f, 0));
If you want to inspect all of the code it's here
package adventure;
import java.applet.Applet;
import com.jme3.math.Quaternion;
import com.jme3.math.FastMath;
import java.applet.AudioClip;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.TextArea;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import com.jme3.material.RenderState.FaceCullMode;
import javax.swing.JFrame;
import javax.swing.JPanel;
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.TextureKey;
import com.jme3.asset.plugins.ZipLocator;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.effect.shapes.EmitterSphereShape;
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.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.scene.shape.Sphere.TextureMode;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeCanvasContext;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.SkyFactory;
public class MountainWorld extends SimpleApplication implements ActionListener,
PhysicsCollisionListener, AnimEventListener, Playable {
/** Prepare Materials */
Material wall_mat;
Material stone_mat;
Material floor_mat;
/** Prepare geometries and physical nodes for bricks and cannon balls. */
private RigidBodyControl brick_phy;
private static final Box box;
private RigidBodyControl ball_phy;
private static final Sphere sphere;
private RigidBodyControl floor_phy;
private static final Box floor;
/** dimensions used for bricks and wall */
private static final float brickLength = 0.48f;
private static final float brickWidth = 0.24f;
private static final float brickHeight = 0.12f;
static {
/** Initialize the cannon ball geometry */
sphere = new Sphere(32, 32, 0.4f, true, false);
sphere.setTextureMode(TextureMode.Projected);
/** Initialize the brick geometry */
box = new Box(Vector3f.ZERO, brickLength, brickHeight, brickWidth);
box.scaleTextureCoordinates(new Vector2f(1f, .5f));
/** Initialize the floor geometry */
floor = new Box(Vector3f.ZERO, 10f, 0.1f, 5f);
floor.scaleTextureCoordinates(new Vector2f(3, 6));
}
private static World world;
private static Person person;
private static Player dplayer;
private static TextArea textarea;
private BulletAppState bulletAppState;
private AnimChannel channel;
private AnimControl control;
// character
CharacterControl character;
Node model;
// temp vectors
Vector3f walkDirection = new Vector3f();
// terrain
TerrainQuad terrain;
RigidBodyControl terrainPhysicsNode;
// Materials
Material matRock;
Material matBullet;
// animation
AnimChannel animationChannel;
AnimChannel shootingChannel;
AnimControl animationControl;
float airTime = 0;
// camera
boolean left = false, right = false, up = false, down = false;
ChaseCamera chaseCam;
// bullet
Sphere bullet;
SphereCollisionShape bulletCollisionShape;
// explosion
ParticleEmitter effect;
// brick wall
Box brick;
float bLength = 0.8f;
float bWidth = 0.4f;
float bHeight = 0.4f;
FilterPostProcessor fpp;
private Spatial sceneModel;
private RigidBodyControl landscape;
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
AppSettings settings = new AppSettings(true);
settings.setWidth(850);
settings.setHeight(440);
MountainWorld canvasApplication = new MountainWorld();
canvasApplication.setSettings(settings);
canvasApplication.createCanvas(); // create canvas!
JmeCanvasContext ctx = (JmeCanvasContext) canvasApplication
.getContext();
ctx.setSystemListener(canvasApplication);
Dimension dim = new Dimension(640, 480);
ctx.getCanvas().setPreferredSize(dim);
JFrame window = new JFrame("Mountain World");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout()); // a panel
world = new DungeonWorld(canvasApplication);
person = new Person(world, "You", null);
dplayer = new Player(world, person);
Commands commands = new Commands(person);
textarea = new TextArea("", 10, 60,
TextArea.SCROLLBARS_VERTICAL_ONLY);
textarea.append("You are in a mountain. The trolls live here.\n");
textarea.setEditable(false);
panel.add("West", ctx.getCanvas());
panel.add("East", commands);
panel.add("South", textarea);
window.add(panel);
window.pack();
window.setVisible(true);
canvasApplication.startCanvas();
}
});
}
#Override
public void simpleInitApp() {
bulletAppState = new BulletAppState();
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
stateManager.attach(bulletAppState);
setupKeys();
//prepareBullet();
//prepareEffect();
createLight();
//createSky();
initMaterials();
initFloor();
//createTerrain();
//createWall();
createCharacters();
setupChaseCamera();
setupAnimationController();
setupFilter();
}
/** Make a solid floor and add it to the scene. */
public void initFloor() {
Geometry floor_geo = new Geometry("Floor", floor);
floor_geo.setMaterial(floor_mat);
floor_geo.setLocalTranslation(0, -0.1f, 0);
this.rootNode.attachChild(floor_geo);
/* Make the floor physical with mass 0.0f! */
floor_phy = new RigidBodyControl(0.0f);
floor_geo.addControl(floor_phy);
bulletAppState.getPhysicsSpace().add(floor_phy);
}
/** Initialize the materials used in this scene. */
public void initMaterials() {
wall_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg");
key.setGenerateMips(true);
Texture tex = assetManager.loadTexture(key);
wall_mat.setTexture("ColorMap", tex);
stone_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
key2.setGenerateMips(true);
Texture tex2 = assetManager.loadTexture(key2);
stone_mat.setTexture("ColorMap", tex2);
floor_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg");
key3.setGenerateMips(true);
Texture tex3 = assetManager.loadTexture(key3);
tex3.setWrap(WrapMode.Repeat);
floor_mat.setTexture("ColorMap", tex3);
}
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_RETURN));
inputManager
.addMapping("CharShoot", new KeyTrigger(KeyInput.KEY_SPACE));
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 createWall() {
float xOff = -144;
float zOff = -40;
float startpt = bLength / 4 - xOff;
float height = 6.1f;
brick = new Box(Vector3f.ZERO, bLength, bHeight, bWidth);
brick.scaleTextureCoordinates(new Vector2f(1f, .5f));
for (int j = 0; j < 15; j++) {
for (int i = 0; i < 4; i++) {
Vector3f vt = new Vector3f(i * bLength * 2 + startpt, bHeight
+ height, zOff);
addBrick(vt);
}
startpt = -startpt;
height += 1.01f * bHeight;
}
}
private void addBrick(Vector3f ori) {
Geometry reBoxg = new Geometry("brick", brick);
reBoxg.setMaterial(matBullet);
reBoxg.setLocalTranslation(ori);
reBoxg.addControl(new RigidBodyControl(1.5f));
reBoxg.setShadowMode(ShadowMode.CastAndReceive);
this.rootNode.attachChild(reBoxg);
this.getPhysicsSpace().add(reBoxg);
}
private void prepareBullet() {
bullet = new Sphere(32, 32, 0.4f, true, false);
bullet.setTextureMode(TextureMode.Projected);
bulletCollisionShape = new SphereCollisionShape(0.4f);
matBullet = new Material(getAssetManager(),
"Common/MatDefs/Misc/Unshaded.j3md");
matBullet.setColor("Color", ColorRGBA.Green);
// matBullet.setColor("m_GlowColor", ColorRGBA.Green);
getPhysicsSpace().addCollisionListener(this);
}
private void prepareEffect() {
int COUNT_FACTOR = 1;
float COUNT_FACTOR_F = 1f;
effect = new ParticleEmitter("Flame", Type.Triangle, 32 * COUNT_FACTOR);
effect.setSelectRandomImage(true);
effect.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f,
(float) (1f / COUNT_FACTOR_F)));
effect.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
effect.setStartSize(1.3f);
effect.setEndSize(2f);
effect.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
effect.setParticlesPerSec(0);
effect.setGravity(0, -5, 0);
effect.setLowLife(.4f);
effect.setHighLife(.5f);
effect.setInitialVelocity(new Vector3f(0, 7, 0));
effect.setVelocityVariation(1f);
effect.setImagesX(2);
effect.setImagesY(2);
Material mat = new Material(assetManager,
"Common/MatDefs/Misc/Particle.j3md");
mat.setTexture("Texture",
assetManager.loadTexture("Effects/Explosion/flame.png"));
effect.setMaterial(mat);
// effect.setLocalScale(100);
rootNode.attachChild(effect);
}
private void createLight() {
Vector3f direction = new Vector3f(-0.1f, -0.7f, -1).normalizeLocal();
DirectionalLight dl = new DirectionalLight();
dl.setDirection(direction);
dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
rootNode.addLight(dl);
}
private void createSky() {
rootNode.attachChild(SkyFactory.createSky(assetManager,
"Textures/Sky/Bright/BrightSky.dds", false));
}
private void createTerrain2() {
matRock = new Material(assetManager,
"Common/MatDefs/Terrain/TerrainLighting.j3md");
matRock.setBoolean("useTriPlanarMapping", false);
matRock.setBoolean("WardIso", true);
matRock.setTexture("AlphaMap",
assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
Texture heightMapImage = assetManager
.loadTexture("Textures/Terrain/splat/mountains512.png");
Texture grass = assetManager
.loadTexture("Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap", grass);
matRock.setFloat("DiffuseMap_0_scale", 64);
Texture dirt = assetManager
.loadTexture("Textures/Terrain/splat/dirt.jpg");
dirt.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_1", dirt);
matRock.setFloat("DiffuseMap_1_scale", 16);
Texture rock = assetManager
.loadTexture("Textures/Terrain/splat/road.jpg");
rock.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_2", rock);
matRock.setFloat("DiffuseMap_2_scale", 128);
Texture normalMap0 = assetManager
.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
normalMap0.setWrap(WrapMode.Repeat);
Texture normalMap1 = assetManager
.loadTexture("Textures/Terrain/splat/dirt_normal.png");
normalMap1.setWrap(WrapMode.Repeat);
Texture normalMap2 = assetManager
.loadTexture("Textures/Terrain/splat/road_normal.png");
normalMap2.setWrap(WrapMode.Repeat);
matRock.setTexture("NormalMap", normalMap0);
matRock.setTexture("NormalMap_1", normalMap2);
matRock.setTexture("NormalMap_2", normalMap2);
AbstractHeightMap heightmap = null;
try {
heightmap = new ImageBasedHeightMap(heightMapImage.getImage(),
0.25f);
heightmap.load();
} catch (Exception e) {
e.printStackTrace();
}
terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
TerrainLodControl control = new TerrainLodControl(terrain, cameras);
terrain.addControl(control);
terrain.setMaterial(matRock);
terrain.setLocalScale(new Vector3f(2, 2, 2));
terrainPhysicsNode = new RigidBodyControl(
CollisionShapeFactory.createMeshShape(terrain), 0);
terrain.addControl(terrainPhysicsNode);
rootNode.attachChild(terrain);
getPhysicsSpace().add(terrainPhysicsNode);
}
private void createTerrain() {
assetManager.registerLocator("town.zip", ZipLocator.class);
sceneModel = assetManager.loadModel("main.scene");
//sceneModel = assetManager.loadModel("Scenes/ManyLights/Main.scene");
sceneModel.setLocalScale(2f);
//initFloor();
// We set up collision detection for the scene by creating a
// compound collision shape and a static RigidBodyControl with mass
// zero.
CollisionShape sceneShape = CollisionShapeFactory
.createMeshShape((Node) sceneModel);
landscape = new RigidBodyControl(sceneShape, 0);
sceneModel.addControl(landscape);
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
rootNode.attachChild(sceneModel);
}
private void createCharacters() {
CapsuleCollisionShape capsule = new CapsuleCollisionShape(3f, 4f);
character = new CharacterControl(capsule, 0.01f);
model = (Node) assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
float scale = 0.25f;
model.scale(0.05f, 0.05f, 0.05f);
model.addControl(character);
character.setPhysicsLocation(new Vector3f(0, -0.09f, 0));
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(-140, 12.5f, -10));
// man.setShadowMode(ShadowMode.CastAndReceive);
//rootNode.attachChild(man);
}
private void setupChaseCamera() {
flyCam.setEnabled(false);
chaseCam = new ChaseCamera(cam, model, inputManager);
}
private void setupAnimationController() {
animationControl = model.getControl(AnimControl.class);
animationControl.addListener(this);
animationChannel = animationControl.createChannel();
// shootingChannel = animationControl.createChannel();
// shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "uparm.right"));
// shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "arm.right"));
// shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "hand.right"));
}
#Override
public void simpleUpdate(float tpf) {
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 (!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);
if (airTime > .3f) {
if (!"stand".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("stand");
}
} else if (!"Walk".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("Walk", 0.7f);
}
}
character.setWalkDirection(walkDirection);
}
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();
}
}
private void bulletControl() {
shootingChannel.setAnim("Dodge", 0.1f);
shootingChannel.setLoopMode(LoopMode.DontLoop);
Geometry bulletg = new Geometry("bullet", bullet);
bulletg.setMaterial(matBullet);
bulletg.setShadowMode(ShadowMode.CastAndReceive);
bulletg.setLocalTranslation(character.getPhysicsLocation().add(
cam.getDirection().mult(5)));
RigidBodyControl bulletControl = new BombControl(bulletCollisionShape,
1);
bulletControl.setCcdMotionThreshold(0.1f);
bulletControl.setLinearVelocity(cam.getDirection().mult(80));
bulletg.addControl(bulletControl);
rootNode.attachChild(bulletg);
getPhysicsSpace().add(bulletControl);
}
public void collision(PhysicsCollisionEvent event) {
if (event.getObjectA() instanceof BombControl) {
final Spatial node = event.getNodeA();
effect.killAllParticles();
effect.setLocalTranslation(node.getLocalTranslation());
effect.emitAllParticles();
} else if (event.getObjectB() instanceof BombControl) {
final Spatial node = event.getNodeB();
effect.killAllParticles();
effect.setLocalTranslation(node.getLocalTranslation());
effect.emitAllParticles();
}
}
public void onAnimCycleDone(AnimControl control, AnimChannel channel,
String animName) {
if (channel == shootingChannel) {
channel.setAnim("stand");
}
}
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();
}
}
It appears this is due to this line of code:
CapsuleCollisionShape capsule = new CapsuleCollisionShape(3f, 4f);
and then you are scaling down the model. I dont know what the values passed in are, but i suppose one is a radius and the other one is a height, try moving down that 3f and that 4f to something lower, like 1.0f and 2.0f.
It seems the collision detection is pushing your character upwards because its collision shape is a capsule, which is of a different size than the model, and the model is placed in the middle of the capsule.
I'm making a small 3D scene and the character can walk by pressing the WASD buttons but the animations that are playing are not correct and the character turns around so it looks like he is moonwalking when moving.
The control code for the buttons are
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_RETURN));
inputManager
.addMapping("CharShoot", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addListener(this, "CharLeft");
inputManager.addListener(this, "CharRight");
inputManager.addListener(this, "CharUp");
inputManager.addListener(this, "CharDown");
inputManager.addListener(this, "CharSpace");
inputManager.addListener(this, "CharShoot");
}
And the update loop looks like this
public void simpleUpdate(float tpf) {
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 (!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);
if (airTime > .3f) {
if (!"stand".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("stand");
}
} else if (!"Walk".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("Walk", 0.7f);
}
}
character.setWalkDirection(walkDirection);
}
I don't understand what is wrong since it is actually so close to working, all I need is that the character doesn't turns around and plays the correct animation when walking. Do you have any idea what the problem might be?
The entire program is
package adventure;
import java.applet.Applet;
import com.jme3.math.Quaternion;
import com.jme3.math.FastMath;
import java.applet.AudioClip;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.TextArea;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import com.jme3.material.RenderState.FaceCullMode;
import javax.swing.JFrame;
import javax.swing.JPanel;
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.TextureKey;
import com.jme3.asset.plugins.ZipLocator;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.effect.shapes.EmitterSphereShape;
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.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.scene.shape.Sphere.TextureMode;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeCanvasContext;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.SkyFactory;
public class MountainWorld extends SimpleApplication implements ActionListener,
PhysicsCollisionListener, AnimEventListener, Playable {
/** Prepare Materials */
Material wall_mat;
Material stone_mat;
Material floor_mat;
/** Prepare geometries and physical nodes for bricks and cannon balls. */
private RigidBodyControl brick_phy;
private static final Box box;
private RigidBodyControl ball_phy;
private static final Sphere sphere;
private RigidBodyControl floor_phy;
private static final Box floor;
/** dimensions used for bricks and wall */
private static final float brickLength = 0.48f;
private static final float brickWidth = 0.24f;
private static final float brickHeight = 0.12f;
static {
/** Initialize the cannon ball geometry */
sphere = new Sphere(32, 32, 0.4f, true, false);
sphere.setTextureMode(TextureMode.Projected);
/** Initialize the brick geometry */
box = new Box(Vector3f.ZERO, brickLength, brickHeight, brickWidth);
box.scaleTextureCoordinates(new Vector2f(1f, .5f));
/** Initialize the floor geometry */
floor = new Box(Vector3f.ZERO, 100f, 0.1f, 50f);
floor.scaleTextureCoordinates(new Vector2f(3, 6));
}
private static World world;
private static Person person;
private static Player dplayer;
private static TextArea textarea;
private BulletAppState bulletAppState;
private AnimChannel channel;
private AnimControl control;
// character
CharacterControl character;
Node model;
// temp vectors
Vector3f walkDirection = new Vector3f();
// terrain
TerrainQuad terrain;
RigidBodyControl terrainPhysicsNode;
// Materials
Material matRock;
Material matBullet;
// animation
AnimChannel animationChannel;
AnimChannel shootingChannel;
AnimControl animationControl;
float airTime = 0;
// camera
boolean left = false, right = false, up = false, down = false;
ChaseCamera chaseCam;
// bullet
Sphere bullet;
SphereCollisionShape bulletCollisionShape;
// explosion
ParticleEmitter effect;
// brick wall
Box brick;
float bLength = 0.8f;
float bWidth = 0.4f;
float bHeight = 0.4f;
FilterPostProcessor fpp;
private Spatial sceneModel;
private RigidBodyControl landscape;
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
AppSettings settings = new AppSettings(true);
settings.setWidth(850);
settings.setHeight(440);
MountainWorld canvasApplication = new MountainWorld();
canvasApplication.setSettings(settings);
canvasApplication.createCanvas(); // create canvas!
JmeCanvasContext ctx = (JmeCanvasContext) canvasApplication
.getContext();
ctx.setSystemListener(canvasApplication);
Dimension dim = new Dimension(640, 480);
ctx.getCanvas().setPreferredSize(dim);
JFrame window = new JFrame("Mountain World");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout()); // a panel
world = new DungeonWorld(canvasApplication);
person = new Person(world, "You", null);
dplayer = new Player(world, person);
Commands commands = new Commands(person);
textarea = new TextArea("", 10, 60,
TextArea.SCROLLBARS_VERTICAL_ONLY);
textarea.append("You are in a mountain. The trolls live here.\n");
textarea.setEditable(false);
panel.add("West", ctx.getCanvas());
panel.add("East", commands);
panel.add("South", textarea);
window.add(panel);
window.pack();
window.setVisible(true);
canvasApplication.startCanvas();
}
});
}
#Override
public void simpleInitApp() {
bulletAppState = new BulletAppState();
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
stateManager.attach(bulletAppState);
setupKeys();
//prepareBullet();
//prepareEffect();
createLight();
//createSky();
initMaterials();
initFloor();
//createTerrain();
//createWall();
createCharacters();
setupChaseCamera();
setupAnimationController();
setupFilter();
}
/** Make a solid floor and add it to the scene. */
public void initFloor() {
Geometry floor_geo = new Geometry("Floor", floor);
floor_geo.setMaterial(floor_mat);
floor_geo.setLocalTranslation(0, -0.1f, 0);
this.rootNode.attachChild(floor_geo);
/* Make the floor physical with mass 0.0f! */
floor_phy = new RigidBodyControl(0.0f);
floor_geo.addControl(floor_phy);
bulletAppState.getPhysicsSpace().add(floor_phy);
}
/** Initialize the materials used in this scene. */
public void initMaterials() {
wall_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg");
key.setGenerateMips(true);
Texture tex = assetManager.loadTexture(key);
wall_mat.setTexture("ColorMap", tex);
stone_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
key2.setGenerateMips(true);
Texture tex2 = assetManager.loadTexture(key2);
stone_mat.setTexture("ColorMap", tex2);
floor_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg");
key3.setGenerateMips(true);
Texture tex3 = assetManager.loadTexture(key3);
tex3.setWrap(WrapMode.Repeat);
floor_mat.setTexture("ColorMap", tex3);
}
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_RETURN));
inputManager
.addMapping("CharShoot", new KeyTrigger(KeyInput.KEY_SPACE));
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 createWall() {
float xOff = -144;
float zOff = -40;
float startpt = bLength / 4 - xOff;
float height = 6.1f;
brick = new Box(Vector3f.ZERO, bLength, bHeight, bWidth);
brick.scaleTextureCoordinates(new Vector2f(1f, .5f));
for (int j = 0; j < 15; j++) {
for (int i = 0; i < 4; i++) {
Vector3f vt = new Vector3f(i * bLength * 2 + startpt, bHeight
+ height, zOff);
addBrick(vt);
}
startpt = -startpt;
height += 1.01f * bHeight;
}
}
private void addBrick(Vector3f ori) {
Geometry reBoxg = new Geometry("brick", brick);
reBoxg.setMaterial(matBullet);
reBoxg.setLocalTranslation(ori);
reBoxg.addControl(new RigidBodyControl(1.5f));
reBoxg.setShadowMode(ShadowMode.CastAndReceive);
this.rootNode.attachChild(reBoxg);
this.getPhysicsSpace().add(reBoxg);
}
private void prepareBullet() {
bullet = new Sphere(32, 32, 0.4f, true, false);
bullet.setTextureMode(TextureMode.Projected);
bulletCollisionShape = new SphereCollisionShape(0.4f);
matBullet = new Material(getAssetManager(),
"Common/MatDefs/Misc/Unshaded.j3md");
matBullet.setColor("Color", ColorRGBA.Green);
// matBullet.setColor("m_GlowColor", ColorRGBA.Green);
getPhysicsSpace().addCollisionListener(this);
}
private void prepareEffect() {
int COUNT_FACTOR = 1;
float COUNT_FACTOR_F = 1f;
effect = new ParticleEmitter("Flame", Type.Triangle, 32 * COUNT_FACTOR);
effect.setSelectRandomImage(true);
effect.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f,
(float) (1f / COUNT_FACTOR_F)));
effect.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
effect.setStartSize(1.3f);
effect.setEndSize(2f);
effect.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
effect.setParticlesPerSec(0);
effect.setGravity(0, -5, 0);
effect.setLowLife(.4f);
effect.setHighLife(.5f);
effect.setInitialVelocity(new Vector3f(0, 7, 0));
effect.setVelocityVariation(1f);
effect.setImagesX(2);
effect.setImagesY(2);
Material mat = new Material(assetManager,
"Common/MatDefs/Misc/Particle.j3md");
mat.setTexture("Texture",
assetManager.loadTexture("Effects/Explosion/flame.png"));
effect.setMaterial(mat);
// effect.setLocalScale(100);
rootNode.attachChild(effect);
}
private void createLight() {
Vector3f direction = new Vector3f(-0.1f, -0.7f, -1).normalizeLocal();
DirectionalLight dl = new DirectionalLight();
dl.setDirection(direction);
dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
rootNode.addLight(dl);
}
private void createSky() {
rootNode.attachChild(SkyFactory.createSky(assetManager,
"Textures/Sky/Bright/BrightSky.dds", false));
}
private void createTerrain2() {
matRock = new Material(assetManager,
"Common/MatDefs/Terrain/TerrainLighting.j3md");
matRock.setBoolean("useTriPlanarMapping", false);
matRock.setBoolean("WardIso", true);
matRock.setTexture("AlphaMap",
assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
Texture heightMapImage = assetManager
.loadTexture("Textures/Terrain/splat/mountains512.png");
Texture grass = assetManager
.loadTexture("Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap", grass);
matRock.setFloat("DiffuseMap_0_scale", 64);
Texture dirt = assetManager
.loadTexture("Textures/Terrain/splat/dirt.jpg");
dirt.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_1", dirt);
matRock.setFloat("DiffuseMap_1_scale", 16);
Texture rock = assetManager
.loadTexture("Textures/Terrain/splat/road.jpg");
rock.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_2", rock);
matRock.setFloat("DiffuseMap_2_scale", 128);
Texture normalMap0 = assetManager
.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
normalMap0.setWrap(WrapMode.Repeat);
Texture normalMap1 = assetManager
.loadTexture("Textures/Terrain/splat/dirt_normal.png");
normalMap1.setWrap(WrapMode.Repeat);
Texture normalMap2 = assetManager
.loadTexture("Textures/Terrain/splat/road_normal.png");
normalMap2.setWrap(WrapMode.Repeat);
matRock.setTexture("NormalMap", normalMap0);
matRock.setTexture("NormalMap_1", normalMap2);
matRock.setTexture("NormalMap_2", normalMap2);
AbstractHeightMap heightmap = null;
try {
heightmap = new ImageBasedHeightMap(heightMapImage.getImage(),
0.25f);
heightmap.load();
} catch (Exception e) {
e.printStackTrace();
}
terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
TerrainLodControl control = new TerrainLodControl(terrain, cameras);
terrain.addControl(control);
terrain.setMaterial(matRock);
terrain.setLocalScale(new Vector3f(2, 2, 2));
terrainPhysicsNode = new RigidBodyControl(
CollisionShapeFactory.createMeshShape(terrain), 0);
terrain.addControl(terrainPhysicsNode);
rootNode.attachChild(terrain);
getPhysicsSpace().add(terrainPhysicsNode);
}
private void createTerrain() {
assetManager.registerLocator("town.zip", ZipLocator.class);
sceneModel = assetManager.loadModel("main.scene");
//sceneModel = assetManager.loadModel("Scenes/ManyLights/Main.scene");
sceneModel.setLocalScale(2f);
//initFloor();
// We set up collision detection for the scene by creating a
// compound collision shape and a static RigidBodyControl with mass
// zero.
CollisionShape sceneShape = CollisionShapeFactory
.createMeshShape((Node) sceneModel);
landscape = new RigidBodyControl(sceneShape, 0);
sceneModel.addControl(landscape);
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
rootNode.attachChild(sceneModel);
}
private void createCharacters() {
CapsuleCollisionShape capsule = new CapsuleCollisionShape(0.0f, 0.0f);
character = new CharacterControl(capsule, 0.01f);
model = (Node) assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
float scale = 0.25f;
model.scale(0.05f, 0.05f, 0.05f);
model.addControl(character);
character.setPhysicsLocation(new Vector3f(0, 0f, 0));
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(-140, 12.5f, -10));
// man.setShadowMode(ShadowMode.CastAndReceive);
//rootNode.attachChild(man);
}
private void setupChaseCamera() {
flyCam.setEnabled(false);
chaseCam = new ChaseCamera(cam, model, inputManager);
}
private void setupAnimationController() {
animationControl = model.getControl(AnimControl.class);
animationControl.addListener(this);
animationChannel = animationControl.createChannel();
// shootingChannel = animationControl.createChannel();
// shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "uparm.right"));
// shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "arm.right"));
// shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "hand.right"));
}
#Override
public void simpleUpdate(float tpf) {
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 (!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);
if (airTime > .3f) {
if (!"stand".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("stand");
}
} else if (!"Walk".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("Walk", 0.7f);
}
}
character.setWalkDirection(walkDirection);
}
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();
}
}
private void bulletControl() {
shootingChannel.setAnim("Dodge", 0.1f);
shootingChannel.setLoopMode(LoopMode.DontLoop);
Geometry bulletg = new Geometry("bullet", bullet);
bulletg.setMaterial(matBullet);
bulletg.setShadowMode(ShadowMode.CastAndReceive);
bulletg.setLocalTranslation(character.getPhysicsLocation().add(
cam.getDirection().mult(5)));
RigidBodyControl bulletControl = new BombControl(bulletCollisionShape,
1);
bulletControl.setCcdMotionThreshold(0.1f);
bulletControl.setLinearVelocity(cam.getDirection().mult(80));
bulletg.addControl(bulletControl);
rootNode.attachChild(bulletg);
getPhysicsSpace().add(bulletControl);
}
public void collision(PhysicsCollisionEvent event) {
if (event.getObjectA() instanceof BombControl) {
final Spatial node = event.getNodeA();
effect.killAllParticles();
effect.setLocalTranslation(node.getLocalTranslation());
effect.emitAllParticles();
} else if (event.getObjectB() instanceof BombControl) {
final Spatial node = event.getNodeB();
effect.killAllParticles();
effect.setLocalTranslation(node.getLocalTranslation());
effect.emitAllParticles();
}
}
public void onAnimCycleDone(AnimControl control, AnimChannel channel,
String animName) {
if (channel == shootingChannel) {
channel.setAnim("stand");
}
}
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();
}
}
Update
I changed one line of code to include the transform I had to do at the beginning:
character.setViewDirection(walkDirection.add(new Vector3f(1, 0, 0)));
i.e. I added the Vector3f(1, 0, 0) and that seems to have helped, now my character can move forward and backward seemingly perfectly.
But it is still not perfect when moving sideways although the "moonwalking" disappeared and maybe you can confirm that I did the right thing using the add method of the vector?
Update 2
When changing the vector to negate the Ninja is starting to move like he should!
character.setViewDirection(walkDirection.negate());
e.g. in the update code
public void simpleUpdate(float tpf) {
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 (!character.onGround()) {
airTime = airTime + tpf;
} else {
airTime = 0;
}
if (walkDirection.length() == 0) {
if (!"Idle1".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("Idle1", 1f);
}
} else {
System.out.println("setting walk direction " + walkDirection);
character.setViewDirection(walkDirection.negate());
if (airTime > .3f) {
if (!"stand".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("stand");
}
} else if (!"Walk".equals(animationChannel.getAnimationName())) {
System.out.println("in walk Walk ");
animationChannel.setAnim("Walk", 0.7f);
}
}
character.setWalkDirection(walkDirection);
}
Now a remaining problem is to make the char jump and move over stairs.
I'm no animation expert but if you move forward while playing the walk animation it should look ok. For better results you might need some transitions between antimations (e.g. stand, walk, turn) which can either be separate animations or maybe could be achieved by using animation blending.
Nevertheless, it's hard to unterstand what actually is your problem, since the description is quite short and we can't see animation problems from looking at a bunch of code.
Besides that, if your problem is how to correctly configure JMonkeyEngine you might be better off in their forum. Or at https://gamedev.stackexchange.com/.