Angry Birds
Objectives
- Implement
Aliensplitting. - Implement varied textures (with variable strengths).
- Implement
Jointtypes.
Getting Started
Download the distribution code for your game from cdn.cs50.net/2d/2025/x/projects/6/angry.zip and unzip angry.zip, which should yield a directory called angry.
Then, in a terminal window, 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 theAlieninto threeAliensthat all behave just like the baseAlien. The code for actually launching theAlienexists inAlienLaunchMarker, and we could naively implement most, if not all, of this code in the same class, since theAlienin 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 thisAliencollides with anything else, so we’ll likely want the logic for this in theLevelitself here, since that is where we pass in the collision callbacks viaWorld:setCallbacks(). The centerAliendoesn’t really need to be modified for the splitting process; really, all we need to do is spawn two newAliens at the right angle and velocity so that it appears we’ve turned the singleAlieninto three, one above and one below. For this, you’ll need to take linear velocity into consideration. Additionally, be aware that theAlienwe want to launch has theuserDataof the string “Player”, as opposed to theAlienwe want to kill, which has just theuserDataof “Alien”. Finally for this objective, be sure that the launch marker doesn’t reset until all of theAliens we fling have slowed to nearly being still, not just the oneAlienwe 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.pngandgraphics/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 theLevel:init’sbeginContactcallback), 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 allObstacles 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 anObstacle,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 theObstacles’Fixtures to allow for including theObstaclereference itself as part of the user data, e.g., using something like the following inObstacle.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
Jointtype 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 oneJointtype to the level, combiningObstacles, either those pre-existing in the scene or new ones, to do whatever your imagination concocts!
Be sure to comment your code in the places where you implement the above items. This is true for all implementations, but particularly if you do so in a spot we might not expect you to.
Errata
NONE
How to Submit
When you submit your project, the contents of your branch must match the file structure of the unzipped distribution code exactly as originally received. That is to say, your files should not be nested inside of any other directories of your own creation or otherwise deviate from the file structure we gave you. Your branch should also not contain any code from any other projects, only this one. Failure to adhere to this file structure will result in your submission being rejected.
By way of a simple example, for this project that means that if the grading staff visits https://github.com/me50/USERNAME/blob/games50/projects/2025/x/angry/src/Level.lua (where USERNAME is your own GitHub username as provided in the form, below) we should be brought to your Level.lua file for Angry Birds. If that’s not how your code is organized when you check (e.g., you get a 404 error or don’t see your edits), reorganize your repository as needed to match this paradigm. Code improperly organized will not be eligible for a passing score.
- If you haven’t already, visit this link, log in with your GitHub account, and click Authorize cs50. Then, check the box indicating that you’d like to grant course staff access to your submissions, and click Join course.
-
Install Git and, optionally, install
submit50. -
Using Git, push your work to
https://github.com/me50/USERNAME.git, whereUSERNAMEis your GitHub username, on a branch calledgames50/projects/2025/x/angryor, if you’ve installedsubmit50, executesubmit50 games50/projects/2025/x/angryinstead.
-
Record a screencast, not to exceed 5 minutes in length in which you demonstrate your game’s functionality. Upload that video to YouTube (as unlisted or public, but not private). This video’s requirements are:
- It is not a YouTube “short”.
- The video begins with a slide or text overlay containing both your edX and GitHub usernames.
- It must show your game live and in action. Do not use this video to walk us through any code.
- It demonstrates that all four (4) items of the specification have been implemented.
- The video description has been timestamped at the (first) point where your video demonstrates each of the above-referenced implementations.
- The video has been uploaded less than one month from the time of your submission of the form for this project (the final step below).
- Submit this form.
You can then go to https://cs50.me/cs502d to view your current progress!