A lot of people have asked in the Modification Development section how they make custom structures. There are a handful of good tutorials for 1.7.10 on this, and the basic idea has not changed much. But for beginners in 1.8 and beyond, I present a tutorial for custom structure generation.
The whole mod (all the files together) can be found on GitHub.
Objective:
We want a bunch of Cookie Bushes (our custom block) to appear in Plains biomes randomly.
Preparation:
First we need the bush block. We want this bush to, when clicked, give the player a cookie. Then it will replace itself with a bush that has no cookies, which will eventually replace itself with a bush that does have cookies.
We want two bushes: one that will give cookies, and one that will not. It's simple to make the non-cookie bush change randomly.
BlockCookieBushFull:
public class BlockCookieBushFull extends BlockCookieBushEmpty
{
public BlockCookieBushFull()
{
super();
this.setTickRandomly(false);
}
@Override
public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumFacing side, float hitX, float hitY, float hitZ)
{
// make the cookie item to give the player
if(!worldIn.isRemote) // only spawn on server side
{
EntityItem cookie = new EntityItem(worldIn, playerIn.posX, playerIn.posY, playerIn.posZ, new ItemStack(Items.cookie));
cookie.setNoPickupDelay();
worldIn.spawnEntityInWorld(cookie);
}
// replace this block with a non-cookie bush
worldIn.setBlockState(pos, TutorialBlocks.cookieBushEmpty.getDefaultState(), 2);
return true;
}
}
BlockCookieBushEmpty:
public class BlockCookieBushEmpty extends BlockBush
{
public BlockCookieBushEmpty()
{
super();
this.setTickRandomly(true); // this allows the plant to randomly update
this.setStepSound(soundTypeGrass);
float f = 0.4F;
this.setBlockBounds(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, 0.5F + f, 0.5F + f);
}
@Override
public void randomTick(World worldIn, BlockPos pos, IBlockState state, Random rand)
{
super.randomTick(worldIn, pos, state, rand);
// doesn't grow with every update.
// 40% of the time it grows
final int GROW_CHANCE = 30;
if(rand.nextInt(100) < GROW_CHANCE)
{
worldIn.setBlockState(pos, TutorialBlocks.cookieBushFull.getDefaultState());
}
}
}
We register both these bushes and the JSONs for them. I can post those if you guys want them. I override the isFullCube method and the others because I want my block to look like a cross, like flowers do.
Right, so now on to the generation:
Make a WorldGenerator class:
I made a class called WorldGenCookieBushes that extends WorldGenerator. Inside the generate method that I am forced to override, I do several things:
- figure out what height to generate at
- make sure it is only replacing air, liquids, or plants
- make sure it is on top of dirt or grass
- actually place the block
Here's what I ended up with:
public class WorldGenCookieBushes extends WorldGenerator
{
@Override
public boolean generate(World worldIn, Random rand, BlockPos pos)
{
// we randomly pick between a bush with a cookie and a bush without a cookie
Block cookieBush = rand.nextBoolean() ? TutorialBlocks.cookieBushFull : TutorialBlocks.cookieBushEmpty;
int y = 1 + getGroundFromAbove(worldIn, pos.getX(), pos.getZ());
// debug:
// System.out.println("Y-value of ground is " + y + " at (" + pos.getX() + ", " + pos.getZ() + ")");
// the Y we passed earlier will be used here as the minimum spawn height allowed
if(y >= pos.getY())
{
BlockPos bushPos = new BlockPos(pos.getX(), y, pos.getZ());
// we know it's on top of grass or dirt, but what is here already?
Block toReplace = worldIn.getBlockState(bushPos).getBlock();
// only place bush if it is air or plant
if(toReplace == Blocks.air || toReplace.getMaterial() == Material.plants)
{
// set the block to a bush
// use 2 as the flag to prevent update -- you don't have to include that parameter
worldIn.setBlockState(bushPos, cookieBush.getDefaultState(), 2);
// debug:
// System.out.println("placed a cookie bush!");
} // else System.out.println("Sadly, this block is occupied by " + toReplace.getUnlocalizedName());
}
return false;
}
// find a grass or dirt block to place the bush on
public static int getGroundFromAbove(World world, int x, int z)
{
int y = 255;
boolean foundGround = false;
while(!foundGround && y-- >= 0)
{
Block blockAt = world.getBlockState(new BlockPos(x,y,z)).getBlock();
// "ground" for our bush is grass or dirt
foundGround = blockAt == Blocks.dirt || blockAt == Blocks.grass;
}
return y;
}
}
Next we have to tell Forge when to call this class. We do this by making and registering an IWorldGenerator.
Make an IWorldGenerator:
I named mine TutorialWorldGenerator. This class will be called every time a new chunk is generated. Implementing IWorldGenerator forces us to override a method that uses chunk coordinates.
First I use the default method to check the dimension and call different methods if I'm in the Overworld, Nether, or End.
Inside generateOverworld, I check the biome. I only want my bushes to appear in Plains. Then I call the WorldGen class I made to place a single cookie bush. There are hundreds of ways to accomplish this, but I chose to keep it straightforward.
public class TutorialWorldGenerator implements IWorldGenerator
{
@Override
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider)
{
// these are important!
int blockX = chunkX * 16;
int blockZ = chunkZ * 16;
// generate differently based on dimension
switch(world.provider.getDimensionId())
{
case -1: generateNether(world, random, blockX, blockZ);
break;
case 0: generateOverworld(world, random, blockX, blockZ);
break;
case 1: generateEnd(world, random, blockX, blockZ);
break;
}
}
private void generateNether(World world, Random rand, int blockX, int blockZ)
{
// leaving blank for now
}
private void generateOverworld(World world, Random rand, int blockX, int blockZ)
{
/** COOKIE BUSH GEN **/
// make a world generator to use
WorldGenerator genCookieBushes = new WorldGenCookieBushes();
// get the biome. I used 64 for Y, but you can use anything between 0 and 255
BiomeGenBase biome = world.getBiomeGenForCoords(new BlockPos(blockX, 64, blockZ));
// check that it's a Plains biome
// we could also use: if(biome instanceof BiomeGenPlains)
if(biome == BiomeGenBase.plains)
{
// how many we want to make per chunk
// let's make it random between MIN and MAX
int MIN = 4;
int MAX = 12;
int numBushes = MIN + rand.nextInt(MAX - MIN);
// now let's generate the bushes
for(int i = 0; i < numBushes; i++)
{
// get a random position in the chunk
int randX = blockX + rand.nextInt(16);
int randZ = blockZ + rand.nextInt(16);
// the y-value we pass here will be used as minimum spawn height (in our generator, anyway)
genCookieBushes.generate(world, rand, new BlockPos(randX, 24, randZ));
}
}
/** END COOKIE BUSH GEN **/
}
private void generateEnd(World world, Random rand, int blockX, int blockZ)
{
// leaving blank for now
}
/** HELPER METHODS **/
// find a grass or dirt block to place the bush on
public static int getGroundFromAbove(World world, int x, int z)
{
int y = 255;
boolean foundGround = false;
while(!foundGround && y-- >= 0)
{
Block blockAt = world.getBlockState(new BlockPos(x,y,z)).getBlock();
// "ground" for our bush is grass or dirt
foundGround = blockAt == Blocks.dirt || blockAt == Blocks.grass;
}
return y;
}
}
We register this, by the way, from the FMLInitializationEvent part of the FML life cycle. I place this in my init method:
// 10 here is the weight. Higher number = generates later
GameRegistry.registerWorldGenerator(new TutorialWorldGenerator(), 10);
And, that's it! Let's see what happens when I load up the game:
Awesome, it worked!
More information about WorldGenerators:
Take a look at the Minecraft world generators in net.minecraft.world.gen.feature
One way we could have done the bush generation is to give WorldGenCookieBushes a block position and tell it to make a bunch of bushes around it. WorldGenPumpkin and WorldGenMelon both do this.
If you are interested in a larger structure, maybe look at WorldGenIceSpike. Complicated, right? It doesn't have to be that way -- you can find a technique that works for you instead.
Personally I use arrays of Block positions when I make a large structure.
Personally I use arrays of Block positions when I make a large structure. If that interests you, let me know so I can post a tutorial about how I do that.
That sounds great! Can you make another Tutorial for this?
That sounds great! Can you make another Tutorial for this?
Okay, done. I described how to make a structure that has about 3 dozen blocks. Rather than list 3 dozen "world.setBlockState" calls and BlockPos instances, I make an array of relative locations for each type of block. I then iterate through the offsets using a for-each loop, add them to my original BlockPos, and use that to set the block more efficiently.
That I do not know, sorry. A quick google search turned up this post asking the same question (with code included, which may get you started) and this 1.7.10 explanation, which may also help.
Rollback Post to RevisionRollBack
Click this banner for a list of illegal mod distributors -- only download from legal sites!
Structure Generation for 1.8.9
A lot of people have asked in the Modification Development section how they make custom structures. There are a handful of good tutorials for 1.7.10 on this, and the basic idea has not changed much. But for beginners in 1.8 and beyond, I present a tutorial for custom structure generation.
The whole mod (all the files together) can be found on GitHub.
Objective:
We want a bunch of Cookie Bushes (our custom block) to appear in Plains biomes randomly.
Preparation:
First we need the bush block. We want this bush to, when clicked, give the player a cookie. Then it will replace itself with a bush that has no cookies, which will eventually replace itself with a bush that does have cookies.
We want two bushes: one that will give cookies, and one that will not. It's simple to make the non-cookie bush change randomly.
BlockCookieBushFull:
BlockCookieBushEmpty:
We register both these bushes and the JSONs for them. I can post those if you guys want them. I override the isFullCube method and the others because I want my block to look like a cross, like flowers do.
Right, so now on to the generation:
Make a WorldGenerator class:
I made a class called WorldGenCookieBushes that extends WorldGenerator. Inside the generate method that I am forced to override, I do several things:
- figure out what height to generate at
- make sure it is only replacing air, liquids, or plants
- make sure it is on top of dirt or grass
- actually place the block
Here's what I ended up with:
Next we have to tell Forge when to call this class. We do this by making and registering an IWorldGenerator.
Make an IWorldGenerator:
I named mine TutorialWorldGenerator. This class will be called every time a new chunk is generated. Implementing IWorldGenerator forces us to override a method that uses chunk coordinates.
First I use the default method to check the dimension and call different methods if I'm in the Overworld, Nether, or End.
Inside generateOverworld, I check the biome. I only want my bushes to appear in Plains. Then I call the WorldGen class I made to place a single cookie bush. There are hundreds of ways to accomplish this, but I chose to keep it straightforward.
We register this, by the way, from the FMLInitializationEvent part of the FML life cycle. I place this in my init method:
And, that's it! Let's see what happens when I load up the game:
Awesome, it worked!
More information about WorldGenerators:
Take a look at the Minecraft world generators in net.minecraft.world.gen.feature
One way we could have done the bush generation is to give WorldGenCookieBushes a block position and tell it to make a bunch of bushes around it. WorldGenPumpkin and WorldGenMelon both do this.
If you are interested in a larger structure, maybe look at WorldGenIceSpike. Complicated, right? It doesn't have to be that way -- you can find a technique that works for you instead.
Personally I use arrays of Block positions when I make a large structure.
Next up:
Complex Structure Generation
Have any other questions or comments? Post below!
If this was helpful, you can let me know by pressing the little green arrow down there. I might make more tutorials -- what do you want to know next?
That sounds great! Can you make another Tutorial for this?
Okay, done. I described how to make a structure that has about 3 dozen blocks. Rather than list 3 dozen "world.setBlockState" calls and BlockPos instances, I make an array of relative locations for each type of block. I then iterate through the offsets using a for-each loop, add them to my original BlockPos, and use that to set the block more efficiently.
Tutorial for Complex Structure Generation.
Ok, so i followed the tutorial but on this line there is an error in my WorldGeneration class:
It says, "Type mismatch, cannot convert WorldGenCottonPlant to WorldGenerator" and when i change WorldGenerator it just says the same thing.
I love your randomizing math function for generating the bush/structure. GREAT STUFF!
How do we specify for our object to spawn as part of a village? Say, add a field?
[SSSS]
That I do not know, sorry. A quick google search turned up this post asking the same question (with code included, which may get you started) and this 1.7.10 explanation, which may also help.
Man, you got a different set of results than I did. Thank you, Looks like I can figure It out from those.
[SSSS]
What would I do to make mushrooms spawn in darkness with this they only spawn in daylight(I made sure that they can generate on stone).
Look at the mushroom code in minecraft!
[SSSS]
Where can I find the mushroom code, I have been looking at the worldgen and all I have found were big mushroom generation
net.minecraft.block.BlockMushrrom
updateTick dictates the spread of small mushrooms, canBlockStay includes the light level part.
net.minecraft.world.biome.BiomeDecorator places small mushrooms n the world.
start at line 114/115
[SSSS]
Ok, I will check it out. Thank you