5.6 KiB
title = "World Generation: Ore"
date = "2016-10-10 11:28:42 -0400"
We've got our copper ore block, but it doesn't generate in the world so it's not very useful to players. Let's fix that.
The first thing we'll need to do is create a class called ModWorldGeneration
in the world
sub-package in our mod. This class will implement Forge's IWorldGenerator
interface which is used to hook into Minecraft's world generation.
package net.shadowfacts.tutorial.world;
import net.minecraft.world.World;
import net.minecraft.world.chunk.IChunkGenerator;
import net.minecraft.world.chunk.IChunkProvider;
import net.minecraftforge.fml.common.IWorldGenerator;
import java.util.Random;
public class ModWorldGen implements IWorldGenerator {
@Override
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkGenerator chunkGenerator, IChunkProvider chunkProvider) {
}
}
The generate
method is called by Forge for every chunk that's generated and is our entry point into MC's world generation. Inside the generate
method, we'll check if world.provider.getDimension() == 0
because we only want our ore to generate in the overworld. If that's true, we'll call a separate method called generateOverworld
that takes the same parameters as generate
. In this method we'll have our generation code that's specific to the overworld.
// ...
public class ModWorldGen implements IWorldGenerator {
@Override
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkGenerator chunkGenerator, IChunkProvider chunkProvider) {
if (world.provider.getDimension() == 0) { // the overworld
generateOverworld(random, chunkX, chunkZ, world, chunkGenerator, chunkProvider);
}
}
private void generateOverworld(Random random, int chunkX, int chunkY, World world, IChunkGenerator chunkGenerator, IChunkProvider chunkProvider) {
}
}
Before we write the code that will actually add our Copper Ore into the world, let's write a little helper method to make our lives a bit easier.
This method will take a couple of things:
- The
IBlockState
to generate. - The
World
to generate in. - The
Random
to use for generation. - The X and Z positions to generate the block at.
- The minimum and maximum Y positions for which the ore can be generated.
- The size of each ore vein.
- The number of veins per chunk.
// ...
public class ModWorldGen implements IWorldGenerator {
// ...
private void generateOre(IBlockState ore, World world, Random random, int x, int z, int minY, int maxY, int size, int chances) {
int deltaY = maxY - minY;
for (int i = 0; i < chances; i++) {
BlockPos pos = new BlockPos(x + random.nextInt(16), minY + random.nextInt(deltaY), z + random.nextInt(16));
WorldGenMinable generator = new WorldGenMinable(ore, size);
generator.generate(world, random, pos);
}
}
}
This method does a couple of things:
- Calculate the difference between the maximum Y and minimum Y values.
- Create a
BlockPos
with X, minimum Y, and Z values passed into the method and offset by:- A random number from 0 to 15
- A random number from 0 to the difference between the min and max Y values (so that the ore is generated somewhere in between)
- A random number from 0 to 15
- Creates a new
WorldGenMinable
instance. - Calls the
generate
method on it to generate our ore in the world. - Repeats steps 2 through 4
chances
times.
This results in our ore being generated chanes
times per chunk with each chance having a different position inside the chunk and in between the specificed Y values.
// ...
public class ModWorldGen implements IWorldGenerator {
// ...
private void generateOverworld(Random random, int chunkX, int chunkZ, World world, IChunkGenerator chunkGenerator, IChunkProvider chunkProvider) {
generateOre(ModBlocks.oreCopper.getDefaultState(), world, random, chunkX * 16, chunkZ * 16, 16, 64, 4 + random.nextInt(4), 6);
}
private void generateOre(IBlockState ore, World world, Random random, int x, int z, int minY, int maxY, int size, int chances) {
// ...
}
}
We call the generateOre
method with:
- The block state we want to generate (the default block state of our copper ore block).
- The world we want generate in (the
World
we've been passed). - The random we want to use to generate (the
Random
we've been passed). - The X position we want to generate at (the
chunkX
value multipled by 16, because chunks are 16x16). - The Z position we want to generate at (the
chunkZ
value multiplied by 16, because chunks are 16x16). - The minimum Y position we want to generate at (16).
- The maximum Y position we want to generate at (64).
- The size of the vein to generate (a random number from 4 to 7).
- The number of times per chunk to generate (6).
Lastly, in the preInit
of our main mod class, we'll need to register our world generator.
// ...
public class TutorialMod {
// ...
public void preInit(FMLPreInitializationEvent event) {
// ...
GameRegistry.registerWorldGenerator(new ModWorldGen(), 3);
}
// ...
}
The int
parameter of GameRegistry.registerWorldGenerator
is the weight of our mod's world generator. This usually doens't matter, however, if you're experiencing issues with other mods interfering with your world generation, you may want to change this.
Now, if you create a new world and search around for a bit, you'll bet able to find a deposit of our Copper Ore!
You may want to play around with the vein size and chances settings until you achieve the desired concentration of ore per chunk.