CS 3 (Spring 2024) Project 05: Collision Detection (Game)

In this project, you will add buttons to your asset library.

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

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.

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!

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.

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.

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:

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:

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:

  1. Check if the location of the mouse is within the bounding box of the button (We recommend writing a helper function).
  2. If we’ve reached this step, we can run the button handler.

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.

  1. Check if button is rendered. If it’s not, we can return.
  2. Reset the is_rendered field. Why reset it here? Look at where asset_on_button_click is being called, and compare it to where asset_render is called. How do we know that asset_on_button_click will always run after all calls of asset_render? (Hint: it relates to the earlier section about the game loop!)

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:

Open-ended Questions