Week 2
I’ve been working on biome gen but the tutorial series I was using was never intended for biomes. I have managed to make some progress, but I don’t really know what I’m doing lol so any help would be appreciated here’s what I’ve got so far.
As you can see the biomes just repeat over and over again and I can’t figure out how to get it too have an off set based on which chunk it’s in here is the code for the biome gen
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VoronoiDigram : MonoBehaviour
{
[SerializeField] BiomeDate[] possibleBiomes;
[SerializeField] Color[] possibleColors;
[SerializeField] Material biomeMat;
[SerializeField] int mapSize;
[SerializeField] int gridSize;
int pixelsPerCell;
Vector2Int[,] pointPositions;
Color[,] colors;
private void Start()
{
BiomeColors();
GenerateBiomes();
GenerateBiomes();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.B))
{
GenerateBiomes();
}
}
void BiomeColors()
{
for(int i = 0; i < possibleBiomes.Length; i++)
{
possibleColors[i] = possibleBiomes[i].color;
}
}
private void GenerateBiomes()
{
GeneratePoints();
Texture2D texture = new Texture2D(mapSize, mapSize);
pixelsPerCell = mapSize / gridSize;
for(int i = 0; i < mapSize; i++)
{
for(int j = 0; j < mapSize; j++)
{
int gridX = i / pixelsPerCell;
int gridY = j / pixelsPerCell;
float nearestDistance = Mathf.Infinity;
Vector2Int nearestPoint = new Vector2Int();
for (int a = -1; a < 2; a++)
{
for(int b = -1; b < 2; b++)
{
int x = gridX + a;
int y = gridY + b;
if (x < 0 || y < 0 || x >= gridSize || y >= gridSize) continue;
float distance = Vector2Int.Distance(new Vector2Int(i, j), pointPositions[x, y]);
if (distance < nearestDistance)
{
nearestDistance = distance;
nearestPoint = new Vector2Int(x, y);
}
}
}
texture.SetPixel(i, j, colors[nearestPoint.x, nearestPoint.y]);
}
}
texture.Apply();
biomeMat.mainTexture = texture;
}
private void GeneratePoints()
{
pointPositions = new Vector2Int[gridSize, gridSize];
colors = new Color[gridSize, gridSize];
for(int i = 0; i < gridSize; i++)
{
for(int j = 0; j < gridSize; j++)
{
pointPositions[i, j] = new Vector2Int(i * pixelsPerCell + Random.Range(0, pixelsPerCell), j * pixelsPerCell + Random.Range(0, pixelsPerCell));
colors[i, j] = possibleColors[Random.Range(0, possibleColors.Length)];
}
}
}
}
and here is the endless terrain script
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditor.AssetImporters;
using UnityEditor.Timeline;
using UnityEngine;
using UnityEngine.UIElements;
public class EndlessTerrainGen : MonoBehaviour
{
Dictionary<Vector2, TerrainChunk> terrainChunkDictionary = new Dictionary<Vector2, TerrainChunk>();
static List<TerrainChunk> terrainChunksVisibleLstFrame = new List<TerrainChunk>();
const float movementThresholdForChunkUpdate = 25f;
const float squrMovementThresholdForChunkUpdate = movementThresholdForChunkUpdate * movementThresholdForChunkUpdate;
Vector2 viewerPosOld;
public LODInfo[] detailLevels;
public static float maxViewDistance = 500f;
static TerrainGeneration terrainGen;
public static Vector2 viewerPosition;
[SerializeField] Material mapMaterial;
[SerializeField] Transform viewer;
[SerializeField] int chunkSize;
[SerializeField] int chunksVisibleInViewDist;
private void Start()
{
terrainGen = FindObjectOfType<TerrainGeneration>();
maxViewDistance = detailLevels[detailLevels.Length - 1].visibleDistThreshold;
chunkSize = terrainGen.mapChunkSize - 1;
chunksVisibleInViewDist = Mathf.RoundToInt( maxViewDistance / chunkSize);
UpdateVisibleChunks();
}
private void Update()
{
viewerPosition = new Vector2 (viewer.position.x, viewer.position.z) / terrainGen.terrainDate.uniformScale;
if((viewerPosOld - viewerPosition).sqrMagnitude > squrMovementThresholdForChunkUpdate)
{
viewerPosOld = viewerPosition;
UpdateVisibleChunks();
}
}
void UpdateVisibleChunks()
{
int currentChunkCoordX = Mathf.RoundToInt(viewerPosition.x / chunkSize);
int currentChunkCoordZ = Mathf.RoundToInt(viewerPosition.y / chunkSize);
for(int i = 0; i < terrainChunksVisibleLstFrame.Count; i++)
{
terrainChunksVisibleLstFrame[i].SetVisible(false);
}
terrainChunksVisibleLstFrame.Clear();
for (int zOffSet = - chunksVisibleInViewDist; zOffSet <= chunksVisibleInViewDist; zOffSet++)
{
for (int xOffSet = -chunksVisibleInViewDist; xOffSet <= chunksVisibleInViewDist; xOffSet++)
{
Vector2 viewedChunkCoord = new Vector2(currentChunkCoordX + xOffSet, currentChunkCoordZ + zOffSet);
if(terrainChunkDictionary.ContainsKey(viewedChunkCoord))
{
terrainChunkDictionary[viewedChunkCoord].UpdateTerrainChunk();
}
else
{
terrainChunkDictionary.Add(viewedChunkCoord, new TerrainChunk(viewedChunkCoord, chunkSize, detailLevels, transform, mapMaterial));
}
}
}
}
public class TerrainChunk
{
GameObject meshObject;
Vector2 position;
Bounds bounds;
MeshRenderer meshRenderer;
MeshFilter meshFilter;
MeshCollider meshCollider;
LODInfo[] detailLevel;
LODMesh[] lODMeshes;
LODMesh collisionLODMesh;
MapDate mapDate;
bool mapDateReceived;
int previousLODIndex = -1;
public TerrainChunk(Vector2 coord, int size, LODInfo[] detailLevel, Transform parent, Material material)
{
this.detailLevel = detailLevel;
position = coord * size;
bounds = new Bounds(position, Vector2.one * size);
Vector3 positionV3 = new Vector3(position.x, 0, position.y);
meshObject = new GameObject("Terrain Chunk");
meshRenderer = meshObject.AddComponent<MeshRenderer>();
meshFilter = meshObject.AddComponent<MeshFilter>();
meshCollider = meshObject.AddComponent<MeshCollider>();
meshObject.transform.position = positionV3 * terrainGen.terrainDate.uniformScale;
meshObject.transform.localScale = Vector3.one * terrainGen.terrainDate.uniformScale;
meshObject.transform.parent = parent;
meshRenderer.material = material;
lODMeshes = new LODMesh[detailLevel.Length];
for(int i = 0; i < detailLevel.Length; i++)
{
lODMeshes[i] = new LODMesh(detailLevel[i].lOD, UpdateTerrainChunk);
if (detailLevel[i].useforColider)
{
collisionLODMesh = lODMeshes[i];
}
}
SetVisible(false);
terrainGen.RequestMapDate(position,OnMapDateReceived);
}
void OnMapDateReceived(MapDate mapDate)
{
this.mapDate = mapDate;
mapDateReceived = true;
UpdateTerrainChunk();
}
public void UpdateTerrainChunk()
{
if (mapDateReceived)
{
float viewerDistFromNestEdge = Mathf.Sqrt( bounds.SqrDistance(viewerPosition));
bool visible = viewerDistFromNestEdge <= maxViewDistance;
SetVisible(visible);
if (visible)
{
int lODIndex = 0;
for(int i = 0; i < detailLevel.Length - 1; i++)
{
if(viewerDistFromNestEdge > detailLevel[i].visibleDistThreshold)
{
lODIndex = i + 1;
}
else
{
break;
}
}
if(lODIndex != previousLODIndex)
{
LODMesh lODMesh = lODMeshes[lODIndex];
if (lODMesh.hasMesh)
{
previousLODIndex = lODIndex;
meshFilter.mesh = lODMesh.mesh;
}
else if (!lODMesh.hasRequestedMesh)
{
lODMesh.RequestMesh(mapDate);
}
}
if(lODIndex == 0)
{
if (collisionLODMesh.hasMesh)
{
meshCollider.sharedMesh = collisionLODMesh.mesh;
}
else if (!collisionLODMesh.hasRequestedMesh)
{
collisionLODMesh.RequestMesh(mapDate);
}
}
terrainChunksVisibleLstFrame.Add(this);
}
}
}
public void SetVisible(bool visible)
{
meshObject.SetActive (visible);
}
public bool IsVisible()
{
return meshObject.activeSelf;
}
}
class LODMesh
{
public Mesh mesh;
public bool hasRequestedMesh;
public bool hasMesh;
int lOD;
System.Action updateCallBack;
public LODMesh(int lOD, System.Action updateCallBack)
{
this.lOD = lOD;
this.updateCallBack = updateCallBack;
}
void OnMeshDateReceived(MeshDate meshDate)
{
mesh = meshDate.CreateMesh();
hasMesh = true;
updateCallBack();
}
public void RequestMesh(MapDate mapDate)
{
hasRequestedMesh = true;
terrainGen.RequestMeshDate(mapDate, lOD, OnMeshDateReceived);
}
}
[System.Serializable]
public struct LODInfo
{
public int lOD;
public float visibleDistThreshold;
public bool useforColider;
}
}
so, I decided to take a break from biome gen. and started on trees.
Thay drop branches and seeds over time and if you walk up to them, you can shack them to make them fall faster right now all the models are place holders. Next I’ll be working on inventory tell me what you think.