Testing out JMonkeyEngine

April 17, 2016

I have been wanting to get back into graphics and (perhaps) game programming, but not on such a low level as earlier. It seems most people these days use some form of engine, or at least a framework, and there are many too choose from, even many free ones. Many people are using Unity, and while I’ve heard great things about that, I’ve been checking out JMonkeyEngine, which seems like an excellent choice for those of us using Java. It also seem to be somewhat lower level than Unity, witch I think fits someone like me, who have more programming than artistic skills.

After going through the tutorials here, I was thinking about how to put it all together, and also get a better feel for the libraries and engine. I decided to implement the easiest real-time game I know: Pong.

Starting and geometries

To begin with, I made a main class extending SimpleApplication, with a main method to start it all. The first step of nitializing is to create meshes for the paddles, and the ball. These are made from simple boxes and spheres. I don’t need any textures or lighting, so the simple “Common/MatDefs/Misc/Unshaded.j3md” material is used. The sizes are based on the window size, so they look good no matter the resolution used. Th. Z axis is not used, but set to 10.0 for physics (more on that later). After creating, the geometries are attached to the GUI node, so it can be rendered in 2D.

Box paddleMesh = new Box(paddleWidth, paddleHeight, 10.0f);
Material paddleMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
paddleMaterial.setColor("Color", ColorRGBA.Blue);
Geometry leftPaddle = new Geometry("Left paddle", paddleMesh);
leftPaddle.setMaterial(paddleMaterial);
guiNode.attachChild(leftPaddle);

Do this for two paddles and a ball, and you end up with:

paddles_and_ball

Physics

Next up, I created the physics system. While implementing the physics of pong would be pretty easy, I wanted to learn more about using Bullet, and I thought it was a good idea to do it with a simple project like this. I had never worked with Bullet (or JBullet) before, so it took some experimenting to make things work as I wanted. After creating the bulletAppState and registering it to the application, I created rigid body controls for both the paddles and the ball, so they can collide with each other. Giving the paddles mass zero while the ball has a non-zero mass, will ensure only the ball will change direction on collision, and giving the paddles restitution of 1.1 will make the ball increase speed each collision.After that it’s just a matter of giving the ball a little kick in the x direction.

leftPaddlePhysics = new RigidBodyControl(0.0f);
leftPaddle.addControl(leftPaddlePhysics);
bulletAppState.getPhysicsSpace().add(leftPaddlePhysics);
leftPaddlePhysics.setRestitution(1.1f);


ballPhysics = new RigidBodyControl(1.0f);
ball.addControl(ballPhysics);
bulletAppState.getPhysicsSpace().add(ballPhysics);
ballPhysics.setPhysicsLocation(new Vector3f(settings.getWidth() / 2, settings.getHeight() / 2, 0f));
ballPhysics.setLinearVelocity(new Vector3f(BALL_SPEED * settings.getWidth() / 10, 0.0f, 0.0f));

To stop the ball disappearing out of the screen, I make some RigidBodyControls to cover the sides. Again, these will have mass 0, but unlike the paddles I don’t add any visible nodes to them. I make them thick enough so the collision detection don’t run into any problems.

RigidBodyControl topCourtPhysics = new RigidBodyControl(0f);
topCourtPhysics.setCollisionShape(new BoxCollisionShape(new Vector3f(settings.getWidth() / 2, 10f, 10f)));
topCourtPhysics.setPhysicsLocation(new Vector3f(settings.getWidth() / 2, settings.getHeight() + 10f, 0f));
topCourtPhysics.setRestitution(1.0f);
bulletAppState.getPhysicsSpace().add(topCourtPhysics);

I want to make the ball move up or down when hitting the paddle off-centre. To do this, I make my class implement the PhysicsCollisionListener interface and register it as a collision listener. In the collision function, I check if the collision is between a paddle and the ball, and give the ball a kick up or down as apropiate.

if ("Ball".equals(collision.getNodeA().getName())) {
ballPhysics.applyImpulse(new Vector3f(0f, 1.2f * (collision.getNodeA().getLocalTranslation().y - collision.getNodeB().getLocalTranslation().y), 0f), new Vector3f());
} else {
ballPhysics.applyImpulse(new Vector3f(0f, 1.2f * (collision.getNodeB().getLocalTranslation().y - collision.getNodeA().getLocalTranslation().y), 0f), new Vector3f());
}

You can’t see it in the picture but the ball is moving up after hitting the right paddle.
Ball moving up

I also need to check for collision between the ball and either the left or right side of the screen. In this case, one of the players will be awarded a point, and the game will stop.

if (leftCourtPhysics.getObjectId() == collision.getObjectA().getObjectId() || leftCourtPhysics.getObjectId() == collision.getObjectB().getObjectId()) {
rightPlayerScore++;
running = false;
bulletAppState.setEnabled(false);
}

Input

The input system is pretty simple, just up and down keys, and a key to start and toggle pause. The only thing to look out for is that I must move the physics objects of the paddles, and they will in turn move the geometries. I also check to make sure the paddles don’t go out of the screen. Because I’m moving the paddles manually, I don’t think it’s possible to use the collision system to do this.

if (name.equals("Up")) {
Vector3f current = rightPaddlePhysics.getPhysicsLocation();
rightPaddlePhysics.setPhysicsLocation(new Vector3f(current.x, current.y + paddleSpeed * value * speed, current.z));
if (rightPaddlePhysics.getPhysicsLocation().y > settings.getHeight() - paddleHeight) {
rightPaddlePhysics.setPhysicsLocation(new Vector3f(current.x, settings.getHeight() - paddleHeight, current.z));
}
}

GUI

The GUI is really simple, just a couple of BitmapTexts to show the scores. These are updated when the game detects a collision with the left or right wall. Remember to make sure the score board is rendered on top of the ball using the Z position.

guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
leftScoreText = new BitmapText(guiFont, false);
leftScoreText.setSize(guiFont.getCharSet().getRenderedSize() * 3);
leftScoreText.setLocalTranslation(100, settings.getHeight() - leftScoreText.getLineHeight(), 10);
guiNode.attachChild(leftScoreText);

scores

Audio

Adding audio is very easy with JMonkeyEngine. Just remember that if you use a Maven project, the files should be placed in /src/main/resources. I add one looping AudioNode for music, and others for effect, and I play the effects in the collision callback method.

AudioNode music = new AudioNode(assetManager, "assets/Sounds/Cycles.ogg", DataType.Stream);
music.setLooping(true);
music.setPositional(false);
music.setVolume(0.25f);
music.play();
AudioNode blopp = new AudioNode(assetManager, "assets/Sounds/blopp.ogg", DataType.Buffer);
blopp.setLooping(false);
blopp.setPositional(false);
blopp.setVolume(0.5f);

AI

I made a very simple AI, that just tries to keep the centre of the paddle in line with the ball. Like the player, the AI can move up, down, or not at all.

int move = 0;
if (ball.getTranslationMatrix().m31 > cpuPaddle.getTranslationMatrix().m31 + 1.5) {
move = 1;
}
if (ball.getTranslationMatrix().m31 < cpuPaddle.getTranslationMatrix().m31 - 1.5) {
move = -1;
}
leftPaddlePhysics.setPhysicsLocation(new Vector3f(current.x, current.y + ((paddleSpeed * tpf * speed) * move), current.z));

Originally it looked at the ball and paddle centre, but this caused it to go back and forth a lot, since the paddle can move faster than the ball in the y direction. Giving it some slack makes the movement much more smooth and nice.
with_ai

Wrapping up

I found that doing a small, but nonetheless complete game like this after going through all the individual tutorials, was very useful. The individual parts of the engine affect each other in ways that is difficult to predict, but it really helpful to experience this with a game which is familiar. Some other things I have learned doing this small project:

  • It seems the input system would not be called every frame if the frame rate is high. This made the player input and the AI input run at different speeds. I locked the game to run at 60 FPS, and that worked fine.
  • It’s inconvenient to mix physics based movement and movement directly from player input. But in a real game, I probably wouldn’t let the player control them self directly, but rather by using forces anyway.
  • While I haven’t tried the JMonkeyEngine SDK/IDE, using it with Maven and Eclipse works just fine.
  • Even for a very small and simple game like this, it’s more work than expected, especially all the small things that pop up. If I also need to invent gameplay, I expect it to be a lot more.

So that’s my experiences with JMonkeyEngine in a full (albeit small) project. In general good experience so far, but I will try to do other more advanced projects in the coming months.

Generating random terrain

June 26, 2009

I have implemented a random terrain generator, and after some tweaking it, it produces pretty good results.

Randomly generated terrain

Randomly generated terrain

I can seed the generator as I wish, and get the same result every time, or use some other value like the system time to get a new terrain every time.

Another view

Another view

Another view

Another view of the terrain

Also, the artifacts from the previous post have been eliminated.:) The light is working as I want it now.

Light model working

Light model working

The lighting is nice, but it doesn’t do shadows. At least not yet. That will be the next focus for me.

-Knut

Adding light part 2

June 20, 2009

I’ve changed the way the program calculates the light level on the textures, so it should be more realistic. The sun’s position is configurable, and is used to shade one side of a hill, while the other one will be bright.

Better light model

Better light model

The sun would on the left side of this picture, you can see a small valley in the middle with a hill behind it. Notice the difference in light levels between the right and left sides of the valley and hill. This is actually looking very nice. 🙂

From above

From above

Same valley, seen from above. The walls are about the same steepness. However, there are a few problems and limitations. Firstly, the sun’s position can only be specified in the east-west range. This is not much of a problem, and is easily corrected if necessary. Secondly, there are no shadows yet, but I plan to add support for this. And thirdly, there are these artifacts here on the textures:

Artifacts on the terrain

Artifacts on the terrain

See those lines there. They are everywhere. Actually, they appeared when I implemented the texture splatting as well, but then I managed to work around them, for the most part. But now, I have a plan to get rid of them. The problem is, the height map has integer values. So on many slopes the steepness map will be like a stair instead of a smooth slope. To counter this, I think I need to create a random terrain generator.

I’ve been searching around a bit, and I want too implement a fractal generator for the terrain, preferably in a way that allow me to feed it a number to generate a terrain from(seed), or use an random one. Hopefully this will get me smooth slopes as well as interesting terrain, and i hope to complete this as soon as possible.

-Knut