xxxxxxxxxx
191
const DEBUG_GRID = true; // Toggle debug grid on/off
const DETAIL_LEVEL = 4; // Detail level for chunks
const RANDOMNESS_FACTOR = 0.5; // Add randomness to noise (0-1)
const BASE_DETAIL_LEVEL = 4; // Base detail level for far zoom
class MapGen {
constructor(mapSize, chunkSize) {
this.mapSize = mapSize;
this.chunkSize = chunkSize;
this.offsetX = 0;
this.offsetY = 0;
this.zoom = 1;
this.chunks = new Map();
this.initZoomAndCenter();
noiseSeed(99);
}
initZoomAndCenter() {
let minDim = min(width, height);
this.zoom = minDim / (this.chunkSize * 3);
this.offsetX = -(this.chunkSize * 1.5);
this.offsetY = -(this.chunkSize * 1.5);
}
drawGrid() {
noFill();
stroke(0, 100);
let visibleChunksX = ceil(width / (this.chunkSize * this.zoom));
let visibleChunksY = ceil(height / (this.chunkSize * this.zoom));
let tileWidth = this.chunkSize * this.zoom;
let tileHeight = this.chunkSize * this.zoom;
for (let cx = -1; cx <= visibleChunksX; cx++) {
for (let cy = -1; cy <= visibleChunksY; cy++) {
let chunkX = floor(this.offsetX / this.chunkSize) + cx;
let chunkY = floor(this.offsetY / this.chunkSize) + cy;
rect((chunkX * this.chunkSize - this.offsetX) * this.zoom, (chunkY * this.chunkSize - this.offsetY) * this.zoom, tileWidth, tileHeight);
}
}
noStroke();
}
async generateChunk(chunkX, chunkY, detailLevel) {
let chunkKey = `${chunkX},${chunkY}`;
if (this.chunks.has(chunkKey)) {
return this.chunks.get(chunkKey);
}
let chunkData = [];
for (let x = 0; x < detailLevel; x++) {
let row = [];
for (let y = 0; y < detailLevel; y++) {
// Heightmap: Using Perlin noise to generate height data with added randomness
let nx = (chunkX * this.chunkSize + x * (this.chunkSize / detailLevel)) / this.mapSize;
let ny = (chunkY * this.chunkSize + y * (this.chunkSize / detailLevel)) / this.mapSize;
let height = noise(nx * 3 + random(-RANDOMNESS_FACTOR, RANDOMNESS_FACTOR), ny * 3 + random(-RANDOMNESS_FACTOR, RANDOMNESS_FACTOR));
// Temperature: Adding axial tilt variation and latitude dependency
let latitudeFactor = Math.abs((chunkY * this.chunkSize + y * (this.chunkSize / detailLevel)) / this.mapSize - 0.5);
let tiltFactor = sin(radians((chunkY * this.chunkSize + y * (this.chunkSize / detailLevel)) - this.mapSize / 2));
let temperature = 0.5 * (1 - latitudeFactor) + 0.5 * tiltFactor;
temperature = constrain(temperature, 0, 1);
row.push({ height, temperature });
}
chunkData.push(row);
}
this.chunks.set(chunkKey, chunkData);
return chunkData;
}
async drawMap() {
noStroke();
let visibleChunksX = ceil(width / (this.chunkSize * this.zoom));
let visibleChunksY = ceil(height / (this.chunkSize * this.zoom));
let detailLevel = this.zoom < 1 ? BASE_DETAIL_LEVEL : DETAIL_LEVEL;
let partWidth = this.chunkSize * this.zoom / detailLevel;
let partHeight = this.chunkSize * this.zoom / detailLevel;
for (let cx = -1; cx <= visibleChunksX; cx++) {
for (let cy = -1; cy <= visibleChunksY; cy++) {
let chunkX = floor(this.offsetX / this.chunkSize) + cx;
let chunkY = floor(this.offsetY / this.chunkSize) + cy;
let chunkKey = `${chunkX},${chunkY}`;
if (!this.chunks.has(chunkKey)) {
this.generateChunk(chunkX, chunkY, detailLevel).then(() => {
// Redraw once the chunk is ready
this.drawMap();
});
// Draw placeholder for loading chunks
fill(200);
rect((chunkX * this.chunkSize - this.offsetX) * this.zoom, (chunkY * this.chunkSize - this.offsetY) * this.zoom, this.chunkSize * this.zoom, this.chunkSize * this.zoom);
continue;
}
let chunkData = this.chunks.get(chunkKey);
for (let x = 0; x < detailLevel; x++) {
for (let y = 0; y < detailLevel; y++) {
let data = chunkData[x][y];
let color = this.getBiomeColor(data.height, data.temperature);
fill(color);
rect((chunkX * this.chunkSize + x * (this.chunkSize / detailLevel) - this.offsetX) * this.zoom,
(chunkY * this.chunkSize + y * (this.chunkSize / detailLevel) - this.offsetY) * this.zoom,
partWidth, partHeight);
}
}
}
}
}
getBiomeColor(height, temperature) {
// Find appropriate biome based on height and temperature
for (let i = 0; i < biomes.length; i++) {
if (height >= biomes[i].minHeight && height < biomes[i].maxHeight) {
return biomes[i].color;
}
}
return color(255); // Default white for undefined areas
}
}
let mapGen;
function preload() {
// Load a default set of biomes or accept user-defined JSON
let defaultBiomes = `[
{"name": "Deep Sea", "color": "#001f4d", "minHeight": 0.0, "maxHeight": 0.1},
{"name": "Sea", "color": "#003366", "minHeight": 0.1, "maxHeight": 0.3},
{"name": "Beach", "color": "#f4d742", "minHeight": 0.3, "maxHeight": 0.35},
{"name": "Plains", "color": "#228B22", "minHeight": 0.35, "maxHeight": 0.4},
{"name": "Hills", "color": "#808000", "minHeight": 0.4, "maxHeight": 0.6},
{"name": "Mountains", "color": "#8B4513", "minHeight": 0.6, "maxHeight": 0.8},
{"name": "Rocky Mountains", "color": "#A9A9A9", "minHeight": 0.8, "maxHeight": 0.82},
{"name": "Freezing Peaks", "color": "#FFFFFF", "minHeight": 0.82, "maxHeight": 1.0}
]`;
biomes = JSON.parse(defaultBiomes);
}
function setup() {
createCanvas(800, 800);
mapGen = new MapGen(200, 20);
frameRate(30); // Limit frame rate for performance
}
function draw() {
background(255); // Clear the background each frame
handleInput();
mapGen.drawMap();
if (DEBUG_GRID) mapGen.drawGrid();
}
function mousePressed() {
noiseSeed(floor(random(10000)));
mapGen.chunks.clear();
mapGen.drawMap();
}
function handleInput() {
let moved = false;
let moveAmount = mapGen.chunkSize / mapGen.zoom * 0.5;
if (keyIsDown(87)) { // W key
mapGen.offsetY -= moveAmount;
moved = true;
}
if (keyIsDown(83)) { // S key
mapGen.offsetY += moveAmount;
moved = true;
}
if (keyIsDown(65)) { // A key
mapGen.offsetX -= moveAmount;
moved = true;
}
if (keyIsDown(68)) { // D key
mapGen.offsetX += moveAmount;
moved = true;
}
if (moved) {
mapGen.drawMap();
if (DEBUG_GRID) mapGen.drawGrid();
}
}
function mouseWheel(event) {
mapGen.zoom = constrain(mapGen.zoom - event.delta * 0.001, 0.5, 5);
mapGen.drawMap();
if (DEBUG_GRID) mapGen.drawGrid();
}