The Jmoneky Engine gives example code for endless randomly generated Terrain. My Problem is, the code has no comments or indicators to edit the View distance. I am trying to use this example code to build a game, but it looks really bad if the render distance is so short you can see the bottom of the world(void)
The Code:
public class TerrainFractalGridTest extends SimpleApplication {
private Material mat_terrain;
private TerrainGrid terrain;
private float grassScale = 64;
private float dirtScale = 16;
private float rockScale = 128;
public static void main(final String[] args) {
TerrainFractalGridTest app = new TerrainFractalGridTest();
app.start();
}
private CharacterControl player3;
private FractalSum base;
private PerturbFilter perturb;
private OptimizedErode therm;
private SmoothFilter smooth;
private IterativeFilter iterate;
#Override
public void simpleInitApp() {
this.flyCam.setMoveSpeed(100f);
ScreenshotAppState state = new ScreenshotAppState();
this.stateManager.attach(state);
// TERRAIN TEXTURE material
this.mat_terrain = new Material(this.assetManager, "Common/MatDefs/Terrain/HeightBasedTerrain.j3md");
// Parameters to material:
// regionXColorMap: X = 1..4 the texture that should be appliad to state X
// regionX: a Vector3f containing the following information:
// regionX.x: the start height of the region
// regionX.y: the end height of the region
// regionX.z: the texture scale for the region
// it might not be the most elegant way for storing these 3 values, but it packs the data nicely :)
// slopeColorMap: the texture to be used for cliffs, and steep mountain sites
// slopeTileFactor: the texture scale for slopes
// terrainSize: the total size of the terrain (used for scaling the texture)
// GRASS texture
Texture grass = this.assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat);
this.mat_terrain.setTexture("region1ColorMap", grass);
this.mat_terrain.setVector3("region1", new Vector3f(15, 200, this.grassScale));
// DIRT texture
Texture dirt = this.assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
dirt.setWrap(WrapMode.Repeat);
this.mat_terrain.setTexture("region2ColorMap", dirt);
this.mat_terrain.setVector3("region2", new Vector3f(0, 20, this.dirtScale));
// ROCK texture
Texture rock = this.assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg");
rock.setWrap(WrapMode.Repeat);
this.mat_terrain.setTexture("region3ColorMap", rock);
this.mat_terrain.setVector3("region3", new Vector3f(198, 260, this.rockScale));
this.mat_terrain.setTexture("region4ColorMap", rock);
this.mat_terrain.setVector3("region4", new Vector3f(198, 260, this.rockScale));
this.mat_terrain.setTexture("slopeColorMap", rock);
this.mat_terrain.setFloat("slopeTileFactor", 32);
this.mat_terrain.setFloat("terrainSize", 513);
this.base = new FractalSum();
this.base.setRoughness(0.7f);
this.base.setFrequency(1.0f);
this.base.setAmplitude(1.0f);
this.base.setLacunarity(2.12f);
this.base.setOctaves(8);
this.base.setScale(0.02125f);
this.base.addModulator(new NoiseModulator() {
#Override
public float value(float... in) {
return ShaderUtils.clamp(in[0] * 0.5f + 0.5f, 0, 1);
}
});
FilteredBasis ground = new FilteredBasis(this.base);
this.perturb = new PerturbFilter();
this.perturb.setMagnitude(0.119f);
this.therm = new OptimizedErode();
this.therm.setRadius(5);
this.therm.setTalus(0.011f);
this.smooth = new SmoothFilter();
this.smooth.setRadius(1);
this.smooth.setEffect(0.7f);
this.iterate = new IterativeFilter();
this.iterate.addPreFilter(this.perturb);
this.iterate.addPostFilter(this.smooth);
this.iterate.setFilter(this.therm);
this.iterate.setIterations(1);
ground.addPreFilter(this.iterate);
this.terrain = new TerrainGrid("terrain", 33, 129, new FractalTileLoader(ground, 256f));
this.terrain.setMaterial(this.mat_terrain);
this.terrain.setLocalTranslation(0, 0, 0);
this.terrain.setLocalScale(2f, 1f, 2f);
this.rootNode.attachChild(this.terrain);
TerrainLodControl control = new TerrainGridLodControl(this.terrain, this.getCamera());
control.setLodCalculator(new DistanceLodCalculator(33, 2.7f)); // patch size, and a multiplier
this.terrain.addControl(control);
this.getCamera().setLocation(new Vector3f(0, 300, 0));
this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
}
#Override
public void simpleUpdate(final float tpf) {
}}
So following what J Atkin said about Terrain Grid, I found a endless terrain example that is a terrain grid. The Cells loaded class is protected, which means I have to extends it in the class to access it. In Jmonkey, the main class has to extend a simple application in order to run. Java doesn't allow multiple extensions, therefor I build a second class to allow access.
public class ViewTerrain extends TerrainGrid{
public void setView(int numberofcells){
super.cellsLoaded = numberofcells;
}
}
problem I am having with this class is that I don't know how to keep the original declaration IE.
this.terrain = new TerrainGrid("terrain", 65, 257, new ImageTileLoader(assetManager, new Namer() {
public String getName(int x, int y) {
return "Scenes/TerrainMountains/terrain_" + x + "_" + y + ".png";
}
}));
Looking at the source it seems TerrainGrid dynamically redefines an internal TerrainQuad tree based upon which grid the camera is in and the surrounding grid tiles. It would seem to me that you should define these tiles to be the size of the area you would like visible at any one time. Try updating patchSize in the constructor to be larger.
Related
So I'm trying to create a game and it's my first time. My game is a 2D side scroller and is about the player in space avoiding the incoming meteors. I have successfully managed to get the meteors spawning randomly on the x and y axis off screen and re position once it has gone pass the screen.
But the problem I face now is sometimes the spawn of the meteors will clump together which I don't want. How do I get the meteors to spawn at a certain distance from each other so they don't clump together. I couldn't find any good tutorials or if anyone can point me to the right direction. Below are my codes so far.
Meteor Class
public class Meteors {
private Texture bigMeteor;
private Vector2 posBigMeteor;
private Random yrand;
//Constructor
public Meteors(float x){
bigMeteor = new Texture("meteor.png");
yrand = new Random();
//Spawn location of meteor
posBigMeteor = new Vector2(x, yrand.nextInt(AstroDemo.HEIGHT/2 - bigMeteor.getHeight()));
}
public Texture getBigMeteor() {
return bigMeteor;
}
public Vector2 getPosBigMeteor() {
return posBigMeteor;
}
//Reposition the meteors
public void reposition(float x){
posBigMeteor.set(x, yrand.nextInt(AstroDemo.HEIGHT/2 - bigMeteor.getHeight()));
}
}
PlayState Class
public class PlayState extends State {
//Total meteor count on screen
private static final int METEOR_COUNT = 8;
private Naught naught;
private Texture bg;
private Random xrand;
private Array <Meteors> meteors;
public PlayState(GameStateManager gsm) {
super(gsm);
//Starting co-ordinates of main character (Naught)
naught = new Naught(50, 100);
//Setting viewport of the camera
cam.setToOrtho(false, AstroDemo.WIDTH/2, AstroDemo.HEIGHT/2);
bg = new Texture("bg.png");
xrand = new Random();
meteors = new Array <Meteors>();
//Spawn meteors randomly off screen
for (int i = 1; i <= METEOR_COUNT; i++){
meteors.add(new Meteors(AstroDemo.WIDTH/2 + (xrand.nextInt(300))));
}
}
#Override
protected void handleInput() {
//If screen/mouse is held
if(Gdx.input.isTouched()){
//Main Character jumps/flys
naught.jump();
}
}
#Override
public void update(float dt) {
handleInput();
naught.update(dt);
//If meteors are left side of the screen, re-position to the right side of the screen
for(Meteors meteor : meteors){
if (cam.position.x - (cam.viewportWidth/2) > meteor.getPosBigMeteor().x + meteor.getBigMeteor().getWidth()){
meteor.reposition(meteor.getPosBigMeteor().x + (AstroDemo.WIDTH/2 + 20 + (xrand.nextInt(300))));
}
}
cam.position.x = naught.getPosition().x + 80;
cam.update();
}
#Override
public void render(SpriteBatch sb) {
//Adjust the spritebatch for co-ordinate system in relation to camera
sb.setProjectionMatrix(cam.combined);
sb.begin();
//Draw background where the camera is
sb.draw(bg, cam.position.x - (cam.viewportWidth/2), 0);
sb.draw(naught.getTexture(), naught.getPosition().x, naught.getPosition().y);
for (Meteors meteor : meteors) {
sb.draw(meteor.getBigMeteor(), meteor.getPosBigMeteor().x, meteor.getPosBigMeteor().y);
}
sb.end();
}
#Override
public void dispose() {
}
}
create a array of predefined value for your y position and then get value randomly from that array.
or
Divide height into sub portion then get random value from that portion so that each random value not collide with other value.
I've been following along with a LibGDX tutorial on Youtube and have come across an issue rendering a TiledMap to my screen. At the moment, I can render some labels/images using my HUD java class
public class HUD {
public Stage stage; //the background
private Viewport viewport; //so the HUD stays fixed and the world can move
private int score;
private int timer;
private int bactoCount;
private Texture foodTexture, antioBioBottle;
//these are widgets
Label antioBioTxt;
Image antiBioImg;
Label countDown;
Label countTxt;
Image foodImg;
Label foodTxt;
Label bactoCountLabel;
Label bactoTxt;
//these images need to be buttons...
public HUD (SpriteBatch sb) {
foodTexture = new Texture("Bacto food.png");
antioBioBottle = new Texture("Antibioticbottle.png");
bactoCount = 0;
timer = 0;
viewport = new FitViewport(BactoBuds.V_WIDTH, BactoBuds.V_HEIGHT, new OrthographicCamera());
stage = new Stage(viewport, sb);
//stage = new Stage();
//use a Table to organise widgets on the stage
Table table = new Table();
table.top();
table.setFillParent(true); //the table is the size of our stage
antioBioTxt = new Label("Antibiotic", new Label.LabelStyle(new BitmapFont(), Color.WHITE));
antiBioImg = new Image(antioBioBottle);
// %03d means its 3 digits long
//bitmap font sets the font to bit style
//string.format for changing from a string to a int
countDown = new Label(String.format("%03d", timer), new Label.LabelStyle(new BitmapFont(), Color.WHITE));
countTxt = new Label("Time to flood:", new Label.LabelStyle(new BitmapFont(), Color.WHITE));
foodImg = new Image(foodTexture);
foodTxt = new Label("Food", new Label.LabelStyle(new BitmapFont(), Color.WHITE));
bactoCountLabel = new Label(String.format("%06d", bactoCount), new Label.LabelStyle(new BitmapFont(), Color.WHITE));
bactoTxt = new Label("Bacteria:", new Label.LabelStyle(new BitmapFont(), Color.WHITE));
//if multiple labels use expandX then they all share an equal portion of the screen
table.add(antioBioTxt).expandX().padTop(10);
table.add(foodTxt).expandX().padTop(10);
table.add(bactoTxt).expandX().padTop(10);
table.add(countTxt).expandX().padTop(10);
table.row();
table.add(antiBioImg).expandX();
table.add(foodImg).expandX();
table.add(bactoCountLabel).expandX().align(Align.center);
table.add(countDown).expandX().align(Align.center);
stage.addActor(table);
}
}
These render nicely on the screen. However, when I try to render up a background TMX map image, it renders in the wrong location. I've been messing with the code for a few days now, trying to change the position of the map position. At the first instance I was only able to see a tiny corner of the map and now I've gotten the whole thing to render, but it only takes up ~1/4 of the screen. Now I am at a loss as to how to proceed.
public class playScreen implements Screen{
private BactoBuds game;
private OrthographicCamera gamecamera;
private Viewport gameView;
private HUD HUD;
private TmxMapLoader mapLoader; //loads map to screen
private TiledMap map; //reference to map
private OrthogonalTiledMapRenderer renderer;
public playScreen (BactoBuds game) {
this.game = game;
gamecamera = new OrthographicCamera();
gameView = new FitViewport(BactoBuds.V_WIDTH, BactoBuds.V_HEIGHT, gamecamera);
HUD = new HUD(game.batch);
mapLoader = new TmxMapLoader(); //make a new map loader, set map to maploader, then pass it to the renderer
map = mapLoader.load("grassy.tmx");
renderer = new OrthogonalTiledMapRenderer(map);
gamecamera.setToOrtho(false);
gamecamera.position.set(gameView.getWorldWidth() / 2, gameView.getWorldHeight() / 2, 0); //this changes map position
renderer.setView(gamecamera);
}
#Override
public void show() {
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
renderer.render();
game.batch.setProjectionMatrix(HUD.stage.getCamera().combined);
HUD.stage.draw();
}
Please forgive the dodgy coding, I'm quite new and I'm still learning about good practices. I think it has something to do with the camera positioning, but swapping out the values doesn't appear to change anything.
public class BactoBuds extends Game {
public static final int V_WIDTH = 800;
public static final int V_HEIGHT = 480;
public static final String Title = "BactoBuds";
public SpriteBatch batch; //public to let other screens have access to images
private Texture img;
private Game game;
private Screen screen;
private OrthographicCamera camera;
public BactoBuds () {
game = this;
}
#Override
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
// change this to menuScreen later
setScreen(new playScreen(this));
}
public void dispose () {
batch.dispose();
img.dispose();
}
#Override
public void render () {
super.render();
}
public void resume (){
}
public void pause () {
}
}
Thanks for the help!
You need to call camera.update() within render method.
Have you tried moving your camera? If only 1/4 of the map is visible your camera might be centered at 0,0 while your map uses that as the bottom left origin of the texture.
Try camera.position.set(BactoBuds.V_WIDTH / 2, BactoBuds.V_HEIGHT / 2, 0);
I'm making a clone of old Super Mario and I am trying to shake the brick tile when mario in his little form hits it from below. The concept is taken from a great libgdx tutorial by Brent Aureli and everything he wrote/shown works, however when I made this (seemingly small) modification, the modification itself behaves strange.
Namely, when little mario hits the tile (any brick tile) ALL tiles of this kind get this offset. From my understanding, the access is getCell().getTile().setOffsetY(value) which should mean
get a single cell -> get tile in that cell -> set offset to this one tile that is in the given cell.
Documentation also suggests it should work like that:
https://libgdx.badlogicgames.com/nightlies/docs/api/
replacing tile with null when big mario hits the tile works as intended, so I don't understand why the approach with getCell.getTile doesn't.
Finally, I'm aware that cells are immobile, all I want is a purely cosmetic shift of the actual texture for half a second or so - and that is possible, but in the current state it offsets ALL similar tiles.
For the fun of it I
public class Brick extends InteractiveTile {
private static TiledMapTileSet tileSet;
public Brick(PlayScreen screen, MapObject object) {
super(screen, object);
tileSet = screen.getMap().getTileSets().getTileSet("tileset_gutter");
fixture.setUserData(this);
setCategoryFilter(SuperMario.BRICK_BIT);
}
#Override
public void onHeadHit(Mario mario) {
if(mario.isBig()){
SuperMario.manager.get("audio/breakblock.wav", Sound.class).play();
setCategoryFilter(SuperMario.DESTROYED_BIT);
getCell().setTile(null);
}
else {
SuperMario.manager.get("audio/bump.wav", Sound.class).play();
// DOES THE EXACT SAME AS LATER 3 LINES
// getCell().getTile().setOffsetY(2);
getCell().setTile(tileSet.getTile(28)); //this can change texture of individual tile.
TiledMapTile tile = getCell().getTile();
tile.setOffsetY(2);
getCell().setTile(tile);
}
}
}
InteractiveTile:
public abstract class InteractiveTile {
private World world;
private TiledMap map;
private TiledMapTile tile;
private Rectangle bounds;
protected Body body;
protected final Fixture fixture;
protected PlayScreen screen;
protected MapObject object;
public InteractiveTile(PlayScreen screen, MapObject object){
this.screen = screen;
this.world = screen.getWorld();
this.map = screen.getMap();
this.object = object;
this.bounds = ((RectangleMapObject)object).getRectangle();
BodyDef bdef = new BodyDef();
FixtureDef fdef = new FixtureDef();
PolygonShape shape = new PolygonShape();
bdef.type = BodyDef.BodyType.StaticBody;
bdef.position.set(
(bounds.getX()+ bounds.getWidth()/2)/ SuperMario.PPM,
(bounds.getY() + bounds.getHeight()/2)/SuperMario.PPM);
body = world.createBody(bdef);
shape.setAsBox(
(bounds.getWidth()/2)/SuperMario.PPM,
(bounds.getHeight()/2)/SuperMario.PPM); fdef.shape = shape;
fixture = body.createFixture(fdef);
}
public abstract void onHeadHit(Mario mario);
public TiledMapTileLayer.Cell getCell(){
TiledMapTileLayer layer = (TiledMapTileLayer) map.getLayers().get(1);
return layer.getCell(
//REVERT SCALING (PPM) AND by tile size
(int)(body.getPosition().x * SuperMario.PPM / 16),
(int)(body.getPosition().y * SuperMario.PPM / 16));
}
public void setCategoryFilter(short filterBit){
Filter filter = new Filter();
filter.categoryBits = filterBit;
fixture.setFilterData(filter);
}
}
is there an error in this code, or it is a limitation of the engine?
PS - the original (unmodified)files can be found on Brent Aurelis github site:
https://github.com/BrentAureli/SuperMario
I am trying to learn LibGdx and simulate a Spring Mass connection. I am able to define everything correctly and run the simulation but the spring defined seems to have an inherent damping dur to which the oscillations of the dynamic body die out.
I have set the damping ratio to zero but still get the decay.
Can some one explain if this is a bug or am i doing something wrong.
Below are my codes.
SpringMass class:
public class SpringMass implements Screen {
private void createScene() {
trackers = new ArrayList<Tracker>();
isTracking = new ArrayList<Boolean>();
world.setGravity(new Vector2(0, 0));
Vector2 boxPos = new Vector2(200, 200);
staticBox = new Box(world, boxPos, 10, 10, false);
dynBox = new Box(world, boxPos.add(150, 0), 10, 10, true);
new Spring(staticBox.body, dynBox.body, world, 100, 0.8f, 0);
}
}
Spring Class:
public class Spring {
private DistanceJointDef distanceJointDef;
public Spring(Body bodyA, Body bodyB, World world, float length, float stiffness, float dampingRatio) {
distanceJointDef = new DistanceJointDef();
distanceJointDef.bodyA = bodyA;
distanceJointDef.bodyB = bodyB;
distanceJointDef.length = length*CONSTANTS.WORLD_TO_BOX;
distanceJointDef.frequencyHz = stiffness;
distanceJointDef.dampingRatio=dampingRatio;
world.createJoint(distanceJointDef);
}
}
I am learning JME3 and I managed to create my own height map and modifying some of the example code, etc. Now, I created a very simple 4-wall roofless room with Blender, exported it as a Wavefront .Obj file and loaded it unto my scene (I attacked it to the terrain node.
Now, my terrain has a collision detection applied so the player can move and jump around, but it can also walk right through the walls of my model. All the examples I can find loads an already pre-built scene, and I'm still clueless as to why the player goes right through the loaded model?
Sorry for the big code, but I couldn't see how else I could do otherwise. The physics is applied at the section /** 6. Add physics: */ :
public class Main extends SimpleApplication
implements ActionListener {
private BulletAppState bulletAppState;
private RigidBodyControl landscape;
private CharacterControl player;
private Vector3f walkDirection = new Vector3f();
private boolean left = false, right = false, up = false, down = false;
private TerrainQuad terrain;
private Material mat_terrain;
public static void main(String[] args) {
AppSettings settings = new AppSettings(true);
settings.setResolution(1366, 768);
settings.setFullscreen(true);
Main app = new Main();
app.setSettings(settings);
app.setShowSettings(false);
app.start();
}
#Override
public void simpleInitApp() {
/** Set up Physics */
bulletAppState = new BulletAppState();
stateManager.attach(bulletAppState);
//bulletAppState.getPhysicsSpace().enableDebug(assetManager);
flyCam.setMoveSpeed(200);
setUpKeys();
/** 1. Create terrain material and load four textures into it. */
mat_terrain = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
/** 1.1) Add ALPHA map (for red-blue-green coded splat textures) */
mat_terrain.setTexture("Alpha", assetManager.loadTexture("Textures/terrain/island_1_alpha1.png"));
/** 1.2) Add GRASS texture into the red layer (Tex1). */
Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat);
mat_terrain.setTexture("Tex1", grass);
mat_terrain.setFloat("Tex1Scale", 64f);
/** 1.3) Add DIRT texture into the green layer (Tex2) */
Texture dirt = assetManager.loadTexture("Textures/rocks.jpg");
dirt.setWrap(WrapMode.Repeat);
mat_terrain.setTexture("Tex2", dirt);
mat_terrain.setFloat("Tex2Scale", 32f);
/** 1.4) Add ROAD texture into the blue layer (Tex3) */
Texture rock = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
rock.setWrap(WrapMode.Repeat);
mat_terrain.setTexture("Tex3", rock);
mat_terrain.setFloat("Tex3Scale", 128f);
/** 2. Create the height map */
AbstractHeightMap heightmap = null;
Texture heightMapImage = assetManager.loadTexture("Textures/terrain/island_1.png");
heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
heightmap.load();
/** 3. We have prepared material and heightmap.
* Now we create the actual terrain:
* 3.1) Create a TerrainQuad and name it "my terrain".
* 3.2) A good value for terrain tiles is 64x64 -- so we supply 64+1=65.
* 3.3) We prepared a heightmap of size 512x512 -- so we supply 512+1=513.
* 3.4) As LOD step scale we supply Vector3f(1,1,1).
* 3.5) We supply the prepared heightmap itself.
*/
terrain = new TerrainQuad("my terrain", 65, 513, heightmap.getHeightMap());
/** 4. We give the terrain its material, position & scale it, and attach it. */
terrain.setMaterial(mat_terrain);
terrain.setLocalTranslation(0, -170, 0);
terrain.setLocalScale(2f, 1f, 2f);
rootNode.attachChild(terrain);
/** 4.5. Load some models */
Spatial building = assetManager.loadModel("Models/building1.obj");
Material mat_default = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
building.setMaterial(mat_default);
building.setLocalTranslation(90, 117, 90);
building.setLocalScale(5f, 5f, 5f);
terrain.attachChild(building);
/** 4.6. Load Sky */
rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
/** 4.7. Load water */
// we create a water processor
SimpleWaterProcessor waterProcessor = new SimpleWaterProcessor(assetManager);
waterProcessor.setReflectionScene(rootNode);
// we set the water plane
Vector3f waterLocation = new Vector3f(0, -58, 0);
waterProcessor.setPlane(new Plane(Vector3f.UNIT_Y, waterLocation.dot(Vector3f.UNIT_Y)));
viewPort.addProcessor(waterProcessor);
// we set wave properties
waterProcessor.setWaterDepth(50); // transparency of water
waterProcessor.setDistortionScale(0.05f); // strength of waves
waterProcessor.setWaveSpeed(0.05f); // speed of waves
// we define the wave size by setting the size of the texture coordinates
Quad quad = new Quad(1000, 1000);
quad.scaleTextureCoordinates(new Vector2f(10f, 10f));
// we create the water geometry from the quad
Geometry water = new Geometry("water", quad);
water.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X));
water.setLocalTranslation(-500, -58, 500);
water.setShadowMode(ShadowMode.Receive);
water.setMaterial(waterProcessor.getMaterial());
rootNode.attachChild(water);
/** 5. The LOD (level of detail) depends on were the camera is: */
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
TerrainLodControl control = new TerrainLodControl(terrain, cameras);
terrain.addControl(control);
/** 6. Add physics: */
// We set up collision detection for the scene by creating a
// compound collision shape and a static RigidBodyControl with mass zero.*/
CollisionShape terrainShape = CollisionShapeFactory.createMeshShape(terrain);
landscape = new RigidBodyControl(terrainShape, 0);
terrain.addControl(landscape);
terrain.addControl(new RigidBodyControl(CollisionShapeFactory.createMeshShape(building), 0));
// We set up collision detection for the player by creating
// a capsule collision shape and a CharacterControl.
// The CharacterControl offers extra settings for
// size, stepheight, jumping, falling, and gravity.
// We also put the player in its starting position.
CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
player = new CharacterControl(capsuleShape, 0.05f);
player.setJumpSpeed(50);
player.setFallSpeed(70);
player.setGravity(100);
player.setPhysicsLocation(new Vector3f(50, 100, 100));
// We attach the scene and the player to the rootnode and the physics space,
// to make them appear in the game world.
bulletAppState.getPhysicsSpace().add(terrain);
bulletAppState.getPhysicsSpace().add(player);
}
/** We over-write some navigational key mappings here, so we can
* add physics-controlled walking and jumping: */
private void setUpKeys() {
inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addListener(this, "Left");
inputManager.addListener(this, "Right");
inputManager.addListener(this, "Up");
inputManager.addListener(this, "Down");
inputManager.addListener(this, "Jump");
}
/** These are our custom actions triggered by key presses.
* We do not walk yet, we just keep track of the direction the user pressed. */
public void onAction(String binding, boolean value, float tpf) {
if (binding.equals("Left")) {
if (value) {
left = true;
} else {
left = false;
}
} else if (binding.equals("Right")) {
if (value) {
right = true;
} else {
right = false;
}
} else if (binding.equals("Up")) {
if (value) {
up = true;
} else {
up = false;
}
} else if (binding.equals("Down")) {
if (value) {
down = true;
} else {
down = false;
}
} else if (binding.equals("Jump")) {
player.jump();
}
}
/**
* This is the main event loop--walking happens here.
* We check in which direction the player is walking by interpreting
* the camera direction forward (camDir) and to the side (camLeft).
* The setWalkDirection() command is what lets a physics-controlled player walk.
* We also make sure here that the camera moves with player.
*/
#Override
public void simpleUpdate(float tpf) {
Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);
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());
}
player.setWalkDirection(walkDirection);
cam.setLocation(player.getPhysicsLocation());
}
}
So, why isn't my model applied to the collision detection?
I have found the answer, here. The solution to the problem is this :
Create a CollisionShape. Create a PhysicsControl by supplying the
CollisionShape and mass. E.g. com.jme3.bullet.control.RigidBodyControl
Add the PhysicsControl to the Spatial. Add the PhysicsControl to the
physicsSpace object. Attach the Spatial to the rootNode, as usual.
(Optional) Implement the PhysicsCollisionListener interface to respond
to PhysicsCollisionEvents if desired.
Therefore, replace
terrain.attachChild(building);
by
rootNode.attachChild(building);
and adding
bulletAppState.getPhysicsSpace().add(building);