using System.Numerics; using Raylib_cs; namespace PhysicsEngine { class Program { private static Font robotoFont; private static WorldModel world; // Instance of our WorldModel static void Main(string[] args) { Raylib.SetConfigFlags(ConfigFlags.FullscreenMode | ConfigFlags.VSyncHint); Raylib.InitWindow(0, 0, "Physics Engine"); robotoFont = Raylib.LoadFont("Roboto-Regular.ttf"); Raylib.SetTextureFilter(robotoFont.Texture, TextureFilter.Bilinear); Camera camera = new Camera(); world = new WorldModel(); // Initialize simulation world ScenarioLarge(); const float physicsTimeStep = 1.0f / 60.0f; float accumulator = 0.0f; // Main loop while (!Raylib.WindowShouldClose()) { float frameTime = Raylib.GetFrameTime(); if (frameTime > 0.25f) { frameTime = 0.25f; } // Update Camera camera.Update(frameTime); // Update Physics accumulator accumulator += frameTime; while (accumulator >= physicsTimeStep) { UpdatePhysics(physicsTimeStep); accumulator -= physicsTimeStep; } // Render loop Raylib.BeginDrawing(); Raylib.ClearBackground(MuiDarkColor.BackgroundDefault.ToColor()); // Draw background screen-space elements (Grid) camera.DrawGrid(); // Draw World Bodies inside the Camera's 2D World Transform Raylib.BeginMode2D(camera.RaylibCamera); RenderScene(); Raylib.EndMode2D(); // Draw UI / HUD elements camera.DrawHUD(); DrawText("Physics Engine", 20, 20, 30, MuiDarkColor.TextPrimary.ToColor()); Raylib.EndDrawing(); } Raylib.UnloadFont(robotoFont); Raylib.CloseWindow(); } public static void DrawText(string text, int posX, int posY, float fontSize, Color color) { Raylib.DrawTextEx(robotoFont, text, new Vector2(posX, posY), fontSize, 1.0f, color); } private static void UpdatePhysics(float dt) { world.Step(dt); } private static void RenderScene() { // Iterate and draw all bodies in the world foreach (var body in world.Bodies) { body.Draw(); } } private static void Scenario() { float worldWidth = 12.8f; float worldHeight = 7.2f; float wallThickness = 1.0f; // 1. Static world borders Box floor = new Box(new Vector2(worldWidth * 0.5f, worldHeight + (wallThickness * 0.5f)), new Vector2(worldWidth + wallThickness * 2, wallThickness), 0f); floor.BaseColor = Color.DarkGray; world.AddBody(floor); Box ceiling = new Box(new Vector2(worldWidth * 0.5f, -wallThickness * 0.5f), new Vector2(worldWidth + wallThickness * 2, wallThickness), 0f); ceiling.BaseColor = Color.DarkGray; world.AddBody(ceiling); Box leftWall = new Box(new Vector2(-wallThickness * 0.5f, worldHeight * 0.5f), new Vector2(wallThickness, worldHeight + wallThickness * 2), 0f); leftWall.BaseColor = Color.DarkGray; world.AddBody(leftWall); Box rightWall = new Box(new Vector2(worldWidth + wallThickness * 0.5f, worldHeight * 0.5f), new Vector2(wallThickness, worldHeight + wallThickness * 2), 0f); rightWall.BaseColor = Color.DarkGray; world.AddBody(rightWall); // 2. Static ramps and platforms (rotated boxes) // Left ramp (slopes up to the right) Box leftRamp = new Box(new Vector2(2.5f, 5.5f), new Vector2(4.0f, 0.4f), 0f); leftRamp.Rotation = -0.4f; // tilt upward to the right leftRamp.BaseColor = Color.Gray; world.AddBody(leftRamp); // Right ramp (slopes down to the right) Box rightRamp = new Box(new Vector2(10.5f, 5.5f), new Vector2(4.0f, 0.4f), 0f); rightRamp.Rotation = 0.4f; // tilt downward to the right rightRamp.BaseColor = Color.Gray; world.AddBody(rightRamp); // Floating platform in the middle Box platform = new Box(new Vector2(6.4f, 3.5f), new Vector2(3.0f, 0.4f), 0f); platform.BaseColor = Color.LightGray; world.AddBody(platform); // Small static circle bumper near the bottom center Circle staticBumper = new Circle(new Vector2(6.4f, 6.0f), 0.3f, 0f); staticBumper.BaseColor = Color.Orange; world.AddBody(staticBumper); // 3. Dynamic bodies – a mix of boxes and circles with varied properties // Tall, thin box (will tip over when hitting the ground) Box tallBox = new Box(new Vector2(2.0f, 1.0f), new Vector2(0.3f, 1.8f), 30f); tallBox.BaseColor = Color.SkyBlue; tallBox.Rotation = 0.3f; tallBox.AngularVelocity = 0.5f; tallBox.Friction = 0.8f; tallBox.Restitution = 0.1f; world.AddBody(tallBox); // Heavy square box Box heavyBox = new Box(new Vector2(4.0f, 1.5f), new Vector2(0.8f, 0.8f), 80f); heavyBox.BaseColor = Color.DarkGreen; heavyBox.Friction = 0.9f; heavyBox.Restitution = 0.0f; heavyBox.Velocity = new Vector2(1.5f, -1.0f); world.AddBody(heavyBox); // Long plank (will slide and possibly balance) Box plank = new Box(new Vector2(8.0f, 0.5f), new Vector2(2.5f, 0.25f), 25f); plank.BaseColor = Color.Brown; plank.Rotation = -0.2f; plank.AngularVelocity = -0.3f; plank.Friction = 0.6f; world.AddBody(plank); // Bouncy ball (high restitution, low friction) Circle bouncyBall = new Circle(new Vector2(9.0f, 1.0f), 0.3f, 5f); bouncyBall.BaseColor = Color.Red; bouncyBall.Restitution = 0.9f; bouncyBall.Friction = 0.05f; bouncyBall.Velocity = new Vector2(-30.0f, -20.0f); bouncyBall.AngularVelocity = 10f; world.AddBody(bouncyBall); // Heavy circle (rolls slowly) Circle heavyBall = new Circle(new Vector2(5.0f, 0.8f), 0.5f, 40f); heavyBall.BaseColor = Color.Purple; heavyBall.Friction = 0.7f; heavyBall.Restitution = 0.1f; heavyBall.Velocity = new Vector2(2.0f, -0.5f); world.AddBody(heavyBall); // Small fast circle Circle fastCircle = new Circle(new Vector2(1.0f, 2.0f), 0.2f, 2f); fastCircle.BaseColor = Color.Yellow; fastCircle.Restitution = 0.7f; fastCircle.Friction = 0.2f; fastCircle.Velocity = new Vector2(6.0f, 2.0f); world.AddBody(fastCircle); // Another box with high drag (will slow down quickly) Box dragBox = new Box(new Vector2(10.0f, 2.0f), new Vector2(0.6f, 0.6f), 15f); dragBox.BaseColor = Color.Violet; dragBox.DragCoefficient = 2.0f; dragBox.Velocity = new Vector2(-2.0f, -1.0f); dragBox.AngularVelocity = 2.0f; world.AddBody(dragBox); // Circle with high drag (like a light ball in air) Circle dragCircle = new Circle(new Vector2(3.0f, 3.0f), 0.4f, 8f); dragCircle.BaseColor = Color.Pink; dragCircle.DragCoefficient = 1.5f; dragCircle.Velocity = new Vector2(1.0f, 3.0f); world.AddBody(dragCircle); // A box launched with an impulse at an offset (will spin) Box spinningBox = new Box(new Vector2(7.0f, 2.5f), new Vector2(0.5f, 0.5f), 10f); spinningBox.BaseColor = Color.Beige; spinningBox.ApplyImpulseAtOffset(new Vector2(3.0f, -5.0f), new Vector2(0.25f, 0.0f)); world.AddBody(spinningBox); // A circle launched with angular impulse Circle spinningCircle = new Circle(new Vector2(11.0f, 1.0f), 0.35f, 12f); spinningCircle.BaseColor = Color.Lime; spinningCircle.ApplyImpulseAtOffset(new Vector2(0.0f, -8.0f), new Vector2(0.1f, 0.0f)); world.AddBody(spinningCircle); } private static void ScenarioLarge() { // Larger world dimensions float worldWidth = 20.0f; float worldHeight = 12.0f; float wallThickness = 1.0f; // 1. Static world borders Box floor = new Box(new Vector2(worldWidth * 0.5f, worldHeight + (wallThickness * 0.5f)), new Vector2(worldWidth + wallThickness * 2, wallThickness), 0f); floor.BaseColor = Color.DarkGray; world.AddBody(floor); Box ceiling = new Box(new Vector2(worldWidth * 0.5f, -wallThickness * 0.5f), new Vector2(worldWidth + wallThickness * 2, wallThickness), 0f); ceiling.BaseColor = Color.DarkGray; world.AddBody(ceiling); Box leftWall = new Box(new Vector2(-wallThickness * 0.5f, worldHeight * 0.5f), new Vector2(wallThickness, worldHeight + wallThickness * 2), 0f); leftWall.BaseColor = Color.DarkGray; world.AddBody(leftWall); Box rightWall = new Box(new Vector2(worldWidth + wallThickness * 0.5f, worldHeight * 0.5f), new Vector2(wallThickness, worldHeight + wallThickness * 2), 0f); rightWall.BaseColor = Color.DarkGray; world.AddBody(rightWall); // 2. Static ramps (angled platforms) Box ramp1 = new Box(new Vector2(4.0f, 9.0f), new Vector2(5.0f, 0.4f), 0f); ramp1.Rotation = -0.35f; ramp1.BaseColor = Color.Gray; world.AddBody(ramp1); Box ramp2 = new Box(new Vector2(16.0f, 9.0f), new Vector2(5.0f, 0.4f), 0f); ramp2.Rotation = 0.35f; ramp2.BaseColor = Color.Gray; world.AddBody(ramp2); // Static horizontal platforms Box platform1 = new Box(new Vector2(7.0f, 6.0f), new Vector2(4.0f, 0.4f), 0f); platform1.BaseColor = Color.LightGray; world.AddBody(platform1); Box platform2 = new Box(new Vector2(13.0f, 6.0f), new Vector2(4.0f, 0.4f), 0f); platform2.BaseColor = Color.LightGray; world.AddBody(platform2); // Static vertical pillars to create obstacles Box pillar1 = new Box(new Vector2(5.0f, 4.0f), new Vector2(0.3f, 3.0f), 0f); pillar1.BaseColor = Color.DarkBrown; world.AddBody(pillar1); Box pillar2 = new Box(new Vector2(15.0f, 4.0f), new Vector2(0.3f, 3.0f), 0f); pillar2.BaseColor = Color.DarkBrown; world.AddBody(pillar2); // Static circular bumpers Circle bumper1 = new Circle(new Vector2(10.0f, 8.0f), 0.4f, 0f); bumper1.BaseColor = Color.Orange; world.AddBody(bumper1); Circle bumper2 = new Circle(new Vector2(10.0f, 3.0f), 0.4f, 0f); bumper2.BaseColor = Color.Orange; world.AddBody(bumper2); Circle bumper3 = new Circle(new Vector2(2.0f, 7.0f), 0.3f, 0f); bumper3.BaseColor = Color.Orange; world.AddBody(bumper3); Circle bumper4 = new Circle(new Vector2(18.0f, 7.0f), 0.3f, 0f); bumper4.BaseColor = Color.Orange; world.AddBody(bumper4); // 3. Dynamic bodies – many with high energy, low damping // High restitution balls (bouncy) for (int i = 0; i < 6; i++) { Circle bouncyBall = new Circle(new Vector2(2.0f + i * 3.0f, 1.5f), 0.25f, 3.0f); bouncyBall.BaseColor = Color.Red; bouncyBall.Restitution = 0.95f; bouncyBall.Friction = 0.05f; bouncyBall.Velocity = new Vector2((i % 2 == 0 ? 3.0f : -3.0f), -2.0f); bouncyBall.AngularVelocity = 5.0f; world.AddBody(bouncyBall); } // Heavy boxes (low restitution, high friction) Box heavy1 = new Box(new Vector2(6.0f, 2.0f), new Vector2(1.0f, 0.8f), 60f); heavy1.BaseColor = Color.DarkGreen; heavy1.Friction = 0.9f; heavy1.Restitution = 0.0f; heavy1.Velocity = new Vector2(2.0f, -1.0f); world.AddBody(heavy1); Box heavy2 = new Box(new Vector2(14.0f, 2.0f), new Vector2(1.0f, 0.8f), 60f); heavy2.BaseColor = Color.DarkGreen; heavy2.Friction = 0.9f; heavy2.Restitution = 0.0f; heavy2.Velocity = new Vector2(-2.0f, -1.0f); world.AddBody(heavy2); // Long planks (will slide and rotate) Box plank1 = new Box(new Vector2(8.0f, 10.5f), new Vector2(3.0f, 0.3f), 20f); plank1.BaseColor = Color.Brown; plank1.Rotation = 0.1f; plank1.AngularVelocity = -0.4f; plank1.Friction = 0.6f; plank1.Velocity = new Vector2(1.0f, -0.5f); world.AddBody(plank1); Box plank2 = new Box(new Vector2(12.0f, 10.5f), new Vector2(3.0f, 0.3f), 20f); plank2.BaseColor = Color.Brown; plank2.Rotation = -0.1f; plank2.AngularVelocity = 0.4f; plank2.Friction = 0.6f; plank2.Velocity = new Vector2(-1.0f, -0.5f); world.AddBody(plank2); // Spinning boxes (launched with off-center impulses) Box spinBox1 = new Box(new Vector2(5.0f, 7.0f), new Vector2(0.6f, 0.6f), 10f); spinBox1.BaseColor = Color.Beige; spinBox1.ApplyImpulseAtOffset(new Vector2(4.0f, -6.0f), new Vector2(0.3f, 0.0f)); world.AddBody(spinBox1); Box spinBox2 = new Box(new Vector2(15.0f, 7.0f), new Vector2(0.6f, 0.6f), 10f); spinBox2.BaseColor = Color.Beige; spinBox2.ApplyImpulseAtOffset(new Vector2(-4.0f, -6.0f), new Vector2(-0.3f, 0.0f)); world.AddBody(spinBox2); // Small fast circles Circle fast1 = new Circle(new Vector2(3.0f, 11.0f), 0.2f, 2f); fast1.BaseColor = Color.Yellow; fast1.Restitution = 0.8f; fast1.Friction = 0.2f; fast1.Velocity = new Vector2(8.0f, 1.0f); world.AddBody(fast1); Circle fast2 = new Circle(new Vector2(17.0f, 11.0f), 0.2f, 2f); fast2.BaseColor = Color.Yellow; fast2.Restitution = 0.8f; fast2.Friction = 0.2f; fast2.Velocity = new Vector2(-8.0f, 1.0f); world.AddBody(fast2); // High-drag objects that still move initially Box dragBox = new Box(new Vector2(10.0f, 9.0f), new Vector2(0.8f, 0.8f), 15f); dragBox.BaseColor = Color.Violet; dragBox.DragCoefficient = 2.0f; dragBox.Velocity = new Vector2(3.0f, -2.0f); dragBox.AngularVelocity = 2.0f; world.AddBody(dragBox); Circle dragCircle = new Circle(new Vector2(10.0f, 5.0f), 0.4f, 8f); dragCircle.BaseColor = Color.Pink; dragCircle.DragCoefficient = 1.5f; dragCircle.Velocity = new Vector2(-2.0f, 3.0f); world.AddBody(dragCircle); // A few more random objects to fill the space Circle extra1 = new Circle(new Vector2(1.5f, 5.0f), 0.35f, 12f); extra1.BaseColor = Color.Lime; extra1.Restitution = 0.7f; extra1.Velocity = new Vector2(5.0f, -3.0f); extra1.AngularVelocity = 8.0f; world.AddBody(extra1); Circle extra2 = new Circle(new Vector2(18.5f, 5.0f), 0.35f, 12f); extra2.BaseColor = Color.Lime; extra2.Restitution = 0.7f; extra2.Velocity = new Vector2(-5.0f, -3.0f); extra2.AngularVelocity = -8.0f; world.AddBody(extra2); Box extraBox = new Box(new Vector2(10.0f, 1.0f), new Vector2(1.2f, 0.6f), 25f); extraBox.BaseColor = Color.SkyBlue; extraBox.Velocity = new Vector2(0.5f, -2.0f); world.AddBody(extraBox); } } }