-
Ryan Chang authoredRyan Chang authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
TerrainChunker.cs 3.55 KiB
using System.Diagnostics;
using UnityEngine;
/// <summary>
/// Divide the terrain generation into 9 "chunks".
/// Each chunk is responsible for the generation of its
/// terrain object and only its terrain object.
///
/// A chunk may be moved when the player moves, such that a 3x3
/// grid of chunks surrounds the player at any time, and that
/// there's a minimum of one full terrain between the player and
/// the void.
///
/// Once a chunk is moved, it will regenerate its terrain object.
/// This will be done by a thread/job.
/// </summary>
public abstract class TerrainChunker : MonoBehaviour
{
#region Variables
[Header("Autogenerated values")]
[Tooltip("Current grid position of the chunker")]
public Vector2Int gridPosition;
public Vector2 crawlOffset;
public TerrainMaster master;
public float TerrainSize => terrainWidth;
protected Terrain terrain;
protected TerrainData data;
protected int heightResolution = 513;
protected float terrainWidth = 1000;
protected float heightScale;
private bool initialGen = true;
private bool lookForCompletion = false;
private Stopwatch createTimer = new();
#endregion
public void Initialize(TerrainMaster master, Vector2Int gridPosition,
Vector2 crawlOffset)
{
this.master = master;
this.gridPosition = gridPosition;
this.crawlOffset = crawlOffset;
gameObject.name = $"Chunker {gridPosition}";
data = Instantiate(Resources.Load<TerrainData>("Template"));
heightScale = data.size.y / master.terrainHeight;
data.size = new(data.size.x, master.terrainHeight, data.size.z);
heightResolution = data.heightmapResolution;
terrainWidth = data.size.x;
UpdateChunk(gridPosition, true);
}
public void UpdateChunk(Vector2Int newGridPosition, bool forceUpdate = false)
{
if (newGridPosition != gridPosition || forceUpdate)
{
gridPosition = newGridPosition;
// Now we update the terrain
UpdateTerrain();
}
}
private void UpdateTerrain()
{
print($"Updating {gameObject.name} terrain.");
createTimer.Start();
CreateShapeGenerator();
lookForCompletion = true;
}
private void ApplyTerrain(float[,] heights)
{
createTimer.Stop();
print($"Apply {gameObject.name} terrain. " +
$"Took {createTimer.Elapsed} with mode {master.mode}.");
// Calculate position of game object
Vector2 flatPosition = (Vector2)gridPosition * terrainWidth;
Vector3 worldPosition = new(flatPosition.x, 0, flatPosition.y);
// Set heights of terrain data.
master.shapeApplier.ApplyHeights(transform, terrain, data,
worldPosition, heights);
}
private void Update()
{
if (lookForCompletion && CheckShapeDone() && !CheckShapeCancelled())
{
ApplyTerrain(GetHeights());
lookForCompletion = false;
if (initialGen)
{
initialGen = false;
master.ChunkInitGenerate();
}
}
}
#region Shape generation
protected abstract bool CheckShapeDone();
protected abstract bool CheckShapeCancelled();
protected abstract void CreateShapeGenerator();
protected abstract float[,] GetHeights();
#endregion
#region Texture generation
protected abstract float[,,] GetAlphamap();
#endregion
private void OnDestroy()
{
CleanUp();
}
protected abstract void CleanUp();
}