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.
If you are looking for a really small number to use, use -__DBL_MAX__
instead of __DBL_MIN__
!
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
.
Task 0.
First, copy and paste your previous implementations of sdl_wrapper.c
and
sdl_wrapper.h
.
Then, implement the function to get a bounding box for a body in sdl_wrapper.c
.
Make sure to define
the function header in sdl_wrapper.h
!
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
.
Task 1a.
First, copy and paste
your previous implementations of asset_cache.c
and asset.c
, being sure not to overwrite
the new image_asset_t
struct definition.
Then, implement and modify asset_make_image_with_body
and asset_make_image
, respectively.
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.
Task 1b.
Modify asset_render
so that images with bodies use the body’s bounding box.
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.
Task 2a.
Modify emscripten_init
so that the frog and log bodies attach to their corresponding
image assets. Make sure to only replace the TODOs labeled with “TODO (task 2a)...
”.
Once you’re done, run run make game
or make NO_ASAN=true game
.
If you’ve followed everything so far, your images might look a little funky:
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:
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.
Task 2b. Modify your function to get the bounding box of a body so that it converts each coordinate to the SDL coordinates.
Once you’re done, run make game
or make NO_ASAN=true game
.
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:
- We can render a background image. Since we are rendering all the images after we render the polygons, the polygons won’t be visible.
- We can delete
sdl_render_scene()
fromemscripten_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.
Task 2c.
Modify emscripten_init
so that the background image is initialized and emscripten_main
so that we no longer run sdl_render_scene
.
Once you’re done, run make game
or make NO_ASAN=true game
.
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.
Task 3. When you are done, your group should meet and complete the questions here.