终于看到生成区块的代码了,接下来比较复杂,分成几篇文章吧
本篇内容是生物群系的算法
生成生物群系总流程
接着上篇文章我们看到这个函数
类名net.minecraft.world.gen.ChunkProviderGenerate
/**
* Will return back a chunk, if it doesn't exist and its not a MP client it will generates all the blocks for the
* specified chunk from the map seed and chunk seed
*/
public Chunk provideChunk(int x, int z)
{
// 根据区块坐标设置随机种子
this.rand.setSeed((long)x * 341873128712L + (long)z * 132897987541L);
// 这个是生成区块时临时储存方块数据的对象,后面会转为Chunk对象
ChunkPrimer chunkprimer = new ChunkPrimer();
// 生成基本地形(没有洞穴等建筑)
this.setBlocksInChunk(x, z, chunkprimer);
// ...
}
public void setBlocksInChunk(int x, int z, ChunkPrimer chunkprimer)
{
// 决定生物群系,这里把区块坐标*4-2了,而且结果是10*10的
this.biomesForGeneration = this.worldObj.getWorldChunkManager().getBiomesForGeneration(this.biomesForGeneration, x * 4 - 2, z * 4 - 2, 10, 10);
// ...
}
类名net.minecraft.world.biome.WorldChunkManager
/**
* Returns an array of biomes for the location input.
*/
public BiomeGenBase[] getBiomesForGeneration(BiomeGenBase[] result, int x, int z, int width, int height)
{
IntCache.resetIntCache();
if (result == null || result.length < width * height)
{
result = new BiomeGenBase[width * height];
}
// 生成生物群系ID
int[] biomeId = this.genBiomes.getInts(x, z, width, height);
try
{
for (int i = 0; i < width * height; ++i)
{
// 把生物群系ID转成BiomeGenBase对象
result[i] = BiomeGenBase.getBiomeFromBiomeList(biomeId[i], BiomeGenBase.field_180279_ad);
}
return result;
}
catch (Throwable throwable)
{
// ...
}
}
public WorldChunkManager(long seed, WorldType p_i45744_3_, String p_i45744_4_)
{
this();
this.field_180301_f = p_i45744_4_;
GenLayer[] agenlayer = GenLayer.initializeAllBiomeGenerators(seed, p_i45744_3_, p_i45744_4_);
agenlayer = getModdedBiomeGenerators(p_i45744_3_, seed, agenlayer);
// 这里初始化了genBiomes
this.genBiomes = agenlayer[0];
this.biomeIndexLayer = agenlayer[1];
}
接下来就非常复杂了,net.minecraft.world.gen.layer
这个包里有很多GenLayer,每层都会用上一层的输出生成这一层的输出
它们的初始化函数如下,最后的genlayerrivermix就是用来生成生物群系的
类名net.minecraft.world.gen.layer.GenLayer
public static GenLayer[] initializeAllBiomeGenerators(long worldSeed, WorldType worldType, String settings)
{
GenLayer genlayer = new GenLayerIsland(1L);
genlayer = new GenLayerFuzzyZoom(2000L, genlayer);
GenLayerAddIsland genlayeraddisland = new GenLayerAddIsland(1L, genlayer);
GenLayerZoom genlayerzoom = new GenLayerZoom(2001L, genlayeraddisland);
GenLayerAddIsland genlayeraddisland1 = new GenLayerAddIsland(2L, genlayerzoom);
genlayeraddisland1 = new GenLayerAddIsland(50L, genlayeraddisland1);
genlayeraddisland1 = new GenLayerAddIsland(70L, genlayeraddisland1);
GenLayerRemoveTooMuchOcean genlayerremovetoomuchocean = new GenLayerRemoveTooMuchOcean(2L, genlayeraddisland1);
GenLayerAddSnow genlayeraddsnow = new GenLayerAddSnow(2L, genlayerremovetoomuchocean);
GenLayerAddIsland genlayeraddisland2 = new GenLayerAddIsland(3L, genlayeraddsnow);
GenLayerEdge genlayeredge = new GenLayerEdge(2L, genlayeraddisland2, GenLayerEdge.Mode.COOL_WARM);
genlayeredge = new GenLayerEdge(2L, genlayeredge, GenLayerEdge.Mode.HEAT_ICE);
genlayeredge = new GenLayerEdge(3L, genlayeredge, GenLayerEdge.Mode.SPECIAL);
GenLayerZoom genlayerzoom1 = new GenLayerZoom(2002L, genlayeredge);
genlayerzoom1 = new GenLayerZoom(2003L, genlayerzoom1);
GenLayerAddIsland genlayeraddisland3 = new GenLayerAddIsland(4L, genlayerzoom1);
GenLayerAddMushroomIsland genlayeraddmushroomisland = new GenLayerAddMushroomIsland(5L, genlayeraddisland3);
GenLayerDeepOcean genlayerdeepocean = new GenLayerDeepOcean(4L, genlayeraddmushroomisland);
GenLayer genlayer4 = GenLayerZoom.magnify(1000L, genlayerdeepocean, 0);
ChunkProviderSettings chunkprovidersettings = null;
int biomeSize = 4;
int riverSize = biomeSize;
if (worldType == WorldType.CUSTOMIZED && settings.length() > 0)
{
chunkprovidersettings = ChunkProviderSettings.Factory.jsonToFactory(settings).func_177864_b();
biomeSize = chunkprovidersettings.biomeSize;
riverSize = chunkprovidersettings.riverSize;
}
if (worldType == WorldType.LARGE_BIOMES)
{
biomeSize = 6;
}
biomeSize = getModdedBiomeSize(worldType, biomeSize);
GenLayer lvt_8_1_ = GenLayerZoom.magnify(1000L, genlayer4, 0);
GenLayerRiverInit genlayerriverinit = new GenLayerRiverInit(100L, lvt_8_1_);
GenLayer lvt_10_1_ = GenLayerZoom.magnify(1000L, genlayerriverinit, 2);
GenLayer genlayerbiomeedge = worldType.getBiomeLayer(worldSeed, genlayer4, settings);
GenLayer genlayerhills = new GenLayerHills(1000L, genlayerbiomeedge, lvt_10_1_);
GenLayer genlayer5 = GenLayerZoom.magnify(1000L, genlayerriverinit, 2);
genlayer5 = GenLayerZoom.magnify(1000L, genlayer5, riverSize);
GenLayerRiver genlayerriver = new GenLayerRiver(1L, genlayer5);
GenLayerSmooth genlayersmooth = new GenLayerSmooth(1000L, genlayerriver);
genlayerhills = new GenLayerRareBiome(1001L, genlayerhills);
for (int i = 0; i < biomeSize; ++i)
{
genlayerhills = new GenLayerZoom((long)(1000 + i), genlayerhills);
if (i == 0)
{
genlayerhills = new GenLayerAddIsland(3L, genlayerhills);
}
if (i == 1 || biomeSize == 1)
{
genlayerhills = new GenLayerShore(1000L, genlayerhills);
}
}
GenLayerSmooth genlayersmooth1 = new GenLayerSmooth(1000L, genlayerhills);
GenLayerRiverMix genlayerrivermix = new GenLayerRiverMix(100L, genlayersmooth1, genlayersmooth);
GenLayer genlayer3 = new GenLayerVoronoiZoom(10L, genlayerrivermix);
genlayerrivermix.initWorldGenSeed(worldSeed);
genlayer3.initWorldGenSeed(worldSeed);
return new GenLayer[] {genlayerrivermix, genlayer3, genlayerrivermix};
}
各层GenLayer
GenLayer生成的就是生物群系ID,对应的生物群系可以在net.minecraft.world.biome.BiomeGenBase
查看。每个GenLayer有3个种子,baseSeed初始化时指定,worldGenSeed世界种子,chunkSeed计算时根据坐标计算。还有一个parent层,初始化时指定
GenLayerIsland
这个是最底层,很简单,随机生成1和0(平原和海洋)
/**
* Returns a list of integer values generated by this layer. These may be interpreted as temperatures, rainfall
* amounts, or biomeList[] indices based on the particular GenLayer subclass.
*/
public int[] getInts(int areaX, int areaY, int areaWidth, int areaHeight)
{
// IntCache用来避免反复分配内存,提高性能,后面还会看到很多次
int[] result = IntCache.getIntCache(areaWidth * areaHeight);
for (int y = 0; y < areaHeight; ++y)
{
for (int x = 0; x < areaWidth; ++x)
{
this.initChunkSeed((long)(areaX + x), (long)(areaY + y));
// 1/10的概率为1,其他为0
result[x + y * areaWidth] = this.nextInt(10) == 0 ? 1 : 0;
}
}
if (-areaWidth < areaX && areaX <= 0 && -areaHeight < areaY && areaY <= 0)
{
result[-areaX - areaY * areaWidth] = 1;
}
return result;
}
// 以下两个函数是基类(GenLayer)的,以后会经常用到
/**
* Initialize layer's current chunkSeed based on the local worldGenSeed and the (x,z) chunk coordinates.
*/
public void initChunkSeed(long x, long y)
{
this.chunkSeed = this.worldGenSeed;
this.chunkSeed *= this.chunkSeed * 6364136223846793005L + 1442695040888963407L;
this.chunkSeed += x;
this.chunkSeed *= this.chunkSeed * 6364136223846793005L + 1442695040888963407L;
this.chunkSeed += y;
this.chunkSeed *= this.chunkSeed * 6364136223846793005L + 1442695040888963407L;
this.chunkSeed += x;
this.chunkSeed *= this.chunkSeed * 6364136223846793005L + 1442695040888963407L;
this.chunkSeed += y;
}
/**
* returns a LCG pseudo random number from [0, x). Args: int x
*/
protected int nextInt(int upperLimit)
{
int i = (int)((this.chunkSeed >> 24) % (long)upperLimit);
if (i < 0)
{
i += upperLimit;
}
this.chunkSeed *= this.chunkSeed * 6364136223846793005L + 1442695040888963407L;
this.chunkSeed += this.worldGenSeed;
return i;
}
GenLayerZoom、GenLayerFuzzZoom
这层作用是把上一层结果放大,采样方式是在附近4个点随机采样
public int[] getInts(int areaX, int areaY, int areaWidth, int areaHeight)
{
// parent尺度是本层的1/2
int parentAreaX = areaX / 2;
int parentAreaY = areaY / 2;
// +2添加边界
int parentWidth = areaWidth / 2 + 2;
int parentHeight = areaHeight / 2 + 2;
// parentRes是本层的1/4
int[] parentRes = this.parent.getInts(parentAreaX, parentAreaY, parentWidth, parentHeight);
int tmpWidth = (parentWidth - 1) * 2;
int tmpHeight = (parentHeight - 1) * 2;
// 临时结果
int[] tmp = IntCache.getIntCache(tmpWidth * tmpHeight);
for (int parentY = 0; parentY < parentHeight - 1; ++parentY)
{
// tmp中当前点索引
int tmpIndex = (parentY * 2) * tmpWidth;
// parent当前点的值
int parentValue = parentRes[ parentY * parentWidth];
// parent当前点y+1点的值
int parentValueY1 = parentRes[(parentY + 1) * parentWidth];
for (int parentX = 0; parentX < parentWidth - 1; ++parentX)
{
this.initChunkSeed((parentX + parentAreaX) * 2, (parentY + parentAreaY) * 2);
// parent当前点x+1点的值
int parentValueX1 = parentRes[parentX + 1 + parentY * parentWidth];
// parent当前点x+1, y+1点的值
int parentValueX1Y1 = parentRes[parentX + 1 + (parentY + 1) * parentWidth];
// 当前点值 = parent点值
tmp[tmpIndex] = parentValue;
// 当前点y+1值 = 在 parent点、parent点y+1 中随机选
tmp[tmpIndex + tmpWidth] = this.selectRandom(new int[] {parentValue, parentValueY1});
// 当前点移动x+1
++tmpIndex;
// 当前点值 = 在 parent点、parent点x+1 中随机选
tmp[tmpIndex] = this.selectRandom(new int[] {parentValue, parentValueX1});
// 当前点y+1值 = parent四个点值中的众数或随机选
tmp[tmpIndex + tmpWidth] = this.selectModeOrRandom(parentValue, parentValueX1, parentValueY1, parentValueX1Y1);
// 当前点移动x+1
++tmpIndex;
// parent当前点移动x+1
parentValue = parentValueX1;
parentValueY1 = parentValueX1Y1;
}
}
int[] result = IntCache.getIntCache(areaWidth * areaHeight);
// tmp和result尺寸可能不同,这里把tmp左上角部分复制到result
for (int resultY = 0; resultY < areaHeight; ++resultY)
{
System.arraycopy(tmp, (resultY + areaY % 2) * tmpWidth + areaX % 2, result, resultY * areaWidth, areaWidth);
}
return result;
}
GenLayerAddIsland
从名字来看貌似是用来添加岛屿的
public int[] getInts(int areaX, int areaY, int areaWidth, int areaHeight)
{
int parentAreaX = areaX - 1;
int parentAreaY = areaY - 1;
int parentWidth = areaWidth + 2;
int parentHeight = areaHeight + 2;
int[] parentRes = this.parent.getInts(parentAreaX, parentAreaY, parentWidth, parentHeight);
int[] result = IntCache.getIntCache(areaWidth * areaHeight);
for (int y =