Hello All,
Here is my post-mortem on the LD32. As a pacifist I was not really into the theme, but I wanted to do something with Procedural Generation.
A theme can’t destroy an idea.
I lost the first day to an overambitious ‘level-player’ to do the generated stuff with.
The second day I was much more focussing on the thing I wanted to investigate and that was Procedural Generated Content.
Inspiration from: NO MAN’S SKY (
http://www.no-mans-sky.com )
Procedural Generation is not random. At least that is what I was aiming for.
No Man’s Sky is using functions and no randomness is involved. So each time you visit a world it was as you (or anyone else) left it.
Exceptions are going to be made on impact that the player has on the environment, but that is out of scope of this demonstration.
So .. how to make something unique but still be able to get the same outcome?
I am more of a puzzler myself and I wanted levels (and maybe content) to be generated.
So, I needed the idea / logic behind the way that No Man’s Sky is doing.
There are some videos on how they came up with the idea and how they use their tools (
https://www.youtube.com/watch?v=h-kifCYToAU)
The way I implemented was this idea:
You have generated data based on a function that takes a seed. Fibonacci (
http://en.wikipedia.org/wiki/Fibonacci_number) uses a formula to generate numbers and you can start with a number in the sequence.
Problem is that this always repeats.
A way to avoid that is to add an external data source to the generation sequence and you get pretty ‘random’ results.
for(i in 0 ... 1000)
g=getFour(g+i);
The code will execute the function getFour (which returns a four-digit number) and it will repeat that over and over
again using the generated number as the input for a new generated number.
The iterator ‘i’ will introduce a number to the generator that is different.
What does getFour do?
The generated numbers I want should be
- number 1 through 9 (no zero)
- And be kind of random
Nothing is totally random, but you get close if you use seeding with larger numbers.
I don’t want to use 64 bit seeds or even higher, I can live with a seed between 1000 and 9999
To live by my own rules I made it 1111 to avoid zeroes in the seed as well.
It makes a string like ‘681527341961683373871374818487424’
This NOISE data is used with my generator functions.
Suppose you have 4 tiles that you can choose from:
1=Grass
2=Dirt
3=Water
4=Ice
If you get the first digit from the string (6) it is higher than one of the tiles.
We have modulo operation : (
http://en.wikipedia.org/wiki/Modulo_operation)
This operation makes certain that we don’t overshoot the list.
Stencyl has this in the form of ‘remainder of []/[]’ block.
The number of tiles = 4, the generated number is 6. we want a number between 1 and 4
In Haxe we notate : 6%4
There is however a problem with using this operation when I want a number between 1 and 4.
The remainder of 4/4 =
0.
Therefore I use this method: 6%(number of items in list)+1:
In Haxe : (6%4)+1
Now we get 3.
You can use the generator to get digits and base a decision on it.
Suppose you want a Robot to be either black or white ‘randomly’ but if you revisit the level or restart the level
you want the same black or white robot.
Use this:
var genCode = getDigit(); // -> returns for example 4
if (genCode%2 == 0 )
// do something
else
// do something else
The genCode number is the same next time you run the level so the outcome is the same.
Well, that is if you use the initCounter to reset the ‘getDigit’ function.
That is what I use to generate the Robots. I initiate the generator with the robot number (From a previously generated list)
and therefore the robot is the same every time.
It worked well in my case and I plan on using this more in the future.
public static function genData(Start:Float){
var g:Float=Start%9999;
initCounter(0); // reset counter
data=""; // reset data
for(i in 0 ... 1000){
g=getFour(g+i);
data=data+""+g;
}
trace("Four generated: length: "+data.length+" = " +data);
} //genData
Initialized variables:
public static var data:String="";
public static var dataCounter:Int=0;
// when resetting the scene or if I want robots with a specific number generated:
public static function initCounter(Pos:Int){
dataCounter=(Pos%data.length);
} // initCounter <- Start 'counting' from the new position
Generation of the noise-data could be done only once and you could get the output and stored it literally, but you could also re-generate it.
In the LD entry I let the user input the initial seed.
public static function getFour(F:Float):Float{
var f:Float=(F%9)+1;
//trace("F: "+F+" 1-9: "+f);
var retval:Float=f;
// Generate 3 more numbers
for(i in 1 ... 4){
var c=Math.ceil(F/Math.pow(10,i));
f+=c*i;
f=(f%9)+1; // make 1- 9
retval+=(f*Math.pow(10,i));
} // for 3 more times
return retval;
} // getFour function
GetDigit is just a substring except that it returns a Float rather than a String.
public static function getDigit():Float{
var Pos:Int=cast(dataCounter++%data.length,Int);
var S=data.substr(Pos,1);
if(dataCounter+1 > data.length)dataCounter=0; // start over on the data
return Std.parseFloat(""+S);
}
Best regards from
M.E.
http://mazeland.net/ld/ld32/GenDemo.swf