From 94ebc4ace4dd4d10c90616d85b50243971dbd448 Mon Sep 17 00:00:00 2001 From: Max Westerlund Date: Wed, 3 Sep 2025 00:37:42 +0200 Subject: [PATCH] added block breaking --- Blocks.cs | 4 ++- Chunk.cs | 16 ++++++--- Player.cs | 40 +++++++++++++++++++++ Program.cs | 11 ++++-- Window.cs | 32 +++-------------- World.cs | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 171 insertions(+), 36 deletions(-) create mode 100644 Player.cs create mode 100644 World.cs diff --git a/Blocks.cs b/Blocks.cs index fb41c05..75d7f92 100644 --- a/Blocks.cs +++ b/Blocks.cs @@ -6,7 +6,8 @@ Stone, Dirt, OakPlanks, - Grass + Grass, + Bedrock } public enum Orientation : uint @@ -60,6 +61,7 @@ { Voxel.Blocks.Stone, new BlockDefinition(Voxel.Blocks.Stone, Voxel.Textures.Stone) }, { Voxel.Blocks.Dirt, new BlockDefinition(Voxel.Blocks.Dirt, Voxel.Textures.Dirt) }, { Voxel.Blocks.OakPlanks, new BlockDefinition(Voxel.Blocks.OakPlanks, Voxel.Textures.OakPlanks) }, + { Voxel.Blocks.Bedrock, new BlockDefinition(Voxel.Blocks.Bedrock, Voxel.Textures.Bedrock) }, { Voxel.Blocks.Grass, new BlockDefinition( Voxel.Blocks.Grass, diff --git a/Chunk.cs b/Chunk.cs index 9528265..011f28c 100644 --- a/Chunk.cs +++ b/Chunk.cs @@ -2,8 +2,8 @@ { public class Chunk { - public readonly int Size = 16; - public readonly int Height = 256; + public static int Size = 16; + public static int Height = 256; public readonly int PositionX; public readonly int PositionY; @@ -45,9 +45,12 @@ private void Initialize() { - Random rng = new Random(); - for (int i = 0; i < Size * Size * 16; i++) - _blocks[i] = (Blocks)rng.Next(5); + for (int i = 0; i < Size * Size; i++) + _blocks[i] = Blocks.Bedrock; + for (int i = Size * Size * 1; i < Size * Size * 15; i++) + _blocks[i] = Blocks.Stone; + for (int i = Size * Size * 15; i < Size * Size * 16; i++) + _blocks[i] = Blocks.Grass; } // todo @@ -62,6 +65,9 @@ private int GetBlockIndex(int x, int y, int z) { + if (x < 0 || x > 15 || y < 0 || y > 255 || z < 0 || z > 15) + return 0; + return x + z * Size + y * Size * Size; } diff --git a/Player.cs b/Player.cs new file mode 100644 index 0000000..0b65af3 --- /dev/null +++ b/Player.cs @@ -0,0 +1,40 @@ +using OpenTK.Mathematics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Voxel +{ + public class Player + { + public Vector3 Position; + + private World _world; + + public Player(World world, Vector3 startPos) + { + _world = world; + } + + public void BreakBlock() + { + var (hit, x, y, z) = _world.Raycast(5f); // max 5 blocks + if (hit != Blocks.Air) + { + _world.SetBlock(x, y, z, Blocks.Air); + } + } + + public void Update(float deltaTime) + { + Camera.Update(deltaTime); + + if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.Space)) + { + BreakBlock(); + } + } + } +} diff --git a/Program.cs b/Program.cs index d4806e0..a4e4e5c 100644 --- a/Program.cs +++ b/Program.cs @@ -1,4 +1,5 @@ -using Voxel; +using OpenTK.Mathematics; +using Voxel; internal class Program { @@ -8,15 +9,19 @@ internal class Program int sizeY = 600; string title = "Game"; + World world = new World(); Window window = new Window(sizeX, sizeY, title); + Player player = new Player(world, Vector3.Zero); + + window.Player = player; Chunk chunk = new Chunk(0, 0); + world.AddChunk(chunk); + ChunkMesh chunkMesh = chunk.GetChunkMesh(); Renderer.AddFaces(chunkMesh.Faces); - window.Chunk = chunk; - window.Run(); } } \ No newline at end of file diff --git a/Window.cs b/Window.cs index 7c0e9a1..5a7ac56 100644 --- a/Window.cs +++ b/Window.cs @@ -1,5 +1,6 @@ using OpenTK.Graphics.OpenGL4; using OpenTK.Windowing.Common; +using OpenTK.Windowing.Common.Input; using OpenTK.Windowing.Desktop; namespace Voxel @@ -8,14 +9,10 @@ namespace Voxel { public readonly int Width = width; public readonly int Height = height; + public Player Player; public uint frames = 0; public double timeElapsed = 0; - // testing - public Chunk Chunk; - private Dictionary _cache = new Dictionary(); - private bool chunkEmpty = false; - protected override void OnUpdateFrame(FrameEventArgs e) { base.OnUpdateFrame(e); @@ -25,30 +22,10 @@ namespace Voxel Close(); } - RemoveRandomBlocks(); - } - - private void RemoveRandomBlocks() - { - if (Chunk != null && !chunkEmpty) + if (Player != null) { - Random rng = new Random(); - - int GetNum() - { - int max = 4096; - int num = rng.Next(max); - if (_cache.ContainsKey(num)) return GetNum(); - _cache[num] = true; - if (_cache.Count == max) chunkEmpty = true; - return num; - } - - Chunk.SetBlockIndex(GetNum(), Blocks.Air); + Player.Update((float)e.Time); } - - Renderer.ClearFaces(); - Renderer.AddFaces(Chunk.GetChunkMesh().Faces); } protected override void OnRenderFrame(FrameEventArgs e) @@ -93,6 +70,7 @@ namespace Voxel GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat); CursorState = CursorState.Grabbed; + Cursor = MouseCursor.Default; VSync = VSyncMode.On; Camera.UpdateProjection(Width, Height); diff --git a/World.cs b/World.cs new file mode 100644 index 0000000..c7008a2 --- /dev/null +++ b/World.cs @@ -0,0 +1,104 @@ +using OpenTK.Mathematics; +using System.Collections.Generic; + +namespace Voxel +{ + public class World + { + private Dictionary<(int, int), Chunk> _chunks; + + public World() + { + _chunks = new Dictionary<(int, int), Chunk>(); + } + + // Get a chunk at world coordinates, returns null if not loaded + public Chunk GetChunk(int chunkX, int chunkZ) + { + _chunks.TryGetValue((chunkX, chunkZ), out Chunk chunk); + return chunk; + } + + // Add a chunk + public void AddChunk(Chunk chunk) + { + _chunks[(chunk.PositionX, chunk.PositionY)] = chunk; + } + + // Remove a chunk + public void RemoveChunk(int chunkX, int chunkZ) + { + _chunks.Remove((chunkX, chunkZ)); + } + + // Iterate over all chunks + public IEnumerable GetAllChunks() + { + return _chunks.Values; + } + + // Optional: get a block at world coordinates + public Blocks GetBlock(int worldX, int worldY, int worldZ) + { + int chunkX = worldX / Chunk.Size; + int chunkZ = worldZ / Chunk.Size; + Chunk chunk = GetChunk(chunkX, chunkZ); + if (chunk == null) return 0; // air if chunk not loaded + + int localX = worldX % Chunk.Size; + int localZ = worldZ % Chunk.Size; + + return chunk.GetBlock(localX, worldY, localZ); + } + + // Optional: set a block at world coordinates + public void SetBlock(int worldX, int worldY, int worldZ, Blocks block) + { + int chunkX = worldX / Chunk.Size; + int chunkZ = worldZ / Chunk.Size; + Chunk chunk = GetChunk(chunkX, chunkZ); + if (chunk == null) return; + + int localX = worldX % Chunk.Size; + int localZ = worldZ % Chunk.Size; + + chunk.SetBlock(localX, worldY, localZ, block); + Renderer.ClearFaces(); + ChunkMesh chunkMesh = chunk.GetChunkMesh(); + Renderer.AddFaces(chunkMesh.Faces); + } + + public (Blocks block, int x, int y, int z) Raycast(float length) + { + Vector3 start = Camera.Position; + Vector3 dir = Camera.Front.Normalized(); + + float stepSize = 0.01f; + float distanceTraveled = 0f; + + while (distanceTraveled < length) + { + Vector3 point = start + dir * distanceTraveled; + + int bx = (int)MathF.Floor(point.X); + int by = (int)MathF.Floor(point.Y); + int bz = (int)MathF.Floor(point.Z); + + Blocks block = GetBlock(bx, by, bz); + if (block != Blocks.Air) + { + return (block, bx, by, bz); + } + + distanceTraveled += stepSize; + } + + return (Blocks.Air, 0, 0, 0); + } + + public void Update() + { + + } + } +}