From f4deaadb57d214d9d220e117c90f4f7972337763 Mon Sep 17 00:00:00 2001 From: maxwes08 Date: Tue, 21 Oct 2025 10:47:45 +0200 Subject: [PATCH] Add project files. --- Pillar.cs | 39 ++++++++ Program.cs | 211 ++++++++++++++++++++++++++++++++++++++++ SFML Flappy Bird.csproj | 30 ++++++ SFML Flappy Bird.sln | 25 +++++ background.png | Bin 0 -> 4576 bytes bird.png | Bin 0 -> 486 bytes pillar-down.png | Bin 0 -> 546 bytes pillar-up.png | Bin 0 -> 540 bytes 8 files changed, 305 insertions(+) create mode 100644 Pillar.cs create mode 100644 Program.cs create mode 100644 SFML Flappy Bird.csproj create mode 100644 SFML Flappy Bird.sln create mode 100644 background.png create mode 100644 bird.png create mode 100644 pillar-down.png create mode 100644 pillar-up.png diff --git a/Pillar.cs b/Pillar.cs new file mode 100644 index 0000000..e632f70 --- /dev/null +++ b/Pillar.cs @@ -0,0 +1,39 @@ +using SFML.Graphics; + +class Pillar +{ + public Sprite Top; + public Sprite Bottom; + public float X; + + public Pillar(Texture topTex, Texture bottomTex, float startX, float gapY, float gapHeight) + { + X = startX; + + Top = new Sprite(topTex) { Scale = new SFML.System.Vector2f(2f, 2f) }; + Bottom = new Sprite(bottomTex) { Scale = new SFML.System.Vector2f(2f, 2f) }; + + Top.Position = new SFML.System.Vector2f(X, gapY - gapHeight / 2f - topTex.Size.Y * Top.Scale.Y); + Bottom.Position = new SFML.System.Vector2f(X, gapY + gapHeight / 2f); + } + + public void Update(float deltaTime, float speed) + { + X -= speed * deltaTime; + Top.Position = new SFML.System.Vector2f(X, Top.Position.Y); + Bottom.Position = new SFML.System.Vector2f(X, Bottom.Position.Y); + } + + public void Draw(RenderWindow window) + { + window.Draw(Top); + window.Draw(Bottom); + } + + public bool IsOffscreen(float screenWidth) + { + // Rightmost point of the pillar + float pillarRightEdge = X + Top.Texture.Size.X * Top.Scale.X; + return pillarRightEdge < 0; + } +} diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..a1db4c8 --- /dev/null +++ b/Program.cs @@ -0,0 +1,211 @@ +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; // px/sec + float scrollSpeed = 150f; // 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; + + // 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.SetFramerateLimit(60); + + // 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, 100) // red with transparency + }; + + // Clock for deltaTime + Clock clock = new Clock(); + + 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; + } + + // 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(); + } + } +} diff --git a/SFML Flappy Bird.csproj b/SFML Flappy Bird.csproj new file mode 100644 index 0000000..d9bafad --- /dev/null +++ b/SFML Flappy Bird.csproj @@ -0,0 +1,30 @@ + + + + WinExe + net8.0 + SFML_Flappy_Bird + enable + enable + + + + + + + + + Always + + + Always + + + Always + + + Always + + + + diff --git a/SFML Flappy Bird.sln b/SFML Flappy Bird.sln new file mode 100644 index 0000000..e9f417f --- /dev/null +++ b/SFML Flappy Bird.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36414.22 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SFML Flappy Bird", "SFML Flappy Bird.csproj", "{5B9EC870-D392-4DA4-81B4-9AAB78D9F5DB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5B9EC870-D392-4DA4-81B4-9AAB78D9F5DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B9EC870-D392-4DA4-81B4-9AAB78D9F5DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B9EC870-D392-4DA4-81B4-9AAB78D9F5DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B9EC870-D392-4DA4-81B4-9AAB78D9F5DB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A0A9F4D4-D2BF-41DF-95F0-7BAC8195E11C} + EndGlobalSection +EndGlobal diff --git a/background.png b/background.png new file mode 100644 index 0000000000000000000000000000000000000000..1f9af512d05cfc90afa725036ba26acc0ca93ab7 GIT binary patch literal 4576 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 literal 0 HcmV?d00001 diff --git a/pillar-down.png b/pillar-down.png new file mode 100644 index 0000000000000000000000000000000000000000..92a5f34e4f354a4040f1dba82ac6d884248e02f2 GIT binary patch literal 546 zcmeAS@N?(olHy`uVBq!ia0vp^4h#&83>?frmdT6;6ClM{9OUlAuCJNT3*Jfk$L90|Vb-5N14{zaj-FC|2ScQ4*Y=R#Ki=l*$m0n3-3i=jR%tV5(>M zz4`lOpo%T25uRzDo>~kXKn^Q|6eBCpu?!4eKr9VqgZ!Yu$P5-|0XA+PN0!%X6s4hbT1CVU;615{iet*vZnR7i|978JN-d@?r)nXvx>bSXc_W%FMI~Q!y(TZ9! z(Ne%;qWMv6qpw?E2Hp;N|NHCzlcntrnoDcmg}jbfuyxMkXFIn>F*ZzRzW4+aM&1|fzC3=J#{Dh$L3E!ob*Z73+ZgmX{p7LY-n Lu6{1-oD!M?frmdT6;6ClM{9OUlAuT~85=MzfS3tV&$<9&&Lkik1ek!PFo9JD zSy}*DP+f)w1|ZqwC2B{4{QjN+GN*gGIEGZjy}fXdi^)*H<)G)L!vFvKmkNeCx`>}~ zeeI|)eWmmDx{#|;ulIg8{50?OHp6FgHovOdV*dwZEfQ#8VNhXEU}WHA@L+IYVi01O zz<^)Kp8v|qYYfxRN3(3X-L_uM=Xejp0eOb^(QI>Gb{`O3e}si+)|%5sAR9bg{an^L HB{Ts5kz#>i literal 0 HcmV?d00001