CS 3 (Spring 2024) Project 06: Collision Resolution (Game)

Your task this week will be implement assets so that they can be attached to bodies.

Game

This week, you will be adding the ability to attach bodies to assets so that images can be rendered on top of bodies. After implementing the library, you will fill in parts of the game to implement a basic version of Frogger:

Attaching Bodies to Images

To render images over bodies, there are two design choices that we can make: attaching bodies to images, or attaching images to bodies. In this implementation, we’ll go with the former. Among other things, we’ll have to refactor less of our library, and we can preserve our pattern of storing assets in our game state and rendering them in emscripten_main.

Asset Library Overvieew

We’ll begin by taking a look at asset.c and asset.h. In asset.c, we’ve provided the new image_asset_t struct for you: the only thing we’ve added is a body field.

If you open up asset.h, you’ll notice that we’ve added a new function: asset_make_image_with_body. It allows the user to initialize an image asset using the filepath and body. Note that the function doesn’t need to take a bounding box anymore, because the image will be rendered over the body, whose bounding box may change after every tick.

Since we want the image to be rendered over its body, we’ll need a way of getting the bounding box that corresponds to the body. This leads us to our first task.

Getting the bounding box for a body

Getting the bounding box for a body will be relatively straightforward. We’ll want the bounding box to be the smallest box that can cover the body entirely.

Thus, we’ll iterate through all the points in the body, storing variables for the top left and bottom right corners as vectors. Once we’ve iterated through all the points, we’ll know that the smallest bounding box will have a top left corner at (min_x, max_y) and bottom right corner of (max_x, min_y). Then, we can use those points to calculate the fields of the SDL_Rect that we return. Remember that .x and .y refer to the top left point, and .w and .h can be calculated from the difference between the maximum and minimum x and y values.

bounding boxes

Since we are using SDL_Rect (and another reason that will become clear later), we should implement the function to get a bounding box for a body in sdl_wrapper.c.

Modifying asset.c

Now we can modify asset.c to render images that contain bodies.

Now that our image assets can contain bodies, we’ll have to modify our asset_make_image functions slightly. We’ll still keep our original asset_make_image function and just set body to NULL. In asset_make_image_with_body, we’ll set the body field appropriately but pass in an arbitrary SDL_Rect to asset_init.

Now that the user can initialize image assets with bodies, we’ll also have to change asset_render, and more specifically, the case if the asset has type ASSET_IMAGE.

The fix will be small: if the asset’s body field is not NULL, we’ll set the bounding box to the body’s bounding box (which we can get using the function we just wrote). Otherwise, we’ll render the image asset like normal.

game.c

Now we can start putting everything together in game.c.

We’ve given you most of the starter code. While you look through it, take a look at emscripten_main: it iterates through the body_assets list stored in the game state and renders each asset. Your job is to add the image assets to the list in emscripten_init. We’ve defined the filepaths for you at the top of the file. You’ll want to call the new asset_make_image_with_body function.

If you’ve followed everything so far, your images might look a little funky:

bounding boxes

It seems like the images are flipped across the center of the window. When the frog body moves up, the corresponding image moves down.

This is because of a small nuance in our function to get the bounding box for a body. In the SDL library, the y coordinates increase from top to bottom, not bottom to top. This explains why the images are “flipped” horizontally:

bounding boxes

How should we fix this? Take a look at sdl_draw_polygon function in sdl_wrapper.c and look at how polygons are rendered. As it iterates through all the points in the polygon, it accounts for the different y axes by using get_window_position and get_window_center to get the appropriate SDL coordinates. It then uses the new list of converted points to render the polygon onto the screen correctly.

We’ll do the same thing in our function to get a body’s bounding box. Since we’ve defined it in sdl_wrapper.c, we have access to the get_window_position and get_window_center functions.

Following sdl_draw_polygon as a guide, we’ll get the window center and use it to convert the top left and bottom right variables to the appropriate SDL coords.

Once you do that, you may also have to fix how we calculate .h for the SDL_Rect that we return since the top left corner now has a smaller SDL y coordinate than the bottom right corner. If .h is negative, the image won’t appear.

Now the images should overlay the bodies correctly. However, there’s one problem: the polygons for the bodies can be seen behind the images. There are two fixes for this:

  1. We can render a background image. Since we are rendering all the images after we render the polygons, the polygons won’t be visible.
  2. We can delete sdl_render_scene() from emscripten_main, since every body in our scene has a corresponding image.

In emscripten_init, replace the “TODO: (2c)...” with code to render the background image and add it to the state. Note that we shouldn’t be using asset_make_image_with_body since there is no body attached to the background image.

Even though we have a background image now, we might as well delete sdl_render_scene from emscripten_main since the polygons aren’t going to be visible anyways.

Congrats! You’ve implemented a basic version of Frogger.

Misc Notes

In this frogger game, we aren’t removing any bodies. However, if we had removed any bodies that were attached to images (which you will most likely be doing for your game), this could lead to problems like heap-use-after-free errors.

The easiest way to address this would be to check if the body that the image asset points to is removed before scene_tick is called (where each removed body is free’d). If the body is removed, then you should remove and free the corresponding image asset.