i created this code for a 3d endless maze explorer with a minimap, but the minimap appears on one of the walls of the maze instead of being a 2d overlay. everytime i try to embed it as a 2d overlay on top of the 3d game things start get really visually glitchy i see the map overall of the walls and some really strange bugs.
my question is what would be the best way to add the minimap as a 2d overlay on the top right of the screen?
// mySketch.pde
import java.util.HashMap;
import java.util.Random;
import java.util.ArrayList;
import java.util.Stack;
// Player position and viewing angles
float x, y, z;
float angleX, angleY;
float targetAngleX, targetAngleY;
float angleSmoothing = 0.1f;
// Movement settings
float speed = 5;
float friction = 0.85f;
PVector velocity;
// Maze settings
int cellSize = 200;
int wallHeight = 300;
float playerRadius = 30;
float playerHeight = 150;
// Textures
PImage wallTexture;
PImage floorTexture;
// Maze chunks
HashMap<String, MazeChunk> mazeChunks;
int chunkSize = 20;
int renderDistance = 2;
// Movement keys
boolean moveForward = false;
boolean moveBackward = false;
boolean moveLeft = false;
boolean moveRight = false;
boolean running = false;
// Minimap settings
PGraphics minimap;
int minimapSize;
void setup() {
fullScreen(P3D);
noCursor();
smooth(4);
// Initialize textures
generateTextures();
// Initialize mouse positions
pmouseX = mouseX;
pmouseY = mouseY;
// Set the perspective
float fov = PI / 3;
float aspect = (float) width / height;
float near = 0.1f;
float far = 10000;
perspective(fov, aspect, near, far);
x = cellSize * 1.5f;
y = playerHeight;
z = cellSize * 1.5f;
angleX = 0;
angleY = 0;
targetAngleX = angleX;
targetAngleY = angleY;
velocity = new PVector(0, 0, 0);
mazeChunks = new HashMap<String, MazeChunk>();
// Initialize minimap
minimapSize = 300; // Adjust as needed
minimap = createGraphics(minimapSize, minimapSize, P2D); // Use P2D renderer for 2D drawing
}
void draw() {
background(0);
// 3D Drawing
lights();
ambientLight(100, 100, 100);
directionalLight(150, 150, 150, -0.5f, -1, -0.5f);
// Smoothly interpolate camera angles
angleX = lerp(angleX, targetAngleX, angleSmoothing);
angleY = lerp(angleY, targetAngleY, angleSmoothing);
// Adjust speed based on running
float currentSpeed = speed;
if (running) {
currentSpeed *= 1.5;
}
// Update player velocity and position based on input
PVector inputDir = new PVector(0, 0, 0);
// Get the forward and right directions
PVector forwardDir = new PVector(cos(angleY), 0, sin(angleY));
PVector rightDir = new PVector(-sin(angleY), 0, cos(angleY));
if (moveForward) {
inputDir.add(forwardDir);
}
if (moveBackward) {
inputDir.sub(forwardDir);
}
if (moveLeft) {
inputDir.sub(rightDir);
}
if (moveRight) {
inputDir.add(rightDir);
}
if (inputDir.mag() > 0) {
inputDir.normalize();
inputDir.mult(currentSpeed);
velocity.lerp(inputDir, 0.2);
} else {
velocity.mult(friction);
}
// Update player position with collision detection
float newX = x + velocity.x;
float newZ = z + velocity.z;
if (!checkCollision(newX, newZ)) {
x = newX;
z = newZ;
} else {
velocity.set(0, 0, 0);
}
// Set the camera
camera(x, y, z,
x + cos(angleY) * cos(angleX), y + sin(angleX),
z + sin(angleY) * cos(angleX), 0, 1, 0);
// Update maze chunks
updateMazeChunks();
// Draw the maze
drawMaze();
// Update and draw the minimap last
updateMinimap();
drawMinimap();
}
void generateTextures() {
// Wall texture
wallTexture = createImage(64, 64, RGB);
wallTexture.loadPixels();
for (int i = 0; i < wallTexture.width; i++) {
for (int j = 0; j < wallTexture.height; j++) {
float shade = random(100, 150);
wallTexture.pixels[j * wallTexture.width + i] = color(shade);
}
}
wallTexture.updatePixels();
// Floor texture
floorTexture = createImage(64, 64, RGB);
floorTexture.loadPixels();
for (int i = 0; i < floorTexture.width; i++) {
for (int j = 0; j < floorTexture.height; j++) {
float shade = random(80, 130);
floorTexture.pixels[j * floorTexture.width + i] = color(shade);
}
}
floorTexture.updatePixels();
}
void updateMazeChunks() {
int playerChunkX = floor(x / (cellSize * chunkSize));
int playerChunkZ = floor(z / (cellSize * chunkSize));
// Remove chunks out of render distance
ArrayList<String> keysToRemove = new ArrayList<String>();
for (String key : mazeChunks.keySet()) {
String[] coords = key.split(",");
int chunkX = Integer.parseInt(coords[0]);
int chunkZ = Integer.parseInt(coords[1]);
if (abs(chunkX - playerChunkX) > renderDistance ||
abs(chunkZ - playerChunkZ) > renderDistance) {
keysToRemove.add(key);
}
}
for (String key : keysToRemove) {
mazeChunks.remove(key);
}
// Add new chunks
for (int i = -renderDistance; i <= renderDistance; i++) {
for (int j = -renderDistance; j <= renderDistance; j++) {
int chunkX = playerChunkX + i;
int chunkZ = playerChunkZ + j;
String key = chunkX + "," + chunkZ;
if (!mazeChunks.containsKey(key)) {
// Determine openings
boolean openLeft = false;
boolean openRight = false;
boolean openTop = false;
boolean openBottom = false;
String leftKey = (chunkX - 1) + "," + chunkZ;
if (mazeChunks.containsKey(leftKey)) {
openLeft = mazeChunks.get(leftKey).openRight;
}
String rightKey = (chunkX + 1) + "," + chunkZ;
if (mazeChunks.containsKey(rightKey)) {
openRight = mazeChunks.get(rightKey).openLeft;
}
String topKey = chunkX + "," + (chunkZ - 1);
if (mazeChunks.containsKey(topKey)) {
openTop = mazeChunks.get(topKey).openBottom;
}
String bottomKey = chunkX + "," + (chunkZ + 1);
if (mazeChunks.containsKey(bottomKey)) {
openBottom = mazeChunks.get(bottomKey).openTop;
}
// Generate the maze chunk
MazeChunkData chunkData = generateMazeChunk(chunkSize, chunkSize,
chunkX, chunkZ, openLeft, openRight, openTop, openBottom);
PShape newChunkShape = createMazeChunkShape(chunkData.maze,
chunkX, chunkZ);
MazeChunk newChunk = new MazeChunk(chunkData.maze, newChunkShape,
chunkX, chunkZ, chunkData.openLeft, chunkData.openRight,
chunkData.openTop, chunkData.openBottom);
mazeChunks.put(key, newChunk);
}
}
}
}
void drawMaze() {
// Draw all chunks
for (MazeChunk chunk : mazeChunks.values()) {
shape(chunk.shape);
}
}
void updateMinimap() {
minimap.beginDraw();
minimap.background(50, 50, 50, 200); // Semi-transparent background
// Set fill and stroke for maze cells
minimap.stroke(255); // White outline
minimap.fill(100, 100, 100); // Gray fill
// Adjusted scale factor
float mapWidth = (renderDistance * 2 + 1) * chunkSize * cellSize;
float scale = minimapSize / mapWidth * 4; // Adjust as needed
// Center the minimap on the player
minimap.pushMatrix();
minimap.translate(minimapSize / 2, minimapSize / 2);
minimap.rotate(-angleY);
// Draw maze walls
for (MazeChunk chunk : mazeChunks.values()) {
int chunkX = chunk.chunkX;
int chunkZ = chunk.chunkZ;
int[][] maze = chunk.maze;
int baseX = chunkX * chunkSize * cellSize;
int baseZ = chunkZ * chunkSize * cellSize;
for (int i = 0; i < chunkSize; i++) {
for (int j = 0; j < chunkSize; j++) {
if (maze[i][j] == 1) {
float cellX = baseX + i * cellSize - x;
float cellZ = baseZ + j * cellSize - z;
float drawX = cellX * scale;
float drawY = cellZ * scale;
minimap.rect(drawX, drawY, cellSize * scale, cellSize * scale);
}
}
}
}
// Draw the player position
minimap.fill(255, 0, 0); // Red color
minimap.noStroke();
minimap.ellipse(0, 0, 15, 15);
minimap.popMatrix();
minimap.endDraw();
}
void drawMinimap() {
// Save the current transformation and style
pushStyle();
pushMatrix();
// Disable depth test
hint(DISABLE_DEPTH_TEST);
// Draw the minimap image
image(minimap, width - minimapSize - 10, 10);
// Draw the minimap border
noFill();
stroke(255);
rect(width - minimapSize - 10, 10, minimapSize, minimapSize);
// Re-enable depth test
hint(ENABLE_DEPTH_TEST);
// Restore the style and transformations
popMatrix();
popStyle();
}
// MazeChunk class
class MazeChunk {
int[][] maze;
PShape shape;
int chunkX, chunkZ;
boolean openLeft, openRight, openTop, openBottom;
MazeChunk(int[][] maze, PShape shape, int chunkX, int chunkZ,
boolean openLeft, boolean openRight,
boolean openTop, boolean openBottom) {
this.maze = maze;
this.shape = shape;
this.chunkX = chunkX;
this.chunkZ = chunkZ;
this.openLeft = openLeft;
this.openRight = openRight;
this.openTop = openTop;
this.openBottom = openBottom;
}
}
// MazeChunkData class
class MazeChunkData {
int[][] maze;
boolean openLeft, openRight, openTop, openBottom;
MazeChunkData(int[][] maze, boolean openLeft, boolean openRight,
boolean openTop, boolean openBottom) {
this.maze = maze;
this.openLeft = openLeft;
this.openRight = openRight;
this.openTop = openTop;
this.openBottom = openBottom;
}
}
PShape createMazeChunkShape(int[][] maze, int chunkX, int chunkZ) {
PShape chunkShape = createShape();
chunkShape.beginShape(TRIANGLES);
chunkShape.noStroke();
chunkShape.textureMode(NORMAL);
int baseX = chunkX * chunkSize;
int baseZ = chunkZ * chunkSize;
chunkShape.texture(floorTexture);
// Draw floor and ceiling
for (int i = 0; i < chunkSize; i++) {
for (int j = 0; j < chunkSize; j++) {
float x0 = (baseX + i) * cellSize;
float z0 = (baseZ + j) * cellSize;
float x1 = x0 + cellSize;
float z1 = z0 + cellSize;
// Floor
chunkShape.normal(0, 1, 0);
chunkShape.vertex(x0, 0, z0, 0, 0);
chunkShape.vertex(x1, 0, z0, 1, 0);
chunkShape.vertex(x1, 0, z1, 1, 1);
chunkShape.vertex(x0, 0, z0, 0, 0);
chunkShape.vertex(x1, 0, z1, 1, 1);
chunkShape.vertex(x0, 0, z1, 0, 1);
// Ceiling
chunkShape.normal(0, -1, 0);
chunkShape.vertex(x0, wallHeight, z0, 0, 0);
chunkShape.vertex(x1, wallHeight, z1, 1, 1);
chunkShape.vertex(x1, wallHeight, z0, 1, 0);
chunkShape.vertex(x0, wallHeight, z0, 0, 0);
chunkShape.vertex(x0, wallHeight, z1, 0, 1);
chunkShape.vertex(x1, wallHeight, z1, 1, 1);
}
}
chunkShape.texture(wallTexture);
// Draw walls
for (int i = 0; i < chunkSize; i++) {
for (int j = 0; j < chunkSize; j++) {
if (maze[i][j] == 1) {
float x0 = (baseX + i) * cellSize;
float z0 = (baseZ + j) * cellSize;
float x1 = x0 + cellSize;
float z1 = z0 + cellSize;
// Front wall (+Z)
chunkShape.normal(0, 0, 1);
chunkShape.vertex(x0, 0, z1, 0, 1);
chunkShape.vertex(x1, 0, z1, 1, 1);
chunkShape.vertex(x1, wallHeight, z1, 1, 0);
chunkShape.vertex(x0, 0, z1, 0, 1);
chunkShape.vertex(x1, wallHeight, z1, 1, 0);
chunkShape.vertex(x0, wallHeight, z1, 0, 0);
// Back wall (-Z)
chunkShape.normal(0, 0, -1);
chunkShape.vertex(x1, 0, z0, 0, 1);
chunkShape.vertex(x0, 0, z0, 1, 1);
chunkShape.vertex(x0, wallHeight, z0, 1, 0);
chunkShape.vertex(x1, 0, z0, 0, 1);
chunkShape.vertex(x0, wallHeight, z0, 1, 0);
chunkShape.vertex(x1, wallHeight, z0, 0, 0);
// Left wall (-X)
chunkShape.normal(-1, 0, 0);
chunkShape.vertex(x0, 0, z0, 0, 1);
chunkShape.vertex(x0, 0, z1, 1, 1);
chunkShape.vertex(x0, wallHeight, z1, 1, 0);
chunkShape.vertex(x0, 0, z0, 0, 1);
chunkShape.vertex(x0, wallHeight, z1, 1, 0);
chunkShape.vertex(x0, wallHeight, z0, 0, 0);
// Right wall (+X)
chunkShape.normal(1, 0, 0);
chunkShape.vertex(x1, 0, z1, 0, 1);
chunkShape.vertex(x1, 0, z0, 1, 1);
chunkShape.vertex(x1, wallHeight, z0, 1, 0);
chunkShape.vertex(x1, 0, z1, 0, 1);
chunkShape.vertex(x1, wallHeight, z0, 1, 0);
chunkShape.vertex(x1, wallHeight, z1, 0, 0);
}
}
}
chunkShape.endShape();
return chunkShape;
}
MazeChunkData generateMazeChunk(int width, int height, int chunkX,
int chunkZ, boolean openLeft,
boolean openRight, boolean openTop,
boolean openBottom) {
int[][] maze = new int[width][height];
// Initialize all cells as walls
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
maze[i][j] = 1;
}
}
// Random number generator with seed
Random rng = new Random(computeHash(chunkX, chunkZ));
// Use a stack for backtracking
Stack<int[]> stack = new Stack<>();
int startX = rng.nextInt(width / 2) * 2 + 1;
int startY = rng.nextInt(height / 2) * 2 + 1;
maze[startX][startY] = 0;
stack.push(new int[]{startX, startY});
// Directions: right, down, left, up
int[][] directions = {{2, 0}, {0, 2}, {-2, 0}, {0, -2}};
while (!stack.isEmpty()) {
int[] current = stack.peek();
int cx = current[0];
int cy = current[1];
ArrayList<int[]> neighbors = new ArrayList<>();
for (int[] dir : directions) {
int nx = cx + dir[0];
int ny = cy + dir[1];
if (nx >= 1 && nx < width - 1 && ny >= 1 && ny < height - 1 &&
maze[nx][ny] == 1) {
neighbors.add(new int[]{nx, ny});
}
}
if (!neighbors.isEmpty()) {
int[] next = neighbors.get(rng.nextInt(neighbors.size()));
int nx = next[0];
int ny = next[1];
// Open the wall
maze[(cx + nx) / 2][(cy + ny) / 2] = 0;
maze[nx][ny] = 0;
stack.push(next);
} else {
stack.pop();
}
}
// Ensure connections
if (openLeft) {
maze[0][height / 2] = 0;
}
if (openRight) {
maze[width - 1][height / 2] = 0;
}
if (openTop) {
maze[width / 2][0] = 0;
}
if (openBottom) {
maze[width / 2][height - 1] = 0;
}
// Randomly create additional openings
if (!openLeft && rng.nextBoolean()) {
maze[0][rng.nextInt(height / 2) * 2 + 1] = 0;
openLeft = true;
}
if (!openRight && rng.nextBoolean()) {
maze[width - 1][rng.nextInt(height / 2) * 2 + 1] = 0;
openRight = true;
}
if (!openTop && rng.nextBoolean()) {
maze[rng.nextInt(width / 2) * 2 + 1][0] = 0;
openTop = true;
}
if (!openBottom && rng.nextBoolean()) {
maze[rng.nextInt(width / 2) * 2 + 1][height - 1] = 0;
openBottom = true;
}
return new MazeChunkData(maze, openLeft, openRight, openTop, openBottom);
}
boolean checkCollision(float newX, float newZ) {
int cellX = floor(newX / cellSize);
int cellZ = floor(newZ / cellSize);
// Get chunk coordinates
int chunkX = floor((float) cellX / chunkSize);
int chunkZ = floor((float) cellZ / chunkSize);
String chunkKey = chunkX + "," + chunkZ;
MazeChunk chunk = mazeChunks.get(chunkKey);
if (chunk != null) {
// Local cell coordinates
int localX = ((cellX % chunkSize) + chunkSize) % chunkSize;
int localZ = ((cellZ % chunkSize) + chunkSize) % chunkSize;
// Check for collision with walls
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
int globalCellX = cellX + i;
int globalCellZ = cellZ + j;
int neighborChunkX = floor((float) globalCellX / chunkSize);
int neighborChunkZ = floor((float) globalCellZ / chunkSize);
String neighborChunkKey = neighborChunkX + "," + neighborChunkZ;
MazeChunk neighborChunk = mazeChunks.get(neighborChunkKey);
if (neighborChunk != null) {
int neighborLocalX = ((globalCellX % chunkSize) + chunkSize) % chunkSize;
int neighborLocalZ = ((globalCellZ % chunkSize) + chunkSize) % chunkSize;
if (neighborLocalX >= 0 && neighborLocalX < chunkSize &&
neighborLocalZ >= 0 && neighborLocalZ < chunkSize) {
if (neighborChunk.maze[neighborLocalX][neighborLocalZ] == 1) {
// Calculate the distance to the wall
float wallX = (globalCellX + 0.5f) * cellSize;
float wallZ = (globalCellZ + 0.5f) * cellSize;
float dx = newX - wallX;
float dz = newZ - wallZ;
float distanceX = abs(dx) - (cellSize / 2 + playerRadius);
float distanceZ = abs(dz) - (cellSize / 2 + playerRadius);
if (distanceX < 0 && distanceZ < 0) {
return true; // Collision with wall
}
}
}
}
}
}
}
return false; // No collision
}
void mouseMoved() {
targetAngleY += (mouseX - pmouseX) * 0.005f;
targetAngleX += (mouseY - pmouseY) * 0.005f;
targetAngleX = constrain(targetAngleX, -PI / 2 + 0.1f,
PI / 2 - 0.1f);
pmouseX = mouseX;
pmouseY = mouseY;
}
void keyPressed() {
if (key == 'w' || key == 'W' || keyCode == UP) moveForward = true;
if (key == 's' || key == 'S' || keyCode == DOWN) moveBackward = true;
if (key == 'a' || key == 'A' || keyCode == LEFT) moveLeft = true;
if (key == 'd' || key == 'D' || keyCode == RIGHT) moveRight = true;
if (keyCode == SHIFT) running = true;
}
void keyReleased() {
if (key == 'w' || key == 'W' || keyCode == UP) moveForward = false;
if (key == 's' || key == 'S' || keyCode == DOWN) moveBackward = false;
if (key == 'a' || key == 'A' || keyCode == LEFT) moveLeft = false;
if (key == 'd' || key == 'D' || keyCode == RIGHT) moveRight = false;
if (keyCode == SHIFT) running = false;
}
int computeHash(int x, int z) {
// Combine x and z into a single hash value
int h = 31;
h = h * 17 + x;
h = h * 17 + z;
return h;
}