Megabite #18: Procedural Generation (part 2)

I busted tail to attempt some code for my loyal Megabite readers this week, and in the end I decided to scrap it.  Why?  Well, my idea was to show that Unity was capable of procedural terrain generation on a massive scale (which it is), but what I came up with pales in comparison to what I came across within the Unity Community forum.  (link here)

A user by the name of Megaton has not only created an amazing Terrain Generator, but he's also been kind enough to release it for free (here).

Also, be sure to check it out in your browser here, because it's hard to explain how amazing it looks live.

 

So, in this article, I'm going to show a few sections of this very complex piece of code and do what I can to explain how something like this can be accomplished.  By comparison, my own little experiment looked far less realistic, and I think I would be doing an injustice for my readers by showing them something like that while knowing this was available to them for free.

The first thing we should look at is the setup of the variables - the ones that control the finer points of generation as a whole and make the world bumpy.  The developer who created this project placed these variables inside of a .js sale called "cgame", and it reads like this:

#pragma strict
class cgame extends UnityEngine.Object {

 // Project
 var PRODUCT_NAME = "Procedural Terrain Generator";
 var PRODUCT_VERSION = "1.0";

 // Engine
 var GAMESPEED = 1.0;

 // Game World
 var WORLDSEED = 123;
 var WATER_COLOR = Color( 0.5, 0.6, 0.7 );

 var TERRAIN_SIZE = 256;
 var TERRAIN_GRIDSIZE : float = 200;
 var TERRAIN_GRIDVIEW : float = 10;
 var TERRAIN_GRIDCOUNT = 100;
 var TERRAIN_HEIGHT = 250.0; // default: 200
 var TERRAIN_MAX_SUBOBJECTS = 75;
 var TERRAIN_PLANE_QUADSIZE = 12.0;
 var TERRAIN_GRID_SIZE = 2000;
}
static var gCgame : cgame;
static function InitCgame() {
 gCgame = new cgame();
}

You can change the numbers in this section to get some varying results - things are labelled fairly well so theres really no need to go much into detail here.  What I will mention about how this script works is that it does continuously generate new terrain meshes that match up to the existing pieces - each has a unique name and they match up fairly well.

The magic of this effect comes from a mixture of scripts, but all seem to be centralized around terrain.js, which contains heading such as:

  • GET PLAYERS POSITION ON THE TERRAIN GRID
  • IF PLAYER STEPS ONTO A NEW TERRAIN GRID THEN ADD AND REMOVE PLANES
  • ADDS PLANES DURING PLAY
  • etc.

Each section in this portion of the script is very clearly labeled, and it is a great learning experience for intermediate unity js programmers.  I found that for me, working backward from this portion made things more understandable:

//----------------------------------------
 // ADDS PLANES DURING PLAY
 //----------------------------------------
 function TerrDrawRow( position : Vector3, tgF : Vector2, tgT : Vector2 ) {
 Math.Floor( tgF );
 Math.Floor( tgT );

 var gridsize = gCgame.TERRAIN_GRIDSIZE;
 while( tgF != tgT ) {
 var pos = position;
 pos[0] += tgF[0] * gridsize;
 pos[2] += tgF[1] * gridsize;
 var name = "X" + Mathf.Floor( pos[0] ) + "Z" + Mathf.Floor( pos[2] );
 if( !TerrPlaneExists( name ) ) {
 TPlaneNames.Add( name );
 var plane : Transform = Instantiate( t_plane, pos, Quaternion.identity ) as Transform;
 plane.parent = parentObject;
 plane.name = name;
 var soc : Terrain_Plane = new Terrain_Plane( plane );
 terrain_planes.Add( soc );
 heightmodqueue.Add( soc );
 }
 tgF[0] += tgF[0] != tgT[0] ? Mathf.Sign( tgT[0] - tgF[0] ) : 0;
 tgF[1] += tgF[1] != tgT[1] ? Mathf.Sign( tgT[1] - tgF[1] ) : 0;
 }
 }

Of course you do need to bounce around between the various functions, and some are spread across multiple files, but things start clicking into place.  This section will add the new terrain objects and name them while attaching them to a parent object.  At the same time, it only does so if the name doesn't already exist.

Admittedly, the author admits that the generation isn't 100% seamless - in some areas where one grid connects to another, you can literally see the seam where one ends and the next begins.  However, it isn't something that detracts from the overall usefulness of the tool.

If you download and use this yourself on a project, load up the test scene and alter the movement controls of the "player" to allow yourself a high ground/air speed and an increased jumping ability.  In doing, you'll be able to take yourself across the landscape for a very long and scenic ride.