Sunspears – a hostile fighter

The previous Ai controlled ship; the trade ships, were large and neutral by default. I wanted to add something more like a fighter; small, nimble, forward firing and hostile by default.

Here’s the work in progress:

 

 

This ship is attacking an imaginary object just above the tower. I’m imagining a few of these surrounding a base and attacking intruders (as hostile ships them wandering around in the world might be a little player hostile, maybe for a hardcore mode).

I don’t think this ship body will be its final form as I wanted it to be more… like a spear. It will also probably attack things that aren’t in its imagination.

As a computer controlled ship this is all good but it immediately made me want to create something human controlled with equal maneuverability to fight it but the command  chair as is stands requires far to much external wiring to process the outputs before they go to the engines. So; I’m planning on allowing simple formulas to be put in the keybindings for the command chair as well as vectors

So the current command chair allows you to select a single key to output in each direction, so for example you might type “a” to bind the a key to the SOUTH output which means to get forwards, fire, up, down, left, right you need 6 outputs, which already means you need 2 layers of outputs which is fine for a capital ship but big for a fighter. But if instead you allow simple formulas you can have:

SOUTH = a-d (controlling left and right together, press a and SOUTH is +1, press d and its is -1, press both and achieve nothing, don’t do that)
EAST = w (for forwards)
WEST = o-l (for up and down)
NORTH = x (for fire)

Great, now were’re at a single layer of outputs, except we’ve got wires coming forwards from the cockpit, that’s going to obscure our view, and the game already supports vectors so how about this:

SOUTH = w, a-d, o-l
EAST = x (for fire)

Now we can get all the signals away from the player nice and easily!

So that’s the plan, if all goes well you should see us in a fighter battle with that sunspear soon

Night and Day (done not stupidly)

In my game I have “sunlight” and “source light”, sunlight provided by the sun, remarkably, and source light provided by light blocks. However, in blockworlds light tends to be “prebaked” that is calculated once then each vertex knows how bright it is, this is great for efficiency! However in this prebaking I ended up combining the sunlight and sourcelight into one value; the bigger of the sunlight or the sourcelight, so a vertex (corner) is say 0.7 in brightness because its near a light block. Great!

Now, try to implement night and day, if you want to turn down the sun from 1 to 0.95 you have to regenerate everything to do the combination of sourcelights (still full power) and sunlight (now at 0.95 power). And you can’t rerender everything at once, its just not fast enough. So that mountain over there; its still day over there! But over here its twilight. Not to mention the fact that the whole game is now lagging under the strain. Turns out this is a really stupid way to do it.

So how do you do it not stupidly? You still prebake the light, but you prebake sunlight at full intensity and sourcelight separately for each vertex. Then you let the graphics card sort it out. Or more precisely; you write shaders to have the graphics card combine those prebaked values every frame and get the best of both worlds; you can do millisecond by millisecond adjustments to the sunlight level without any regenerating of geometry at all.

Link

So that’s a nice video of a timelapsed day/night cycle, but how did I do it in practice, well we’ll need:

  • A custom mesh, where we’ll set TWO colour buffers, one for sunlight and one for sourcelight
  • A custom jMonkey material with custom fragment and vertex shaders (the fragment shader is where the real work will be). Btw a vertex is a corner of your geometry and a fragment is sort-of kind-of a pixel  and the shaders allow you to manipulate them on the graphics card, and graphics cards are really really good at that sort of thing.

Now; code!

(Incidentally this is fairly advanced, you can get a long way with jMonkey without worrying about custom meshes at all)

The main java class:

 


public class Main extends SimpleApplication {

private Material mat;
 private float dayNightPeriod = 4;
 private float time;

 public static void main(String[] args) {
 Main app = new Main();
 app.start();
 }

@Override
 public void simpleInitApp() {

 //the points in 3d space where the geometry will be
 Vector3f [] vertices = new Vector3f[8];
 //one square
 vertices[0] = new Vector3f(0,0,0);
 vertices[1] = new Vector3f(3,0,0);
 vertices[2] = new Vector3f(0,3,0);
 vertices[3] = new Vector3f(3,3,0);
 //a second square
 vertices[4] = new Vector3f(3,0,0);
 vertices[5] = new Vector3f(6,0,0);
 vertices[6] = new Vector3f(3,3,0);
 vertices[7] = new Vector3f(6,3,0);

 //combine those vetexes into triangles 
 int [] indexes = { 
 //first square
 2,0,1,1,3,2, 
 //second square 
 6,4,5,5,7,6 
 };

 //we're not using a texture but if we were this would define whtich parts of the image are where 
 Vector2f[] texCoord = new Vector2f[8];
 texCoord[0] = new Vector2f(0,0);
 texCoord[1] = new Vector2f(1,0);
 texCoord[2] = new Vector2f(0,1);
 texCoord[3] = new Vector2f(1,1);
 texCoord[4] = new Vector2f(0,0);
 texCoord[5] = new Vector2f(1,0);
 texCoord[6] = new Vector2f(0,1);
 texCoord[7] = new Vector2f(1,1);

 //Set the first colour buffer, this will be the sunlight color
 Vector4f[] sunlightColour = new Vector4f[8]; //these are Vector4f because we have a red, green, blue and transparency per vertex
 //both the squares at full brightness in sunlight
 sunlightColour[0] = new Vector4f(1,1,1,1);
 sunlightColour[1] = new Vector4f(1,1,1,1);
 sunlightColour[2] = new Vector4f(1,1,1,1);
 sunlightColour[3] = new Vector4f(1,1,1,1);

 sunlightColour[4] = new Vector4f(1,1,1,1);
 sunlightColour[5] = new Vector4f(1,1,1,1);
 sunlightColour[6] = new Vector4f(1,1,1,1);
 sunlightColour[7] = new Vector4f(1,1,1,1);

 Vector4f[] sourceLightColour = new Vector4f[8];
 sourceLightColour[0] = new Vector4f(0.9f,0.9f,0.9f,1); //first square at 90% brightness in sourcelight
 sourceLightColour[1] = new Vector4f(0.9f,0.9f,0.9f,1);
 sourceLightColour[2] = new Vector4f(0.9f,0.9f,0.9f,1);
 sourceLightColour[3] = new Vector4f(0.9f,0.9f,0.9f,1);

 sourceLightColour[4] = new Vector4f(0.3f,0.3f,0.3f,1);//second square at 30% brightness in sourcelight
 sourceLightColour[5] = new Vector4f(0.3f,0.3f,0.3f,1);
 sourceLightColour[6] = new Vector4f(0.3f,0.3f,0.3f,1);
 sourceLightColour[7] = new Vector4f(0.3f,0.3f,0.3f,1);

 //now we have all the data we create the mesh
 //for more details https://jmonkeyengine.github.io/wiki/jme3/advanced/custom_meshes.html
 Mesh mesh = new Mesh();
 mesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
 mesh.setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(indexes));
 mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord));
 mesh.setBuffer(VertexBuffer.Type.Color, 4, BufferUtils.createFloatBuffer(sunlightColour));
 //we're using the conventional Color buffer for sunlight and using one of the (usually) unused TexCoord5 to hold the source light
 mesh.setBuffer(VertexBuffer.Type.TexCoord5, 4, BufferUtils.createFloatBuffer(sourceLightColour));
 mesh.updateBound();

 Geometry geom = new Geometry("mesh", mesh);

mat = new Material(assetManager, "MatDefs/RepeatingUnshaded.j3md");
 //mat = new Material(assetManager,"Common/MatDefs/Misc/Unshaded.j3md"); 
 mat.setBoolean("VertexColor", true);
 geom.setMaterial(mat);

rootNode.attachChild(geom);
 }

@Override
 public void simpleUpdate(float tpf) {
 time += tpf;
 if (time>dayNightPeriod){
 time = 0; 
 }
 mat.setParam("SunlightIntensity", VarType.Float,time/dayNightPeriod); 
 }
}

 

Now if I posted the whole of the shaders they’d look crazy complicated, but I’ve mostly stolen them from the built in Common/MatDefs/Misc/Unshaded.j3md and just edited what I needed to combine the colour rather than just pass it though. So to avoid a code dump I’ll just pull out the important bits and here’s a repository version for the full project https://bitbucket.org/RichardTingle/sunsourcelightwithshaders

A jMonkey Material def contains all the uniforms (things that are common to the whole material) and references to the shaders to use. The only thing we need to add here is the SunlightIntensity uniform. We can vary this to tell the shader how much to use the sunlight prebaked light


MaterialDef Unshaded {
MaterialParameters {
Float SunlightIntensity // This is send to the shaders to tell them how combine light levels.
.........
}
........
}

The vertex shader can affect the verticies of the mesh, we don’t really want to do much here so we just recieve the extra color data and pass it on to the fragment shader, where the real work will be

attribute vec4 inTexCoord5; // this is the source light that is passed to us
varying vec4 texCoord5; //and this is how we'll pass it through to the fragment shader

void main(){
......
texCoord5 = inTexCoord5; //its the fragment shader that wants this so we just pass it on
}

The fragment shader is where the real work happens, we’ll declare that we want the uniform m_SunlightIntensity and use that to combine the sun and source light. Each fragment (soft-of pixel) will combine the sunlight and sourcelight according to the current sunlight level.

uniform float m_SunlightIntensity; //just by declaring the uniform we'll receive it automatically
varying vec4 texCoord5; //this is the source color passed through to us
void main(){
.......
vec4 color = vec4(1.0);
color.x *= max(vertColor.x * m_SunlightIntensity,texCoord5.x);
color.y *= max(vertColor.y * m_SunlightIntensity,texCoord5.y);
color.z *= max(vertColor.z * m_SunlightIntensity,texCoord5.z);
color.a *= vertColor.a;
......
}

So with all that together you get this:

 

Personally I thought the forest was prettier but nevermind.

Anyway, that’s light done non stupidly (or at least less stupidly). A few thoughts you may immediately have; what about machines? Sourcelight coming from the main world affecting moving machines would need to regenerate geometry each tick to truely track the light through it (slow!) so my plan is to have the light sampled by the machines at a single point and used as the sunlight level; that will give me not quite right but hopefully good enough lighting for the machines.

 

2 Minute Hidden Cave

This is going to demo using the rails block, this allow one machine to run along a rail. In this case I’m using it to create a secret opening to a cave

Link

So that’s great (he says modestly) but pushing things along rails can get boring fast. So can you power the rail so that it can move on its own?

I wouldn’t have asked the question if you couldn’t, so here’s the video

Link

So a rail can be controlled not only by a button directly but remotely using antennas, which is, if I’m honest, much more useful.

You saw there was an addition/subtraction block. This is because a positive signal will move the rail one way, and a negative signal the other way

addSubtractWithAntennas

Tech Stack!

For the more technically minded amongst you here is the tech I use to create my blockworld.

Language: Java

Because Java is almost as fast as C++ but with none of the manual memory management rubbish. Also Java is great!

Framework: jMonkeyEngine

jMonkeyEngine is a bit like Unity in C++, its a scene graph based 3d graphics library for java that uses OpenGl under the hood (i.e. it runs on the graphics card so its fast!).

[Full disclosure I think its got a lot more in it besides that but that’s what I largely use it for]

jMonkeyEngine is great because you can get started really really quickly, here’s a hello world

public class SimpleBox extends SimpleApplication {

    public static void main(String[] args){
        HelloJME3 app = new HelloJME3();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        Box b = new Box(1, 1, 1); // create cube shape
        Geometry geom = new Geometry("Box", b);  // create cube shape
        Material mat = new Material(assetManager,
          "Common/MatDefs/Misc/Unshaded.j3md");  //use a built in material 
        mat.setColor("Color", ColorRGBA.Red);   //make the material red
        geom.setMaterial(mat);                   //use the material on the cube
        rootNode.attachChild(geom);              //make the cube appear in the world
    }
}

And that creates:

redBox

So; easy!

But jMonkey doesn’t stand in your way when you want to do more advanced stuff.

You may be thinking; hey that looks like a block, is your game made of hundreds of thousands of those? And the answer is: originally yes, until I found out that that is a terrible terrible idea. It works but is really really slow because the graphics card hates all those separate geometries (it asks if you can see each one, each has its own material etc etc). So what do you do? You merge blocks together into chunks. These are great because they are small enough that you can recreate one from scratch in a single “tick” but big enough that they can be drawn efficiently. But we’re getting into architecture now so I’ll leave that for another post.

2 Minute Hovercraft

In the last post I showed a video of finished product; an aircraft, a hidden entrance, a turret and a car. which makes for a nice demo but doesn’t really show you how the game is actually played so I want to show some really short videos starting at nothing and building something that shows off a feature of the game. They may not be as pretty but they will be up and running quickly.

So to start; a 2 Minute Hovercraft

Link

(Possibly that explosion at the end was a little over the top for a gentle crash into a tree, work in progress!)

So, that’s got engines to move, a command chair to bind keyboard input to the wire outputs, a skype notification noise (sorry about that, I did say these would be less pretty) and two weird looking blocks that didn’t seem to do anything:

antigrav and stabilisation

These blocks are “physics is hard, sort it out for me!” blocks. Or more specifically the antigrav block (gravity doesn’t affect any machine with one of these) and the stabilization block (keeps the machine more or less flat, the more of these you add the more it tries to keep it flat, but one is usually enough).

So what if you don’t have these blocks. I’m sure that would be fine.

No stabilization block:

Link

No antigrav block:

Link

Turns out a hovercraft without an antigravity block isn’t really a hovercraft. That said its utter failure showed off how blocks that get broken off machines become their own separate machines so I’m counting that as a success

Blockworlds!

I plan on using this blog to write about my progress writing my blockworld game. If the name “Blockworld” doesn’t mean much to you its because one game in particular dominates that genre, but it isn’t the only one. So this is a blockworld:

blockworldIntro

Great, so now we’re on the same page. Now a lot of you will have just said: “But that’s *REDACTED*!” and I’ll agree it does look pretty similar. So why do I want to remake a game that already exists? Well the answer is I don’t. Blockworlds you’ll have seen before tend to be fairly architectural; a building here, a bridge there, and if I’m honest that is already really fun, so many hours sunk, so many. But, when I was a child I didn’t make buildings and bridges out of Lego, I made spaceships and machines and things that moved (or at least that I imagined moving) and that’s what I wanted from my blockworld. Something like this:

tradeship

That incidentally is something you could build in my game (I really need to decide on a title). You can see it has engines to move and maneuver and those green things on the end are energy projectors (weapons) linked up to sensors (the red blocks) all wired together to detect and shoot at hostile machines. But that particular ship is something that is also generated by the game and under Ai controll, it will wander around the world and if you shoot at it it will turn hostile and start shooting back and never forgive you (mental note; need to fix that last bit, slightly player hostile).

So; enough with the screenshots, I keep talking about “things that move” so here’s a video of all the things that move: