233 lines
6.3 KiB
C#
233 lines
6.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace InfiniteWorld
|
|
{
|
|
[CreateAssetMenu(menuName = "Infinite World/Chunk Template", fileName = "ChunkTemplate")]
|
|
public class ChunkTemplate : ScriptableObject
|
|
{
|
|
[Min(4)] public int width = 16;
|
|
[Min(4)] public int height = 16;
|
|
|
|
[Header("Exits")]
|
|
public bool exitTop = true;
|
|
public bool exitRight = true;
|
|
public bool exitBottom = true;
|
|
public bool exitLeft = true;
|
|
|
|
[SerializeField] private List<ChunkCellData> cells = new List<ChunkCellData>();
|
|
|
|
public int CellCount => width * height;
|
|
|
|
public void EnsureCellData()
|
|
{
|
|
int target = Mathf.Max(1, width * height);
|
|
while (cells.Count < target)
|
|
{
|
|
cells.Add(new ChunkCellData());
|
|
}
|
|
|
|
while (cells.Count > target)
|
|
{
|
|
cells.RemoveAt(cells.Count - 1);
|
|
}
|
|
}
|
|
|
|
public void Resize(int newWidth, int newHeight)
|
|
{
|
|
newWidth = Mathf.Max(4, newWidth);
|
|
newHeight = Mathf.Max(4, newHeight);
|
|
|
|
List<ChunkCellData> oldCells = new List<ChunkCellData>(cells);
|
|
int oldWidth = width;
|
|
int oldHeight = height;
|
|
|
|
width = newWidth;
|
|
height = newHeight;
|
|
cells = new List<ChunkCellData>(newWidth * newHeight);
|
|
|
|
for (int i = 0; i < newWidth * newHeight; i++)
|
|
{
|
|
cells.Add(new ChunkCellData());
|
|
}
|
|
|
|
for (int y = 0; y < Mathf.Min(oldHeight, newHeight); y++)
|
|
{
|
|
for (int x = 0; x < Mathf.Min(oldWidth, newWidth); x++)
|
|
{
|
|
int oldIndex = y * oldWidth + x;
|
|
if (oldIndex < oldCells.Count)
|
|
{
|
|
cells[Index(x, y)] = oldCells[oldIndex];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
EnsureCellData();
|
|
for (int i = 0; i < cells.Count; i++)
|
|
{
|
|
cells[i] = new ChunkCellData();
|
|
}
|
|
}
|
|
|
|
public bool GetWall(int x, int y)
|
|
{
|
|
return IsInBounds(x, y) && cells[Index(x, y)].wall;
|
|
}
|
|
|
|
public bool GetEnvironment(int x, int y)
|
|
{
|
|
return IsInBounds(x, y) && cells[Index(x, y)].environment;
|
|
}
|
|
|
|
public void SetWall(int x, int y, bool value)
|
|
{
|
|
if (!IsInBounds(x, y))
|
|
{
|
|
return;
|
|
}
|
|
|
|
EnsureCellData();
|
|
ChunkCellData data = cells[Index(x, y)];
|
|
data.wall = value;
|
|
if (value)
|
|
{
|
|
data.environment = false;
|
|
}
|
|
cells[Index(x, y)] = data;
|
|
}
|
|
|
|
public void SetEnvironment(int x, int y, bool value)
|
|
{
|
|
if (!IsInBounds(x, y))
|
|
{
|
|
return;
|
|
}
|
|
|
|
EnsureCellData();
|
|
ChunkCellData data = cells[Index(x, y)];
|
|
data.environment = value;
|
|
if (value)
|
|
{
|
|
data.wall = false;
|
|
}
|
|
cells[Index(x, y)] = data;
|
|
}
|
|
|
|
public bool GetExit(ChunkExit exit)
|
|
{
|
|
return exit switch
|
|
{
|
|
ChunkExit.Top => exitTop,
|
|
ChunkExit.Right => exitRight,
|
|
ChunkExit.Bottom => exitBottom,
|
|
ChunkExit.Left => exitLeft,
|
|
_ => false
|
|
};
|
|
}
|
|
|
|
public int ExitCount()
|
|
{
|
|
int count = 0;
|
|
count += exitTop ? 1 : 0;
|
|
count += exitRight ? 1 : 0;
|
|
count += exitBottom ? 1 : 0;
|
|
count += exitLeft ? 1 : 0;
|
|
return count;
|
|
}
|
|
|
|
public void ApplyBorderWallsFromExits(int openingWidth = 3)
|
|
{
|
|
EnsureCellData();
|
|
Clear();
|
|
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
SetWall(x, 0, true);
|
|
SetWall(x, height - 1, true);
|
|
}
|
|
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
SetWall(0, y, true);
|
|
SetWall(width - 1, y, true);
|
|
}
|
|
|
|
CarveExit(ChunkExit.Top, openingWidth);
|
|
CarveExit(ChunkExit.Right, openingWidth);
|
|
CarveExit(ChunkExit.Bottom, openingWidth);
|
|
CarveExit(ChunkExit.Left, openingWidth);
|
|
}
|
|
|
|
public void CarveExit(ChunkExit exit, int openingWidth = 3)
|
|
{
|
|
if (!GetExit(exit))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int half = Mathf.Max(1, openingWidth) / 2;
|
|
switch (exit)
|
|
{
|
|
case ChunkExit.Top:
|
|
for (int x = width / 2 - half; x <= width / 2 + half; x++)
|
|
{
|
|
SetWall(x, height - 1, false);
|
|
SetWall(x, height - 2, false);
|
|
}
|
|
break;
|
|
case ChunkExit.Right:
|
|
for (int y = height / 2 - half; y <= height / 2 + half; y++)
|
|
{
|
|
SetWall(width - 1, y, false);
|
|
SetWall(width - 2, y, false);
|
|
}
|
|
break;
|
|
case ChunkExit.Bottom:
|
|
for (int x = width / 2 - half; x <= width / 2 + half; x++)
|
|
{
|
|
SetWall(x, 0, false);
|
|
SetWall(x, 1, false);
|
|
}
|
|
break;
|
|
case ChunkExit.Left:
|
|
for (int y = height / 2 - half; y <= height / 2 + half; y++)
|
|
{
|
|
SetWall(0, y, false);
|
|
SetWall(1, y, false);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
private int Index(int x, int y)
|
|
{
|
|
return y * width + x;
|
|
}
|
|
|
|
private bool IsInBounds(int x, int y)
|
|
{
|
|
return x >= 0 && y >= 0 && x < width && y < height;
|
|
}
|
|
}
|
|
|
|
public enum ChunkExit
|
|
{
|
|
Top,
|
|
Right,
|
|
Bottom,
|
|
Left
|
|
}
|
|
|
|
[Serializable]
|
|
public struct ChunkCellData
|
|
{
|
|
public bool wall;
|
|
public bool environment;
|
|
}
|
|
}
|