Skip to content

Commit

Permalink
Split tile arrays into Chunks (MinicraftPlus#727)
Browse files Browse the repository at this point in the history
  • Loading branch information
Litorom authored Feb 10, 2025
2 parents 8c12cf7 + dbedb35 commit 85ffbb6
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 178 deletions.
77 changes: 77 additions & 0 deletions src/client/java/minicraft/level/ChunkManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package minicraft.level;

import minicraft.level.tile.Tile;
import minicraft.level.tile.Tiles;

public class ChunkManager {

public static final int CHUNK_SIZE = 64;

/**
* 2D array for tile IDs and data.
* Outer array is widthInChunks x heightInChunks and represents all chunks
* Inner arrays is CHUNK_SIZE x CHUNK_SIZE and represents tile ids and data
*
* In the future, can be changed to
* Map<Integer, Map<Integer, List<Short>>> (or some equivalent) to allow negative coordinates and a pseudo-infinite map
*/
public short[][] chunkedData, chunkedTiles;

public int levelWidth, levelHeight;
private int widthInChunks, heightInChunks;

public ChunkManager(int levelWidth, int levelHeight) {
this.levelWidth = levelWidth;
this.levelHeight = levelHeight;
// Create enough chunks to cover the number of tiles provided
widthInChunks = (int)Math.ceil((double)levelWidth / CHUNK_SIZE);
heightInChunks = (int)Math.ceil((double)levelHeight / CHUNK_SIZE);
chunkedData = new short[widthInChunks * heightInChunks][CHUNK_SIZE * CHUNK_SIZE];
chunkedTiles = new short[widthInChunks * heightInChunks][CHUNK_SIZE * CHUNK_SIZE];
}

/**
* Return the chunk index in which the tileX and tileY land, or null if out of bounds
*/
private int getChunkIndex(int tileX, int tileY) {
if(tileX < 0 || tileY < 0 || tileX >= levelWidth || tileY >= levelHeight /* || (tileX + tileY * levelWidth / Chunk.SIZE) >= chunks.length */)
return -1;
return tileX / CHUNK_SIZE + (tileY / CHUNK_SIZE) * widthInChunks;
}

/**
* Returns a tile. After finding the right chunk, mods x and y to the range 0-CHUNK_SIZE as to never be out of bounds
*/
public Tile getTile(int x, int y) {
int c = getChunkIndex(x, y);
if(c == -1) return Tiles.get("connector tile");
return Tiles.get(chunkedTiles[c][(x % CHUNK_SIZE) + (y % CHUNK_SIZE) * CHUNK_SIZE]);
}

/**
* Updates a tile. After finding the right chunk, mods x and y to the range 0-CHUNK_SIZE as to never be out of bounds
*/
public void setTile(int x, int y, Tile t, int dataVal) {
int c = getChunkIndex(x, y);
if(c == -1) return;

int tileInd = (x % CHUNK_SIZE) + (y % CHUNK_SIZE) * CHUNK_SIZE;
chunkedTiles[c][tileInd] = t.id;
chunkedData[c][tileInd] = (short) dataVal;
}

public int getData(int x, int y) {
int c = getChunkIndex(x, y);
if(c == -1) return 0;

return chunkedData[c][(x % CHUNK_SIZE) + (y % CHUNK_SIZE) * CHUNK_SIZE] & 0xFFFF;
}

public void setData(int x, int y, int val) {
int c = getChunkIndex(x, y);
if(c == -1) return;

chunkedData[c][(x % CHUNK_SIZE) + (y % CHUNK_SIZE) * CHUNK_SIZE] = (short) val;
}

}
35 changes: 11 additions & 24 deletions src/client/java/minicraft/level/Level.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ public static String getDepthString(int depth) {
public int w, h; // Width and height of the level
private final long seed; // The used seed that was used to generate the world

public short[] tiles; // An array of all the tiles in the world.
public short[] data; // An array of the data of the tiles in the world.
public ChunkManager chunkManager; // A collection of chunks with it's own interface

public final TreeTile.TreeType[] treeTypes; // An array of tree types

Expand Down Expand Up @@ -186,24 +185,18 @@ public Level(int w, int h, long seed, int level, Level parentLevel, boolean make
updateMobCap();

if (!makeWorld) {
int arrsize = w * h;
tiles = new short[arrsize];
data = new short[arrsize];
chunkManager = new ChunkManager(w, h);
return;
}

Logging.WORLD.debug("Making level " + level + "...");

maps = LevelGen.createAndValidateMap(w, h, level, seed);
if (maps == null) {
Logging.WORLD.error("Level generation: Returned maps array is null");
chunkManager = LevelGen.createAndValidateMap(w, h, level, seed);
if (chunkManager == null) {
Logging.WORLD.error("Level generation: Returned chunks array is null");
return;
}

tiles = maps[0]; // Assigns the tiles in the map
data = maps[1]; // Assigns the data of the tiles


if (level < 0)
generateSpawnerStructures();

Expand Down Expand Up @@ -564,8 +557,7 @@ private void sortAndRender(Screen screen, List<Entity> list) {
}

public Tile getTile(int x, int y) {
if (x < 0 || y < 0 || x >= w || y >= h /* || (x + y * w) >= tiles.length*/) return Tiles.get("connector tile");
return Tiles.get(tiles[x + y * w]);
return chunkManager.getTile(x, y);
}

/**
Expand All @@ -587,21 +579,16 @@ public void setTile(int x, int y, Tile t) {
}

public void setTile(int x, int y, Tile t, int dataVal) {
if (x < 0 || y < 0 || x >= w || y >= h) return;

tiles[x + y * w] = t.id;
data[x + y * w] = (short) dataVal;
t.onTileSet(this, x, y);
chunkManager.setTile(x, y, t, dataVal);
getTile(x, y).onTileSet(this, x, y);
}

public int getData(int x, int y) {
if (x < 0 || y < 0 || x >= w || y >= h) return 0;
return data[x + y * w] & 0xFFFF;
return chunkManager.getData(x, y);
}

public void setData(int x, int y, int val) {
if (x < 0 || y < 0 || x >= w || y >= h) return;
data[x + y * w] = (short) val;
chunkManager.setData(x, y, val);
}

public void add(Entity e) {
Expand Down Expand Up @@ -1198,7 +1185,7 @@ private void generateDungeonStructures() {
*/
public void regenerateBossRoom() {
if (depth == -4) {
Structure.dungeonBossRoom.draw(tiles, w / 2, h / 2, w); // Generating the boss room at the center.
Structure.dungeonBossRoom.draw(chunkManager, w / 2, h / 2); // Generating the boss room at the center.
for (int x = w / 2 - 4; x < w / 2 + 5; x++) { // Resetting tile data.
for (int y = h / 2 - 4; y < h / 2 + 5; y++) {
setData(x, y, 0);
Expand Down
Loading

0 comments on commit 85ffbb6

Please sign in to comment.