지하 최적화
블록 프리팹 단위 -> 블록 청크 단위 스폰 기타 건설, 조준 관련 사이드이펙트 버그 수정
This commit is contained in:
193
Assets/Scripts/Underground/BlockData.cs
Normal file
193
Assets/Scripts/Underground/BlockData.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using Unity.Netcode;
|
||||
|
||||
/// <summary>
|
||||
/// Compact block state struct for chunk-based storage.
|
||||
/// 3 bytes per block: type (1), health (1), flags (1)
|
||||
/// </summary>
|
||||
public struct BlockData : INetworkSerializable
|
||||
{
|
||||
/// <summary>
|
||||
/// Block type: 0=empty/air, 1=normal stone, 2=resource ore
|
||||
/// </summary>
|
||||
public byte blockType;
|
||||
|
||||
/// <summary>
|
||||
/// Block health: 0-255 (0 = destroyed)
|
||||
/// </summary>
|
||||
public byte health;
|
||||
|
||||
/// <summary>
|
||||
/// Block flags: bit 0 = isDiscovered (fog of war)
|
||||
/// </summary>
|
||||
public byte flags;
|
||||
|
||||
// Block type constants
|
||||
public const byte TYPE_EMPTY = 0;
|
||||
public const byte TYPE_NORMAL = 1;
|
||||
public const byte TYPE_RESOURCE = 2;
|
||||
|
||||
// Flag bit masks
|
||||
public const byte FLAG_DISCOVERED = 1 << 0; // Has been seen at least once (networked/permanent)
|
||||
|
||||
/// <summary>
|
||||
/// Whether this block is empty (air or destroyed)
|
||||
/// </summary>
|
||||
public bool IsEmpty => blockType == TYPE_EMPTY || health == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this block has been discovered by players
|
||||
/// </summary>
|
||||
public bool IsDiscovered
|
||||
{
|
||||
get => (flags & FLAG_DISCOVERED) != 0;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
flags |= FLAG_DISCOVERED;
|
||||
else
|
||||
flags &= unchecked((byte)~FLAG_DISCOVERED);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this block is a resource block
|
||||
/// </summary>
|
||||
public bool IsResource => blockType == TYPE_RESOURCE;
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty block
|
||||
/// </summary>
|
||||
public static BlockData Empty => new BlockData
|
||||
{
|
||||
blockType = TYPE_EMPTY,
|
||||
health = 0,
|
||||
flags = 0
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Create a normal stone block with full health
|
||||
/// </summary>
|
||||
public static BlockData Normal(byte maxHealth = 100) => new BlockData
|
||||
{
|
||||
blockType = TYPE_NORMAL,
|
||||
health = maxHealth,
|
||||
flags = 0
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Create a resource ore block with full health
|
||||
/// </summary>
|
||||
public static BlockData Resource(byte maxHealth = 150) => new BlockData
|
||||
{
|
||||
blockType = TYPE_RESOURCE,
|
||||
health = maxHealth,
|
||||
flags = 0
|
||||
};
|
||||
|
||||
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
||||
{
|
||||
serializer.SerializeValue(ref blockType);
|
||||
serializer.SerializeValue(ref health);
|
||||
serializer.SerializeValue(ref flags);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Network-serializable container for entire chunk state.
|
||||
/// Used for initial sync when clients connect.
|
||||
/// </summary>
|
||||
public struct ChunkState : INetworkSerializable
|
||||
{
|
||||
public BlockData[] blocks;
|
||||
|
||||
public const int CHUNK_SIZE = 4;
|
||||
public const int BLOCKS_PER_CHUNK = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE; // 64
|
||||
|
||||
public ChunkState(int size)
|
||||
{
|
||||
blocks = new BlockData[size];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure blocks array is initialized
|
||||
/// </summary>
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
if (blocks == null)
|
||||
{
|
||||
blocks = new BlockData[BLOCKS_PER_CHUNK];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get block at local coordinates within the chunk
|
||||
/// </summary>
|
||||
public BlockData GetBlock(int x, int y, int z)
|
||||
{
|
||||
if (blocks == null) return BlockData.Empty;
|
||||
int index = x + y * CHUNK_SIZE + z * CHUNK_SIZE * CHUNK_SIZE;
|
||||
if (index < 0 || index >= blocks.Length) return BlockData.Empty;
|
||||
return blocks[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set block at local coordinates within the chunk
|
||||
/// </summary>
|
||||
public void SetBlock(int x, int y, int z, BlockData block)
|
||||
{
|
||||
EnsureInitialized();
|
||||
int index = x + y * CHUNK_SIZE + z * CHUNK_SIZE * CHUNK_SIZE;
|
||||
if (index >= 0 && index < blocks.Length)
|
||||
{
|
||||
blocks[index] = block;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert local index to local 3D coordinates
|
||||
/// </summary>
|
||||
public static (int x, int y, int z) IndexToLocal(int index)
|
||||
{
|
||||
int x = index % CHUNK_SIZE;
|
||||
int y = (index / CHUNK_SIZE) % CHUNK_SIZE;
|
||||
int z = index / (CHUNK_SIZE * CHUNK_SIZE);
|
||||
return (x, y, z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert local 3D coordinates to index
|
||||
/// </summary>
|
||||
public static int LocalToIndex(int x, int y, int z)
|
||||
{
|
||||
return x + y * CHUNK_SIZE + z * CHUNK_SIZE * CHUNK_SIZE;
|
||||
}
|
||||
|
||||
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
||||
{
|
||||
// Serialize array length first
|
||||
int length = BLOCKS_PER_CHUNK;
|
||||
serializer.SerializeValue(ref length);
|
||||
|
||||
if (serializer.IsReader)
|
||||
{
|
||||
blocks = new BlockData[length];
|
||||
}
|
||||
else if (blocks == null)
|
||||
{
|
||||
blocks = new BlockData[length];
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
blocks[i].NetworkSerialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a default initialized ChunkState
|
||||
/// </summary>
|
||||
public static ChunkState CreateEmpty()
|
||||
{
|
||||
return new ChunkState(BLOCKS_PER_CHUNK);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user