using SFML.Graphics; using SFML.System; using SFML.Window; internal class Program { private static void Main() { // Game constants uint gameWidth = 900; uint gameHeight = 504; float gravity = 800f; // px/sec^2 float jumpForce = -300f; float scrollSpeedInitial = 200f;// px/sec float scrollSpeed = scrollSpeedInitial; // px/sec float scrollDecay = 500f; // px/sec^2 float maxUpAngle = -30f; // tilt up max float maxDownAngle = 60f; // tilt down max float maxVelocity = 500f; // velocity that corresponds to max tilt float backgroundSpeed = 0.75f; int points = 0; // Pillars List pillars = new List(); float spawnInterval = 2.0f; // seconds between pillar spawns float pillarTimer = spawnInterval; // triggers first pillar immediately float spacing = 200f; // horizontal distance between pillars // Gap limits float minGapHeight = 110f; float maxGapHeight = 300f; float minGapY = 100f; // min distance from top // Game status bool alive = true; Random random = new Random(); // Create window RenderWindow window = new RenderWindow(new VideoMode(gameWidth, gameHeight), "Flappy Bird Clone"); window.Closed += (s, e) => window.Close(); window.SetVerticalSyncEnabled(true); // Setup view for scaling View view = new View(new FloatRect(0, 0, gameWidth, gameHeight)); window.SetView(view); window.Resized += (s, e) => { float windowRatio = (float)e.Width / e.Height; float gameRatio = (float)gameWidth / gameHeight; FloatRect viewport; if (windowRatio > gameRatio) { float width = gameRatio / windowRatio; viewport = new FloatRect((1 - width) / 2f, 0, width, 1); } else { float height = windowRatio / gameRatio; viewport = new FloatRect(0, (1 - height) / 2f, 1, height); } view.Viewport = viewport; window.SetView(view); }; // Load textures Texture backgroundTexture = new Texture("background.png") { Repeated = true }; Texture birdTexture = new Texture("bird.png"); // Background rectangle RectangleShape backgroundRect = new RectangleShape(new Vector2f(gameWidth, gameHeight)); backgroundRect.Texture = backgroundTexture; long backgroundOffset = 0; // Bird sprite Sprite bird = new Sprite(birdTexture); bird.Scale = new Vector2f(3f, 3f); // scale as desired bird.Position = new Vector2f(100, gameHeight / 2f); bird.Origin = new SFML.System.Vector2f( bird.Texture.Size.X / 2f, // horizontal center bird.Texture.Size.Y / 2f // vertical center ); float birdVelocity = 0f; float hitboxWidth = bird.Texture.Size.X * bird.Scale.X * 0.6f; // 60% of sprite width float hitboxHeight = bird.Texture.Size.Y * bird.Scale.Y * 0.8f; // 80% of sprite height float hitboxOffsetX = bird.Texture.Size.X * bird.Scale.X * 0.2f; // offset from left float hitboxOffsetY = bird.Texture.Size.Y * bird.Scale.Y * 0.1f; // offset from top // Pillar textures Texture pillarUpTexture = new Texture("pillar-up.png"); Texture pillarDownTexture = new Texture("pillar-down.png"); RectangleShape hitboxRect = new RectangleShape(new Vector2f(hitboxWidth, hitboxHeight)) { FillColor = new Color(255, 0, 0, 0) // red with transparency }; // Clock for deltaTime Clock clock = new Clock(); void Restart() { birdVelocity = 0; pillars.Clear(); bird.Position = new Vector2f(100, gameHeight / 2f); backgroundOffset = 0; alive = true; scrollSpeed = scrollSpeedInitial; points = 0; } while (window.IsOpen) { float deltaTime = clock.Restart().AsSeconds(); window.DispatchEvents(); FloatRect birdHitbox = new FloatRect( bird.Position.X - hitboxWidth / 2f, bird.Position.Y - hitboxHeight / 2f, hitboxWidth, hitboxHeight ); // Check lose condition bool collision = false; foreach (var pillar in pillars) { if (birdHitbox.Intersects(pillar.Top.GetGlobalBounds()) || birdHitbox.Intersects(pillar.Bottom.GetGlobalBounds())) { collision = true; break; } } hitboxRect.Position = new Vector2f( bird.Position.X - hitboxWidth / 2f, bird.Position.Y - hitboxHeight / 2f ); if (collision && alive) { birdVelocity = 0; alive = false; } // Input if (alive && Keyboard.IsKeyPressed(Keyboard.Key.Space)) { birdVelocity = jumpForce; } if (!alive && Keyboard.IsKeyPressed(Keyboard.Key.Space)) { Restart(); } // Update pillars pillarTimer += deltaTime; if (pillarTimer > spawnInterval) { pillarTimer = 0f; // Randomize gap size float gapHeight = minGapHeight + (float)random.NextDouble() * (maxGapHeight - minGapHeight); float maxGapY = gameHeight - 100f - gapHeight; // adjust max Y for new gap float gapY = minGapY + (float)random.NextDouble() * (maxGapY - minGapY); // Add new pillar at the right edge of the screen pillars.Add(new Pillar(pillarUpTexture, pillarDownTexture, gameWidth, gapY, gapHeight)); } // Update existing pillars for (int i = pillars.Count - 1; i >= 0; i--) { pillars[i].Update(deltaTime, scrollSpeed); if (pillars[i].IsOffscreen(gameWidth)) pillars.RemoveAt(i); } // Physics birdVelocity += gravity * deltaTime; bird.Position += new Vector2f(0, birdVelocity * deltaTime); // Prevent bird from falling below floor float birdHeight = bird.Texture.Size.Y * bird.Scale.Y; if (bird.Position.Y + birdHeight > gameHeight) { bird.Position = new Vector2f(bird.Position.X, gameHeight - birdHeight); birdVelocity = 0; } if (!alive) { // Gradually reduce speed towards 0 scrollSpeed -= scrollDecay * deltaTime; if (scrollSpeed < 0) scrollSpeed = 0; } // Scroll background backgroundOffset += (long)(scrollSpeed * deltaTime * backgroundSpeed); if (backgroundOffset > backgroundTexture.Size.X) backgroundOffset -= backgroundTexture.Size.X; // Clear window.Clear(); // Draw tiled scrolling background backgroundRect.TextureRect = new IntRect((int)backgroundOffset, 0, (int)gameWidth, (int)gameHeight); window.Draw(backgroundRect); // Draw pillars foreach (var pillar in pillars) pillar.Draw(window); // Draw bird float targetRotation = MathF.Max(MathF.Min(birdVelocity / maxVelocity * maxDownAngle, maxDownAngle), maxUpAngle); bird.Rotation += (targetRotation - bird.Rotation) * 0.1f; // smoothing factor window.Draw(bird); window.Draw(hitboxRect); // Display window.Display(); } } }