Skip to content

New Flappy Bird example #19282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

tertsdiepraam
Copy link

@tertsdiepraam tertsdiepraam commented May 18, 2025

Hi!

Objective

This adds a new example to the repository implementing Flappy Bird, as was suggested during the RustWeek unconf. As far as I understand, the goal is to add another example implementing a "full" game, combining multiple Bevy features.

Solution

This is a simplified implementation of Flappy Bird using the logo as the titular bird and simply using black rectangles as pipes. It features a scoring mechanism and resets on collision. Since this is an example, I tried to document even the more obvious parts.

While I've used Bevy a bit before, I do not have a very good idea of what idiomatic Bevy code looks like. I would appreciate any feedback there.

Missing from this implementation are:

  • Prettier artwork
  • Background music
  • A menu screen
  • A game over screen
  • A good way of handling window resizing

Testing

I tested this on my own machine. One thing that probably needs some tweaking is handling of other window sizes and resizing as the game is largely tailored to a particular screen size.

cargo run --example flappy_bird

Showcase

This is what it looks like:

image

Copy link
Contributor

Welcome, new contributor!

Please make sure you've read our contributing guide and we look forward to reviewing your pull request shortly ✨

@tertsdiepraam tertsdiepraam changed the title Add initial implementation of the Flappy Bird example New Flappy Bird example May 18, 2025
@@ -4361,3 +4361,14 @@ name = "Extended Bindless Material"
description = "Demonstrates bindless `ExtendedMaterial`"
category = "Shaders"
wasm = false

[[example]]
name = "flappy_bird"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Should we choose a name other than "Flappy Bird"? I would assume the name is trademarked, and unlike "Breakout", it's reasonably recent.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are probably right. I'll think about a nice bevy-inspired name!

Fun fact: apparently there is a whole thing around the copyright of Flappy Bird. Some crypto bros did a Flappy Bird thing and then the original creator got mad. There's also some people claiming the original Flappy Bird infringes on Nintendo copyright.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick search for Floppy Bevy led me to https://github.com/rust-adventure/floppy-corgi haha...

Comment on lines 12 to 36
const BACKGROUND_COLOR: Color = Color::srgb(0.9, 0.9, 0.9);

/// Timer spawning a pipe each time it finishes
const PIPE_TIMER_DURATION: Duration = Duration::from_millis(2000);

/// Movement speed of the pipes
const PIPE_SPEED: f32 = 200.;

/// The size of each pipe rectangle
const PIPE_SIZE: Vec2 = Vec2::new(100., 500.);

/// How large the gap is between the pipes
const GAP_HEIGHT: f32 = 300.;

/// Gravity applied to the bird
const GRAVITY: f32 = 700.;

/// Size of the bird sprite
const BIRD_SIZE: f32 = 100.;

/// Acceleration the bird is set to on a flap
const FLAP_POWER: f32 = 400.;

/// Horizontal position of the bird
const BIRD_POSITION: f32 = -500.;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Controversial Nit: This is consistent with our other examples, but I'm not a fan of these hard-coded constants. They're all-caps and I don't believe particularly good practice (these kinds of settings would normally be controlled in a configuration file read by the build system for example). I think it would be nicer if all of this was wrapped in a Settings resource (ideally implementing Reflect for runtime configuration...), with these values being in the Default implementation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be so good with this RFC: https://github.com/rust-lang/rfcs/blob/master/text/3681-default-field-values.md. I'll give your suggestion a go.

const BIRD_POSITION: f32 = -500.;

#[derive(Component)]
struct Bird;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super tiny nit: I believe we refer to the Bevy bird as birb in the examples, might be nice to be consistent here, even if it is a little silly.

) {
commands.spawn(Camera2d);

let score_sound = asset_server.load("sounds/breakout_collision.ogg");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let score_sound = asset_server.load("sounds/breakout_collision.ogg");
// TODO: Replace with a specific sound, or rename file
let score_sound = asset_server.load("sounds/breakout_collision.ogg");

Comment on lines 167 to 177
for ent in bird {
commands.entity(ent).despawn();
}

for ent in pipes {
commands.entity(ent).despawn();
}

for ent in pipe_markers {
commands.entity(ent).despawn();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future PR: Surprised we don't have a Commands::despawn_batch to make this cleaner! Might be nice to create a new EntityBatchCommands (similar to EntityCommands) for operations which could be applied to many entities, just as a quality-of-life improvement.

commands.batch([e1, e2, e3]).despawn();

Copy link
Contributor

@urben1680 urben1680 May 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cart once said something about a despawn_batch: #2693 (comment)

It seems to be valid today in principle.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll make it slightly cleaner by combining all the entities in a single query. Then at least it just becomes one for loop.

Comment on lines 181 to 183
for mut text in score_text {
text.0 = 0.to_string();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: We expect a single entity to exist with the ScoreText component. That query should be changed to Single<> and this for loop removed.

Comment on lines +41 to +45
#[derive(Component)]
struct Pipe;

#[derive(Component)]
struct PipeMarker;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Might be nice to use required-components to ensure these are added together. Likewise for Score and ScoreText.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think required components are correct here. At least as far as I understand required components. The pipes are represented with 1 marker and 2 pipes, so there isn't really a 1-1 requirement. For the scoring part, the Score is a resource, not a component, so I'm not sure that works either?

I might be totally off here, please correct me if I'm wrong.

@bushrat011899 bushrat011899 added C-Examples An addition or correction to our examples A-Meta About the project itself D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels May 19, 2025
@rparrett
Copy link
Contributor

I wonder a bit about using flappy bird specifically as an example. There's just a great deal of overlap in what this is demonstrating and what we already have in breakout.

@tertsdiepraam
Copy link
Author

I was wondering about that too. One solution to that would be to, for example, to put more emphasis on sprites and adding menus. Then this might become different enough to merit another example.

@tertsdiepraam
Copy link
Author

I added some menus to see how complex that would make it. I'm not sure whether that makes it better as an example. As far as I can see there is no example that features both a menu and a game, but it's also not too hard to infer. Anyway, I'm having fun learning those parts of bevy (also this is my first time using states, so ideas on how to make it cleaner are very welcome).

I can revert this commit if a more trimmed down version is preferred. I guess it comes down to what the goal of this example would be. Either we keep it very small (possibly stepping on the toes of breakout) or it's more complex (possibly too complex). Opinions welcome!

@tertsdiepraam tertsdiepraam marked this pull request as draft May 19, 2025 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Meta About the project itself C-Examples An addition or correction to our examples D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Needs-Review Needs reviewer attention (from anyone!) to move forward
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants