Implementin blocks in a Minecraft-like, AI-heavy, complex game with huge worlds

I’m developing an AI-driven fantasy world generator(Meaning that the resident NPCs will take care of doing everything after the initial “normal” generation.) with the aim of making it generate the most engaging worlds possible. Basically my own Dwarf Fortress to lord over, gameplay- and complexity- wise. I’m starting out small, though, so for now, it’s a 100x100 plate of rock and few NPCs on it. I want to see if they will properly seek out water and food if I make them go thirsty and hungry.

But first, I wanted to ask something about my implementation of the Block class, intended to represent every single block in the game world:

public class Block {
    int posX, posY, posZ;
    Material material;
    BlockShape shape;
    ArrayList<Object> objects;


    public Block(int posX, int posY, int posZ) {
        this.posX = posX;
        this.posY = posY;
        this.posZ = posZ;
    }
}

The idea is for the blocks to have obviously their coordinates, then the material they’re made of(dirst, iron ore, adamantium,…), then their shape(solid, ramp, column, hewn wall,…), and then a list of diverse other objects that may be in the block(NPCs, furniture, projectile, effect of a spell, item, remains, some flora, fluid…). Coordinates stay the same, the other stuff is initialized(and deleted) as needed during world generation/gameplay. So, folks with a more extensive knowledge and experience with running times and data structure(I know, technically, there’s no data structure yet, but bear with me) optimization: Do you see any obvious red lights in there; could this cause a huge slowdown or save bloat? There’s gonna be a lot of these in the game, and they’re going to go through a huge number of AI checks, so I figured out I’d ask beforehand, rather than having to weed out such an elementary feature out of everything later and rework it.

Well for material you can have a list of materials and simply store a reference integer for each block.

static ArrayList<Material> materials;

public class Block {
    int posX, posY, posZ;
    int materialId;
    BlockShape shape;
    ArrayList<Object> objects;


    public Block(int posX, int posY, int posZ) {
        this.posX = posX;
        this.posY = posY;
        this.posZ = posZ;
    }

// now you get material with materials.get(materialId)
}

Also, if you are only planning on one or two objects per block, then just use a field or two, because using ArrayLists can use a decent amount of memory.

How does dividing the world into arbitrary blocks help you? It just seems to introduce another thing to loop over.

It’s the most flexible implementation as far as I know. Generate all the volcanoes and crevices you want, have dwarves and drow and undead and gnomes and whatnot carve cities underground, have the Old Ones build some underwater, have the mages make theirs float, have more architectural styles in the game, have the AI use the local materials for building, make the world dried and have civilizations die leaving their ruins after them… What would you suggest instead?

Only store the entities. Most of those blocks are just cubic meters of air or rock or water, and then you are going to be looping over all of these and then looping over their objects lists to find the actual entities?

The entities will be stored in their own list, looping over that will be what will make the game tick, literally. Blocks will only be called when either an entity does something to them, such as move into them or mine them(I don’t know what to do about liquids yet, though. Looping through them and checking the pressure and whatnot would murder the FPS), or when the graphics will need to know what to display.

I haven’t yet decided on the data structure in which to store the blocks. Something with fast enough access for all functionality required, but not too much bloat due to excessive references.

If you can derive X,Y,Z from the location in the block data structure, you can remove these 3 fields from the Block class.

Simple example: currently you store 3 ints = 3x4 bytes = 12 bytes worth of coordinates in each block. For 100x100x100 blocks that’s 12MB. If you create a Block[W][H][D], and loop over it, you get access to these coordinates by iteration, where the Block[][][] will ‘only’ take 4MB. Ofcourse, this is just a trivial example. A Block[][][] has its downsides too, but it all depends on how sparse you expect your space to be. In case of an extremely sparse space, you could fall back to HashMap<Coord3d, Block>. Whichever way you chose, I find it very unlikely that the data structure holding the blocks will not be able to provide you with the coordinates, so you can safely remove them from the Block class.

I suppose a question would be: how big is the world, and how big are the blocks?