CS50 2D - Lecture 1 - Flappy Bird
Watch on YouTube →
Overview
CS50's Flappy Bird lecture introduces 2D game development concepts using Lua and LÖVE 2D, building a Flappy Bird clone. Key topics include sprite rendering with `love.graphics.newImage`, parallax scrolling via modulo arithmetic, object-oriented programming with classes for game entities like birds and pipes, gravity simulation, input handling (keyboard and mouse), procedural generation of infinite pipe pairs, collision detection using AABB, state machines for managing game flow (title, play, score, countdown), and integrating sound effects and music. The lecture emphasizes code organization and modularity through states and object-oriented design.
Key takeaways
- Flappy Bird's core mechanics (gravity, jumping, infinite scrolling pipes) are implemented using Lua and LÖVE 2D.
- Object-oriented programming (classes for Bird, PipePair) and state machines are crucial for organizing complex game logic.
- Parallax scrolling is achieved by rendering background elements at different speeds, creating an illusion of depth.
- AABB collision detection is used, with a slight inward offset on the bird's bounding box for fairer gameplay.
- State machines provide a robust way to manage game flow, transitioning between distinct phases like title, play, and score screens.
- Custom input handling (`love.keyboard.wasPressed`, `love.mouse.wasPressed`) allows for single-press detection, mimicking mobile touch interactions.
Chapters
- Flappy Bird (2013) chosen as a more modern example than Pong.
- Key concepts: sprites, scrolling, parallax, procedural generation, state machines, touch input.
- Goal: Recreate Flappy Bird using open-source assets and LÖVE 2D.
- Volunteer David Colton demonstrates Flappy Bird gameplay.
- Controls: Spacebar to jump, Enter to restart.
- Game difficulty and scoring mechanics are highlighted.
- `love.graphics.newImage(path)` loads image files (PNG, GIF, JPEG).
- Images are stored in memory and drawn using `love.graphics.draw(image, x, y)`.
- Example: Loading and drawing background and ground textures.
- Parallax creates an illusion of depth by moving background elements at different speeds.
- Achieved by using looping textures and scrolling them at different rates (e.g., background slower than ground).
- Modulo operator (`%`) is used to create infinite scrolling by resetting texture position.
- Background and ground textures are scrolled using `backgroundScroll` and `groundScroll` variables.
- Modulo operator (`%`) with the texture's looping point (`413` for background) ensures seamless looping.
- Drawing is done at negative scroll positions (`-backgroundScroll`) to simulate leftward movement.
- Bird.png image loaded and dimensions dynamically retrieved (`getWidth`, `getHeight`).
- Bird class created with `init` and `render` methods.
- Bird is positioned in the center of the screen using virtual width/height and its own dimensions.
- Colon syntax (`:`) in Lua implicitly passes `self` as the first argument to a method.
- Example: `bird:render()` is equivalent to `bird.render(bird)`.
- This enables object-oriented programming and cleaner method calls.
- Gravity modeled as a constant `GRAVITY = 980` pixels/second².
- Bird's vertical velocity (`dy`) increases each frame by `gravity * dt`.
- Bird's `y` position is updated by adding `dy * dt`, causing it to fall.
- Jump implemented by setting `bird.dy` to a large negative value (e.g., -300).
- Gravity immediately starts counteracting the upward velocity.
- Tweaking `GRAVITY` and jump velocity balances game difficulty.
- Extending `love.keyboard` with a `keysPressed` table to track single key presses per frame.
- Custom `love.keyboard.wasPressed(key)` function checks the `keysPressed` table.
- Requires resetting `keysPressed` table at the end of `love.update` for frame-specific detection.
- Pipe pairs are stored in a `pipes` table (acting as an array).
- A `spawnTimer` accumulates `dt` to trigger pipe generation every 2 seconds.
- Pipes are created using `table.insert(pipes, PipePair.new(y))`.
- Iterating through `pipes` using `pairs(pipes)` and updating each pipe pair's position.
- Pipes are removed using `table.remove(pipes, k)` when their `x` position is off-screen (`< -pipeWidth`).
- Garbage collection handles memory cleanup for removed objects.
- Pipe pairs are entities composed of two pipes (upper and lower).
- Pipe constructor takes `orientation` ('top'/'bottom') and `y` position.
- Upper pipes are flipped vertically using a negative Y scale factor during rendering.
- Top pipes are rendered with `love.graphics.scale(1, -1)` to flip them vertically.
- A Y-offset (`self.y + pipeHeight`) is added during rendering to keep the flipped pipe in place.
- This prevents the flipped pipe from appearing off-screen.
Summary, takeaways, and chapters were generated by AI from the video's transcript and may contain errors. The video belongs to its creator, CS50.