- Home /
Question by
evan_unity · Dec 14, 2020 at 12:17 PM ·
2dlightingtilemap
How can I make my lighting travel to other chunks?
I have a custom lighting system using tilemaps, and the lighting is handled in the chunk because I found that that's the easiest and has the best performance. My lighting currently looks like this:
The lighting looks good, but there is one issue: light in chunks don't affect neighboring chunks. What is the best way to achieve this?
My Chunk script (where the lighting happens): using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Tilemaps;
public class Chunk : MonoBehaviour
{
[Header("Surface World Gen Values")]
public int seed;
public float surfaceFrequency;
public float surfaceAmplitude;
public int surfaceOctives;
public float surfacePersistence;
public float surfaceLacunarity;
[Header("Cave World Gen Values")]
[SerializeField] List<NoiseLayer> caveNoiseLayers;
[Header("World Stats")]
public int chunkSize = 4;
public int baseGroundHeight = 50;
public int minGroundHeight = 30;
public int maxGroundHeight = 80;
public int offsetX;
public int offsetY;
[Header("World")]
public TileType[,] chunkMap;
[Header("World Display")]
[SerializeField] Tilemap chunkTilemap;
[SerializeField] Tilemap lightingTilemap;
[Header("Items")]
[SerializeField] ItemObject itemObjectPrefab;
[SerializeField] float[,] lighting;
[SerializeField] Tile lightTile;
DB_Tiles dbTiles;
DB_Items dbItems;
World world;
PlayerController player;
void Awake()
{
dbTiles = FindObjectOfType<DB_Tiles>();
dbItems = FindObjectOfType<DB_Items>();
world = FindObjectOfType<World>();
player = FindObjectOfType<PlayerController>();
}
void Update()
{
if (Vector3.Distance(transform.position, player.transform.position) > player.chunkDistanceX * (chunkSize / 2) + (chunkSize / 2))
{
world.RemoveChunk(this);
Destroy(gameObject);
}
CalculateLighting();
}
public void InitializeChunk(int _seed, float _surfFreq, float _surfAmpl, int _surfOct, float _surfPers, float _surgLac, List<NoiseLayer> _caveNoiseLayers, int _chunkSize, int _baseGroundHeight,
int _minGroundHeight, int _maxGroundHeight, int _offsetX, int _offsetY)
{
seed = _seed;
surfaceFrequency = _surfFreq;
surfaceAmplitude = _surfAmpl;
surfaceOctives = _surfOct;
surfacePersistence = _surfPers;
surfaceLacunarity = _surgLac;
caveNoiseLayers = _caveNoiseLayers;
chunkSize = _chunkSize;
baseGroundHeight = _baseGroundHeight;
minGroundHeight = _minGroundHeight;
maxGroundHeight = _maxGroundHeight;
offsetX = _offsetX;
offsetY = _offsetY;
CreateVirtualWorld();
}
void CreateVirtualWorld()
{
chunkMap = new TileType[chunkSize, chunkSize];
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
Vector3Int chunkPos = world.WorldToTilePos(transform.position);
Vector2 currentPos = new Vector2(x + chunkPos.x - offsetX, y + chunkPos.y - offsetY);
float perlinY = Mathf.FloorToInt(Noise.Noise1D(currentPos.x + seed, surfaceFrequency, surfaceAmplitude, surfaceOctives, surfacePersistence, surfaceLacunarity) + baseGroundHeight);
// Basic Terrain
if (perlinY > currentPos.y + 10)
chunkMap[x, y] = dbTiles.GetTileByName("Stone");
else if (perlinY > currentPos.y)
chunkMap[x, y] = dbTiles.GetTileByName("Dirt");
else if (perlinY == currentPos.y)
chunkMap[x, y] = dbTiles.GetTileByName("Grass");
else
chunkMap[x, y] = dbTiles.GetTileByName("Air");
// Caves
for (int i = caveNoiseLayers.Count - 1; i >= 0; i--)
{
if (caveNoiseLayers[i].enabled)
{
float freq = caveNoiseLayers[i].frequency;
float ampl = caveNoiseLayers[i].amplitude;
int oct = caveNoiseLayers[i].octives;
float pers = caveNoiseLayers[i].persistence;
float lac = caveNoiseLayers[i].lacunarity;
float layerX = currentPos.x + seed + caveNoiseLayers[i].offsetX;
float layerY = currentPos.y + seed + caveNoiseLayers[i].offsetY;
float thres = caveNoiseLayers[i].threshold;
if (perlinY > currentPos.y + caveNoiseLayers[i].minDistBelowSurface && perlinY < currentPos.y + caveNoiseLayers[i].maxDistBelowSurface)
{
if (Noise.Noise2D(layerX, layerY, freq, ampl, oct, pers, lac) < thres)
{
chunkMap[x, y] = dbTiles.GetTileByName(caveNoiseLayers[i].tileName);
}
}
}
}
}
}
CreatePhysicalWorld();
}
void CreatePhysicalWorld()
{
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
//Vector3Int chunkPos = world.WorldToTileUnit(transform.position + new Vector3(-chunkSize, -chunkSize));
chunkTilemap.transform.position = transform.position;
Vector3Int tilePos = new Vector3Int(Mathf.FloorToInt(x), Mathf.FloorToInt(y), 0);
chunkTilemap.SetTile(tilePos, chunkMap[x, y].tile);
}
}
CalculateLighting();
FindObjectOfType<PlayerController>().InitializePlayer();
}
void CalculateLighting()
{
lighting = new float[chunkSize, chunkSize];
// Get lights in chunk
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
if (chunkMap[x, y].emissive)
{
if (lighting[x, y] < chunkMap[x, y].lightStrength)
lighting[x, y] = chunkMap[x, y].lightStrength;
List<int> lightX = new List<int>();
List<int> lightY = new List<int>();
lightX.Add(x);
lightY.Add(y);
while (lightX.Count > 0)
{
float thisLighting = lighting[lightX[0], lightY[0]] * 0.5f;
if (lightX[0] + 1 < chunkSize && lighting[lightX[0] + 1, lightY[0]] < thisLighting)
{
lighting[lightX[0] + 1, lightY[0]] = thisLighting;
if (lighting[lightX[0] + 1, lightY[0]] > 0.05f)
{
lightX.Add(lightX[0] + 1);
lightY.Add(lightY[0]);
}
}
if (lightX[0] - 1 >= 0 && lighting[lightX[0] - 1, lightY[0]] < thisLighting)
{
lighting[lightX[0] - 1, lightY[0]] = thisLighting;
if (lighting[lightX[0] - 1, lightY[0]] > 0.05f)
{
lightX.Add(lightX[0] - 1);
lightY.Add(lightY[0]);
}
}
if (lightY[0] + 1 < chunkSize && lighting[lightX[0], lightY[0] + 1] < thisLighting)
{
lighting[lightX[0], lightY[0] + 1] = thisLighting;
if (lighting[lightX[0], lightY[0] + 1] > 0.05f)
{
lightX.Add(lightX[0]);
lightY.Add(lightY[0] + 1);
}
}
if (lightY[0] - 1 >= 0 && lighting[lightX[0], lightY[0] - 1] < thisLighting)
{
lighting[lightX[0], lightY[0] - 1] = thisLighting;
if (lighting[lightX[0], lightY[0] - 1] > 0.05f)
{
lightX.Add(lightX[0]);
lightY.Add(lightY[0] - 1);
}
}
lightX.RemoveAt(0);
lightY.RemoveAt(0);
}
}
}
}
VisualizeLighting();
}
void VisualizeLighting()
{
// Visualize the lighting
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
Vector3Int tilePos = new Vector3Int(x, y, 0);
lightingTilemap.SetTile(tilePos, lightTile);
lightingTilemap.SetTileFlags(tilePos, TileFlags.None);
lightingTilemap.SetColor(tilePos, new Color(0, 0, 0, 1f - Mathf.Min(lighting[x, y], 1)));
}
}
}
public void TryDestroyTile(Vector2 destroyPos)
{
Vector3Int tilePos = chunkTilemap.WorldToCell(destroyPos);
if (chunkMap[tilePos.x, tilePos.y].name != "Air")
{
TileType tileDestroyed = chunkMap[tilePos.x, tilePos.y];
ItemObject newItem = Instantiate(itemObjectPrefab);
newItem.transform.position = chunkTilemap.CellToWorld(new Vector3Int(tilePos.x, tilePos.y, tilePos.z)) + new Vector3(0.25f, 0.25f);
if (tileDestroyed.name != "Grass")
newItem.StartItem(dbItems.GetItemByName(tileDestroyed.name));
else
newItem.StartItem(dbItems.GetItemByName("Dirt"));
chunkMap[tilePos.x, tilePos.y] = dbTiles.GetTileByName("Air");
chunkTilemap.SetTile(tilePos, null);
}
}
public void PlaceTile(Vector2 placePos, string tileName)
{
Vector3Int tilePos = chunkTilemap.WorldToCell(placePos);
chunkMap[tilePos.x, tilePos.y] = dbTiles.GetTileByName(tileName);
chunkTilemap.SetTile(tilePos, dbTiles.GetTileByName(tileName).tile);
}
public bool IsTouchingPlayer(Vector2 checkPos)
{
Vector3Int tilePos = chunkTilemap.WorldToCell(checkPos);
Collider2D touching = Physics2D.OverlapBox(new Vector2((float)tilePos.x / 2f + 0.25f, (float)tilePos.y / 2f + 0.25f), new Vector2(0.49f, 0.49f), 0, LayerMask.GetMask("Player"));
return touching;
}
public bool TileExistsAtPoint(Vector2 checkPos)
{
Vector3Int tilePos = chunkTilemap.WorldToCell(checkPos);
return chunkTilemap.GetTile(tilePos) != null;
}
public bool HasTileAround(Vector2 checkPos)
{
Vector3Int tilePos = chunkTilemap.WorldToCell(checkPos);
if (chunkTilemap.GetTile(new Vector3Int(tilePos.x + 1, tilePos.y, tilePos.z)) != null)
{
return true;
}
else if (chunkTilemap.GetTile(new Vector3Int(tilePos.x - 1, tilePos.y, tilePos.z)) != null)
{
return true;
}
else if (chunkTilemap.GetTile(new Vector3Int(tilePos.x, tilePos.y + 1, tilePos.z)) != null)
{
return true;
}
else if (chunkTilemap.GetTile(new Vector3Int(tilePos.x, tilePos.y - 1, tilePos.z)) != null)
{
return true;
}
return false;
}
}
My world script (where the chunks are made and stored: using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Tilemaps;
public class World : MonoBehaviour
{
[Header("Surface World Gen Values")]
public bool randomizeSeed;
public int seed;
public float surfaceFrequency;
public float surfaceAmplitude;
public int surfaceOctives;
public float surfacePersistence;
public float surfaceLacunarity;
[Header("Cave World Gen Values")]
[SerializeField] List<NoiseLayer> caveNoiseLayers;
[Header("World Stats")]
public int chunkSize = 4;
public int worldSizeX = 100;
public int worldSizeY = 100;
public int baseGroundHeight = 50;
public int minGroundHeight = 30;
public int maxGroundHeight = 80;
public bool centerX;
public int offsetX;
public bool autoY;
public int offsetY;
[Header("World")]
public TileType[,] worldMap;
[SerializeField] Chunk chunkPrefab;
[Header("World Display")]
[SerializeField] Tilemap worldTilemap;
[Header("Items")]
[SerializeField] ItemObject itemObjectPrefab;
[Header("Chunks")]
List<Chunk> chunks = new List<Chunk>();
[SerializeField] List<Vector3Int> chunksToGenerate = new List<Vector3Int>();
DB_Tiles dbTiles;
DB_Items dbItems;
public void InitializeWorld()
{
dbTiles = FindObjectOfType<DB_Tiles>();
dbItems = FindObjectOfType<DB_Items>();
if (centerX)
offsetX = -(worldSizeX * chunkSize) / 2;
if (autoY)
offsetY = -worldSizeY * chunkSize;
}
public void SetGenerateChunk(Vector3Int chunkPos)
{
if (GetChunkAtPos(new Vector2(chunkPos.x, chunkPos.y)) == null && !chunksToGenerate.Contains(chunkPos))
chunksToGenerate.Add(chunkPos);
}
public void GenerateChunk(Vector3Int chunkPos)
{
if (GetChunkAtPos(new Vector2(chunkPos.x, chunkPos.y)) == null)
{
Chunk newChunk = Instantiate(chunkPrefab, chunkPos, Quaternion.identity, transform);
newChunk.InitializeChunk(seed, surfaceFrequency, surfaceAmplitude, surfaceOctives, surfacePersistence, surfaceLacunarity, caveNoiseLayers,
chunkSize, baseGroundHeight, minGroundHeight, maxGroundHeight, offsetX, offsetY);
chunks.Add(newChunk);
}
}
public Vector2 TileToWorldPos(Vector3Int tilePos)
{
Vector2 worldPos = worldTilemap.CellToWorld(tilePos);
worldPos += new Vector2(0.25f, 0.25f);
return worldPos;
}
public Vector3Int WorldToTilePos(Vector2 worldPos)
{
Vector3Int tilePos = worldTilemap.WorldToCell(worldPos);
return tilePos;
}
public Vector3Int WorldToTileUnit(Vector2 worldPos)
{
worldPos /= 2;
Vector3Int tileUnitPos = new Vector3Int(Mathf.RoundToInt(worldPos.x), Mathf.RoundToInt(worldPos.y), 0);
return tileUnitPos;
}
public Chunk GetChunkAtPos(Vector2 pos)
{
pos /= chunkSize / 2;
Vector3 convertedPos = new Vector3(Mathf.Floor(pos.x), Mathf.Floor(pos.y));
convertedPos *= chunkSize / 2;
for (int i = 0; i < chunks.Count; i++)
{
if (chunks[i].transform.position == convertedPos)
{
return chunks[i];
}
}
return null;
}
public Vector2 GetPosAsChunk(Vector2 pos)
{
pos /= chunkSize / 2;
Vector3 convertedPos = new Vector3(Mathf.Floor(pos.x), Mathf.Floor(pos.y));
convertedPos *= 5;
return convertedPos;
}
void Update()
{
if (chunksToGenerate.Count > 0)
{
GenerateChunk(chunksToGenerate[0]);
chunksToGenerate.RemoveAt(0);
}
}
public void RemoveChunk(Chunk chunkToRemove)
{
chunks.Remove(chunkToRemove);
}
}
lighting.png
(9.1 kB)
Comment