3
\$\begingroup\$

So for my game, I'm using perlin noise to generate regions of my map (water/land, forest/grass) but I'd also like to create some probability based generation too. For instance:

if(nextInt(10) > 2 && tile.adjacentTo(Type.WATER))
    tile.setType(Type.SAND);

This works fine, and is even reproduceable (based on a common seed) if the nextInt() calls are always in the same order. The issue is that in my game, the world is generated on demand, based on the player's location. This means, that if I explore the map differently, and the chunks of the map are generated in a different order, the randomness is no longer consistent.

How can I get this sort of randomness to be consistent, independent of call order?

Thanks in advance :)

\$\endgroup\$
1
  • \$\begingroup\$ How about setting the seed for each position? You can combine numbers to create a seed for your random function. \$\endgroup\$
    – William
    Commented Jun 24, 2012 at 20:26

2 Answers 2

4
\$\begingroup\$

This is simple, create a new Random() object for each chunk, using that chunk's position as the seed.

If you think of Random as a really (really) long series of numbers, the seed is just where to start iterating through. So, if you always set the seed before generating the chunk, you'll always get the same integers, in the same order.

EDIT

But how do you ensure that each chunk gets a unique seed?

It's not too critical to get a unique seed for each chunk, unless you want to try to guarantee that each chunk is unique from the last. I say try because even if each seed is unique it doesn't necessarily guarantee the random numbers you get will be different. If your world has a maximum size, you can get a unique long from each position in the world with a method like this:

public long positionToLong(int x, int y, int z, int maxY, int maxZ) {
    return (x * (MaxZ * maxY)) + (y * maxZ) + z;
}

If it doesn't have a maximum size, you can use some algorithm like:

public long positionToLong(int x, int y, int z) {
    return x + y + z + (x*x) + (y*y) + (z*z);
}

Then you can do additional things to strive for unique seeds, like add the distance from the center of the world.

I should mention how I do this in my game. Since I'm generating terrain shape along with terrain type, I wanted to ensure that I'd have continuity between chunks. I did that by using a single world seed. Then the Perlin noise function takes the absolute world position as an input. This makes the transition between chunks seemless, which will not be the case if you're randomly generating each chunk distinct from the others.

\$\endgroup\$
6
  • \$\begingroup\$ But how do you ensure that each chunk gets a unique seed? Or does that even matter? \$\endgroup\$
    – Nick Badal
    Commented Jun 25, 2012 at 3:31
  • \$\begingroup\$ @nickbadal "This is simple, create a new Random() object for each chunk, using that chunk's position as the seed. " Two chunks cannot have the same position, so their seed will be unique. \$\endgroup\$ Commented Jun 25, 2012 at 4:04
  • \$\begingroup\$ @JonathanHobbs That's true, but it has to be converted into a single number in some way. I'm blanking on a way to do that for an arbitrary position, but I added a method for when the world has a maximum. The second would fail fairly often I think, for example 0:0:1, 1:0:0 and 0:1:0 \$\endgroup\$
    – House
    Commented Jun 25, 2012 at 4:08
  • \$\begingroup\$ @Byte56 I guess so. You also can't just do simple addition because for any set of three coordinates, any combination will produce the same seed (edit: which it seems you've noticed!) \$\endgroup\$ Commented Jun 25, 2012 at 4:15
  • \$\begingroup\$ Well, if the OP only needs two numbers (3D is never mentioned) there's this option for generating unique seeds based off two positions: stackoverflow.com/questions/919612/… \$\endgroup\$
    – House
    Commented Jun 25, 2012 at 4:25
1
\$\begingroup\$

I'm not sure if there's any commonly practiced method of accomplishing this, but here's what I usually do for things like this:

Since you are already using a Perlin noise function, you can simply use Perlin noise to get the seed for each chunk. Whenever you need to generate a new chunk, simply feed that chunk's coordinates into the Perlin function, and use the returned value as the seed for that chunk. This means you'll need to reseed your random number generator every time a chunk is generated, or you can simply create a new instance of the generator if it's a class.

Essentially, whenever I need to generate consistent data in arbitrary positions, I use a Perlin noise function because it's reasonably fast and consistent.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .