生物类别 生物被划分为8种类别,分别为怪物 (Monster)、动物 (Creature)、环境生物 (Ambient)、美西螈 (Axolotls)、地下水生生物 (Underground Water Creature)、水生生物 (Water Creature)、水下环境生物 (Water Ambient)和其他 (Misc)。
类别
上限乘数
友好生物
自动清除
清除半径
怪物
70
否
是
128
动物
10
是
否
128
环境生物
15
是
是
128
美西螈
5
是
是
128
地下水生生物
5
是
是
128
水生生物
5
是
是
128
水下环境生物
20
是
是
64
其他
-1
是
否
128
1 2 3 4 5 6 7 8 9 10 11 public enum SpawnGroup implements StringIdentifiable { MONSTER("monster" , 70 , false , false , 128 ), CREATURE("creature" , 10 , true , true , 128 ), AMBIENT("ambient" , 15 , true , false , 128 ), AXOLOTLS("axolotls" , 5 , true , false , 128 ), UNDERGROUND_WATER_CREATURE("underground_water_creature" , 5 , true , false , 128 ), WATER_CREATURE("water_creature" , 5 , true , false , 128 ), WATER_AMBIENT("water_ambient" , 20 , true , false , 64 ), MISC("misc" , -1 , true , true , 128 ); }
生成上限 为了阻止生物无限生成,每个生物类别都有它们的生成上限数量。生成上限数量不仅与类别本身有关,也与玩家有关。以每个玩家所在区块为中心的17×17区块都被认为是可生成区块 ,而每个生物类别的生成上限m 是可生成区块数量c 、生物类别上限乘数a 之积除以289得到的,即m=ac/289。
当仅有一位玩家时 ,生成上限m就等于上线乘数a,即m=a
当存在n名玩家时 ,假如所有玩家所在的可生成区块互不干扰,则生成上限为最大值na,假如所有玩家在同一个区块,则生成上限与一名玩家相同为a。因此该情况下,生成上限m在[a, na]范围之间。
核心条件 主要介绍普通敌对生物的生成条件,史莱姆等特殊生物除外
核心步骤 1. 生物群系筛选 根据生物群系配置获取可生成的生物列表,并基于群系生成概率决定是否进入生成循环。
1 2 3 4 5 6 7 8 9 10 11 SpawnSettings spawnSettings = biomeEntry.value().getSpawnSettings();Pool<SpawnSettings.SpawnEntry> pool = spawnSettings.getSpawnEntries(SpawnGroup.CREATURE); if (!pool.isEmpty()) { int i = chunkPos.getStartX(); int j = chunkPos.getStartZ(); while (random.nextFloat() < spawnSettings.getCreatureSpawnProbability()) { } }
2. 初始点 X/Z轴在区块内随机选择初始点,Y轴从非透明方块的最高点+1到世界底部的随机值
1 2 3 4 5 6 7 8 private static BlockPos getRandomPosInChunkSection (World world, WorldChunk chunk) { ChunkPos chunkPos = chunk.getPos(); int i = chunkPos.getStartX() + world.random.nextInt(16 ); int j = chunkPos.getStartZ() + world.random.nextInt(16 ); int k = chunk.sampleHeightmap(Heightmap.Type.WORLD_SURFACE, i, j) + 1 ; int l = MathHelper.nextBetween(world.random, world.getBottomY(), k); return new BlockPos (i, l, j); }
3. 生成尝试 每个初始点触发3次生成游走尝试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 for (int k = 0 ; k < 3 ; k++) { int l = pos.getX(); int m = pos.getZ(); SpawnSettings.SpawnEntry spawnEntry = null ; EntityData entityData = null ; int o = MathHelper.ceil(world.random.nextFloat() * 4.0F ); int p = 0 ; for (int q = 0 ; q < o; q++) { l += world.random.nextInt(6 ) - world.random.nextInt(6 ); m += world.random.nextInt(6 ) - world.random.nextInt(6 ); mutable.set(l, i, m); double d = (double )l + 0.5 ; double e = (double )m + 0.5 ; PlayerEntity playerEntity = world.getClosestPlayer(d, (double )i, e, -1.0 , false ); if (playerEntity != null ) { double f = playerEntity.squaredDistanceTo(d, (double )i, e); if (isAcceptableSpawnPosition(world, chunk, mutable, f)) { if (spawnEntry == null ) { Optional<SpawnSettings.SpawnEntry> optional = pickRandomSpawnEntry( world, structureAccessor, chunkGenerator, group, world.random, mutable ); if (optional.isEmpty()) break ; spawnEntry = optional.get(); o = spawnEntry.minGroupSize + world.random.nextInt( 1 + spawnEntry.maxGroupSize - spawnEntry.minGroupSize ); } if (canSpawn(world, group, structureAccessor, chunkGenerator, spawnEntry, mutable, f) && checker.test(spawnEntry.type, mutable, chunk)) { MobEntity mobEntity = createMob(world, spawnEntry.type); if (mobEntity == null ) return ; mobEntity.refreshPositionAndAngles(d, (double )i, e, world.random.nextFloat() * 360.0F , 0.0F ); if (isValidSpawn(world, mobEntity, f)) { entityData = mobEntity.initialize(world, world.getLocalDifficulty(mobEntity.getBlockPos()), SpawnReason.NATURAL, entityData ); j++; p++; world.spawnEntityAndPassengers(mobEntity); runner.run(mobEntity, chunk); if (j >= mobEntity.getLimitPerChunk()) { return ; } if (mobEntity.spawnsTooManyForEachTry(p)) { break ; } } } } } } }
生成验证 满足以下条件,将导致生成失败
玩家距离过近或过远
光照条件不符合
空间不足
生物群系或结构不匹配
密度上限已满
碰撞箱被遮挡
生物类型不可生成
LC值对刷怪的影响 LC (Loaded Chunk),即玩家所在区块的最大渲染区段高度,一般是16的倍数减1。 在核心步骤中的第2步,选择随机初始点时,会随机在最高的非透明方块所在位置+1与世界底部之间取一个值,作为刷怪初始点的高度。
当 LC 值低时,意味着最高遮光方块的纵坐标较低,刷怪初始点可选择的高度范围小,就有更大的概率选到合适的刷怪高度,从而提高刷怪成功率。
若 LC 值高,比如刷怪平台建在 y=100 处,那么 y 轴坐标可能是 0-101 之间的任何一个数,选到刚好能刷怪高度的概率就低,刷怪效率自然下降。
参考