using Raylib_cs; using Sand.ChunkPrototype; const int screenWidth = 1280; const int screenHeight = 800; const int sidebarWidth = 220; const int particleSize = 4; const int simWidth = (screenWidth - sidebarWidth) / particleSize; const int simHeight = screenHeight / particleSize; const int brushRadius = 3; var adapter = new PrototypeSparseSandAdapter(simWidth, simHeight); var accumulator = 0f; var fixedStep = 1f / 60f; var stepCounter = 0; var currentParticle = PrototypeParticleType.Sand; var frameBuffer = new byte[simWidth * simHeight * 4]; Raylib.SetConfigFlags(ConfigFlags.ResizableWindow); Raylib.InitWindow(screenWidth, screenHeight, "Sand Chunk Prototype"); Raylib.SetTargetFPS(120); var frameImage = Raylib.GenImageColor(simWidth, simHeight, Color.Black); var frameTexture = Raylib.LoadTextureFromImage(frameImage); Raylib.UnloadImage(frameImage); while (!Raylib.WindowShouldClose()) { var dt = Raylib.GetFrameTime(); var mouse = Raylib.GetMousePosition(); var simMouseX = Math.Clamp((int)((mouse.X - sidebarWidth) / particleSize), 0, simWidth - 1); var simMouseY = Math.Clamp((int)(mouse.Y / particleSize), 0, simHeight - 1); var overWorld = mouse.X >= sidebarWidth; if (Raylib.IsKeyPressed(KeyboardKey.C)) { adapter = new PrototypeSparseSandAdapter(simWidth, simHeight); accumulator = 0f; stepCounter = 0; } if (Raylib.IsKeyPressed(KeyboardKey.One)) { currentParticle = PrototypeParticleType.Sand; } if (Raylib.IsKeyPressed(KeyboardKey.Two)) { currentParticle = PrototypeParticleType.Water; } if (Raylib.IsKeyPressed(KeyboardKey.Three)) { currentParticle = PrototypeParticleType.Steam; } if (Raylib.IsKeyPressed(KeyboardKey.Four)) { currentParticle = PrototypeParticleType.Wall; } if (Raylib.IsMouseButtonDown(MouseButton.Left) && overWorld) { PaintCircle(adapter, simMouseX, simMouseY, brushRadius, currentParticle, add: true); } if (Raylib.IsMouseButtonDown(MouseButton.Right) && overWorld) { PaintCircle(adapter, simMouseX, simMouseY, brushRadius, currentParticle, add: false); } accumulator += dt; var maxSteps = 8; while (accumulator >= fixedStep && maxSteps-- > 0) { adapter.World.ClearDirtyChunks(); adapter.Step(); stepCounter++; if ((stepCounter % 30) == 0) { adapter.TrimResidency(marginChunks: 1); } accumulator -= fixedStep; } if (accumulator > fixedStep * 4f) { accumulator = fixedStep; } var rgbaFrame = adapter.BuildRgbaFrame(); rgbaFrame.CopyTo(frameBuffer); unsafe { fixed (byte* pixels = frameBuffer) { Raylib.UpdateTexture(frameTexture, pixels); } } Raylib.BeginDrawing(); Raylib.ClearBackground(new Color(8, 10, 14, 255)); Raylib.DrawRectangle(0, 0, sidebarWidth, screenHeight, new Color(20, 24, 30, 255)); Raylib.DrawTextureEx(frameTexture, new System.Numerics.Vector2(sidebarWidth, 0), 0f, particleSize, Color.White); if (overWorld) { Raylib.DrawCircleLines((int)mouse.X, (int)mouse.Y, brushRadius * particleSize, new Color(255, 255, 255, 180)); } DrawPanel(adapter, currentParticle); Raylib.EndDrawing(); } Raylib.UnloadTexture(frameTexture); Raylib.CloseWindow(); static void PaintCircle(PrototypeSparseSandAdapter adapter, int centerX, int centerY, int radius, PrototypeParticleType type, bool add) { for (var dx = -radius; dx <= radius; dx++) { for (var dy = -radius; dy <= radius; dy++) { if ((dx * dx) + (dy * dy) > radius * radius) { continue; } var x = centerX + dx; var y = centerY + dy; if (x < 0 || y < 0 || x >= adapter.Width || y >= adapter.Height) { continue; } if (add) { adapter.AddParticle(x, y, type); } else { adapter.RemoveParticle(x, y); } } } } static void DrawPanel(PrototypeSparseSandAdapter adapter, PrototypeParticleType currentParticle) { Raylib.DrawText("Chunk Proto", 12, 14, 28, new Color(110, 172, 228, 255)); DrawParticleButton(12, 60, "1 Sand", currentParticle == PrototypeParticleType.Sand, new Color(214, 188, 96, 255)); DrawParticleButton(12, 96, "2 Water", currentParticle == PrototypeParticleType.Water, new Color(72, 132, 232, 255)); DrawParticleButton(12, 132, "3 Steam", currentParticle == PrototypeParticleType.Steam, new Color(182, 196, 214, 255)); DrawParticleButton(12, 168, "4 Wall", currentParticle == PrototypeParticleType.Wall, new Color(96, 96, 104, 255)); Raylib.DrawText($"FPS {Raylib.GetFPS()}", 12, 228, 20, Color.White); Raylib.DrawText($"Particles {adapter.ParticleCount}", 12, 252, 20, Color.White); Raylib.DrawText($"Loaded Chunks {adapter.World.LoadedChunkCount}", 12, 276, 20, Color.White); Raylib.DrawText($"Active Chunks {adapter.World.ActiveChunkCount}", 12, 300, 20, Color.White); Raylib.DrawText($"Dirty Chunks {adapter.World.DirtyChunkCount}", 12, 324, 20, Color.White); Raylib.DrawText("LMB paint", 12, 372, 18, Color.LightGray); Raylib.DrawText("RMB erase", 12, 394, 18, Color.LightGray); Raylib.DrawText("C clear", 12, 416, 18, Color.LightGray); } static void DrawParticleButton(int x, int y, string label, bool selected, Color color) { Raylib.DrawRectangleRounded(new Rectangle(x, y, 188, 28), 0.2f, 6, selected ? new Color(236, 236, 242, 255) : color); Raylib.DrawText(label, x + 10, y + 5, 18, selected ? Color.Black : Color.Black); }