Introduction
This week, you will add buttons to the asset library that your teammate implemented last week! These will hopefully help with any GUIs that your team decides to incorporate into your game in the latter half of the course. To test that it works, we will once again be improving on the meme generator from previous weeks by adding next/back and play/pause buttons to look like the following example:
Important Changes
We have changed get_keycode
and sdl_render_scene
in the wrapper this week, so
be sure not to overwrite any of those changes when copying and pasting code from
previous weeks over.
Overview
As you saw last week, implementing images and text as assets makes it much easier to intialize, manage, and use them. Thus, it makes sense to treat buttons as assets too. We’ll start by making the necessary changes in asset_cache.c
.
If you haven’t already, talk with your teammate who implemented the asset cache (game component) last week to gain an understanding of how it works.
asset_cache_t
First, copy and paste your implementation for asset_cache_obj_get_or_create
and asset_cache_free_entry
from last week. Remember to copy any helper functions over as well (if your team had any).
Next, implement asset_cache_register_button
. This is similar to the “create” part of asset_cache_obj_get_or_create
, only that we’re creating buttons and never retrieving them outside of asset_cache
. To stay consistent with how asset_cache
functions, we’ll need to wrap the button in an entry_t
type. We can do this by setting the conveniently typed obj
field to the button and filepath
to NULL. Remember that all buttons should also be added to ASSET_CACHE
!
Now that the filepath
of some entry_t
structs are NULL, some of our functions may break. Check to make sure that you’re not assuming filepath
is always non-NULL; if you are, rewrite those functions accordingly.
Note on Ownership
When buttons are registered, they are added to ASSET_CACHE
, whose contents are ultimately freed when asset_cache_destroy
is called. In other words, asset_cache.c
has ownership over all buttons. Thus, buttons
should not be manually freed in game.c
or anywhere else.
Consequently, we’ll add a case for the ASSET_BUTTON
type in asset_cache_free_entry
. Remember that you should
only be freeing what obj
points to.
Finally, implement asset_cache_handle_buttons
. The documentation in asset_cache.h
tells you what to do. The function is simple to implement but very powerful: calling it activates all button handlers that need to be called. Don’t worry about how asset_on_button_click
is implemented for now; we’ll come to that later.
Task 0.
Implement the asset_cache_register_button
and asset_cache_handle_buttons
functions and modify
asset_cache_free_entry
in asset_cache.c
.
sdl_wrapper
Now, we’ll add asset_cache_handle_buttons
in sdl_wrapper
. More specifically, we’ll add it in sdl_is_done
, which is where we detect if the mouse has been clicked. Every time it does, we want to run asset_cache_handle_buttons
to run any button handlers appropriately.
After you’ve done so, take a look at emscripten.c
and look at the game loop. Pay special attention to the fact that we run sdl_is_done
after emscripten_main
runs. This will be very important in the next step.
Task 1.
Call the asset_cache_handle_buttons
function in sdl_wrapper.c
.
asset_t
Like images and text, we’ve defined a button_asset_t
struct in asset.c
using the C inheritance pattern. Here’s a quick breakdown of some new fields:
-
image_asset
andtext_asset
- Buttons will have the option of rendering images and text. Note how we’re using the encapsulatedimage_asset_t
andtext_asset_t
structs-that significantly simplifies ourbutton_asset_t
type. -
handler
- This is the function that should run whenever the button is clicked. It has abutton_handler_t
type that we’ve defined for you inasset.h
. Currently, it only takes in the game state, but feel free to add any parameters as you see fit later on.
We’ll come back to is_rendered
later.
First, copy and paste your implementations of asset_make_image
, asset_make_text
, and asset_render
from last week.
Note: You may be wondering if we have to change asset_destroy
for buttons to free the image and text assets that
they point to. This is a design decision: We’ve decided to code our library so that the user who
initializes the button asset has ownership over the image and text assets that it points to. The reason
for this is because it’s common for buttons to share the same image (like the “Next” and “Back” buttons in the game),
and it’s
nice to be able to reuse the same image asset rather than creating duplicates for every button. In this case,
freeing the image and text assets for every button would lead to double-free errors.
Your first task is to implement asset_make_button
. Make sure to use the asset_init
function that we’ve defined for you to reduce code duplication. It should be pretty similar to the two functions above it.
Next, implement the ASSET_BUTTON
case in asset_render
. Here are a few things to keep in mind:
- Be careful about code duplication here. Think about what fields we included in
button_asset_t
. - Order matters for rendering the image and text!
Finally, we’ll implement the asset_on_button_click
function. First, take a look at the documentation for it in asset.h
. Here is a breakdown of the general steps you should take:
- Check if the location of the mouse is within the bounding box of the button (We recommend writing a helper function).
- If we’ve reached this step, we can run the button handler.
Task 2.
Implement the asset_make_button
, asset_render
, and asset_on_button_click
functions in asset.c
.
Like before, take a look at the documentation in asset.h
to understand what each function should do.
This implementation works great most of the time, but now we have a problem. Let’s say we intialized a game with a start button in the middle of the page. Once it’s pressed, the game starts, and the user is free to use their mouse/keyboard. What happens if they clicked in the middle of the page again? Based on what we wrote here, the start button handler would be activated, which is definitely not what we want to do.
How can we can prevent this problem? We know we only want to activate a button handler when it’s clicked
and it’s rendered onto the screen. That’s where the is_rendered
field comes in.
Here’s the general logic for how we’ll use it: every time the button is rendered, we update the field
appropriately. Then, after every game loop, we reset is_rendered
for every button.
The first fix is easy: we can modify asset_render
so that is_rendered
is updated appropriately.
For the second fix, we’ll have to modify the asset_on_button_click
, which checks if the button handler should be run.
We’ll add the following steps all above the code that we wrote previously.
- Check if
button
is rendered. If it’s not, we canreturn
. - Reset the
is_rendered
field. Why reset it here? Look at whereasset_on_button_click
is being called, and compare it to whereasset_render
is called. How do we know thatasset_on_button_click
will always run after all calls ofasset_render
? (Hint: it relates to the earlier section about the game loop!)
Task 3.
Modify the asset_render
and asset_on_button_click
functions in asset.c
.
Game
Now we can put everything together! We’ve given you starter code for the meme generator project in game.c
. We’ve set up all of the button handlers for you, along with the button information using a button_info_t
struct. You will have to copy and paste your team’s implementation of generate_memes
from last week.
The only thing you will need to implement is the create_button_from_info
function. Before you do, take a look at the rest of the file to get a general sense of how the function is being used. Trust us; it will make a LOT more sense if you do this before implementing create_button_from_info
.
Finally, here are some notes about this game.c
file that may be helpful:
- Note that we’re not actually free’ing any of the buttons in
game.c
. Since they’re being registered and stored in the asset cache, they will all be freed whenasset_cache_destroy
is run. -
emscripten_main
returns a bool now. This is more relevant for thedemo
student, but it essentially signifies if the game has ended. - Buttons don’t have to include both an image and text.
Task 3.
Implement the create_button_from_info
function in game.c
to complete the meme generator.
Open-ended Questions
Task 4. When you are done, your group should meet and complete the questions here.