Angry Birds

Objectives

  • Read and understand all of the Angry Birds source code from Lecture 6.
  • Implement Alien splitting.
  • Implement varied textures (with variable strengths).
  • Implement Joint types.

Getting Started

Download the distribution code for your game from cdn.cs50.net/2d/2025/x/projects/6/project6.zip and unzip project6.zip, which should yield a directory called angry.

Then, in a terminal window (located in /Applications/Utilities on Mac or by typing cmd in the Windows task bar), move to the directory where you extracted angry, and run

cd angry

Three’s Company

This week, we took a look at the fundamentals of Box2D, one of the most widely-used 2D physics engines, and how it ties into LÖVE, with its built-in wrappers for it. This assignment will be a little simpler than some of the previous ones (indeed, there’s only one core objective, albeit a reasonably complex one) but will still require knowledge of Box2D and the distro before we can dive in too quickly.

Specification

  • Implement it such that when the player presses the space bar after they’ve launched an Alien (and it hasn’t hit anything yet), split the Alien into three Aliens that all behave just like the base Alien. The code for actually launching the Alien exists in AlienLaunchMarker, and we could naively implement most, if not all, of this code in the same class, since the Alien in question we want to split off is a field of this class. However, because we want to only allow splitting before we’ve hit anything, we need a flag that will get triggered whenever this Alien collides with anything else, so we’ll likely want the logic for this in the Level itself here, since that is where we pass in the collision callbacks via World:setCallbacks(). The center Alien doesn’t really need to be modified for the splitting process; really, all we need to do is spawn two new Aliens at the right angle and velocity so that it appears we’ve turned the single Alien into three, one above and one below. For this, you’ll need to take linear velocity into consideration. Additionally, be aware that the Alien we want to launch has the userData of the string “Player”, as opposed to the Alien we want to kill, which has just the userData of “Alien”. Finally for this objective, be sure that the launch marker doesn’t reset until all of the Aliens we fling have slowed to nearly being still, not just the one Alien we normally check.
  • Incorporate the glass and metal material types as obstacles. As is often the case with the course’s projects overall, this project ships not just with the wood-textured sprites seen in lecture and the distribution code, but also with glass and metal sprites in similar shapes split out into separate files (graphics/metal.png and graphics/glass.png). Include at least one metal and one glass obstacle in the starting scene, either by including them additionally on top of the wood pieces already there or by replacing some of said wood pieces. Ensure that the glass piece is more sensitive to contact (as by checking the Level:init’s beginContact callback), as well as breaking upon hitting the ground. (Note: Per-material differences should be relatively easy to do as is, but if you want a sneak peek on how to potentially implement some of the data adjustments needed to more comfortably make per-obstacle behavior more feasible, as opposed to just blanket treating all Obstacles the same as we currently do, take a look at the next objective!)
  • Make wood and metal objects take multiple hits to destroy; each hit should add visible cracks to said Obstacles before they are destroyed. Right now, there are essentially just velocity checks to determine whether an Obstacle, Alien, etc. should break, and it’s an all-or-nothing calculation. Each spritesheet for the different materials, you’ll observe, comes with variants of said shapes that have cracks on them; leverage these to add not just a single-hit calculation for objects breaking, but instead a gradual weakening of said objects through multiple hits. Glass should preserve the existing behavior of just needing one hit; wood should take two hits; lastly, metal should require three hits. These hits as well should still be velocity-gated; in other words, a hit should not register unless it would have caused damage in the original implementation. There are multiple ways to accomplish this objective, but likely the most robust way will be to extend our usage of user data through the Obstacles’ Fixtures to allow for including the Obstacle reference itself as part of the user data, e.g., using something like the following in Obstacle.lua, from:
self.fixture:setUserData('Obstacle')

to:

self.fixture:setUserData({
  type = 'Obstacle',
  entity = self
})

whereby it should then be much easier to do things like call methods and update fields on the Obstacles themselves in the beginContact callback, such as HP adjustments, calling something like :takeDamage(1), etc.!

  • Incorporate at least one Joint type in your level. This one’s more for fun and flavor, but per some of the lecture examples, exercise your own creativity and apply at least one Joint type to the level, combining Obstacles, either those pre-existing in the scene or new ones, to do whatever your imagination concocts!

Errata

NONE

How to Submit

  1. Download a ZIP file containing your implementation of this project.
  2. Go to CSCI E-23a’s Gradescope page.
  3. Click Project 6: Angry Birds.
  4. Drag and drop your downloaded file to the area that says “Drag & Drop”.
  5. Click Upload.

You should see a message that your project was submitted successfully. Contact your teaching fellow if not!