Basic Actors and Drawing
In this first assignment, we are going to setup a few classes to support or “game object model” which we will talk more about in Week 2. We’ll use the term actor to reference any object in the game world. For example, in Pong, the ball, paddle, and walls will all be “actors.”
Transform
The Transform
class is a container for information about where actors are in the world, their size, and so on.
In Transform.h, declare a class called Transform
. In the private data, it will need two member variables that are both type Vector2
:
- The position, which represents the center position of the actor
- The size, which represents the width (the x component) and the height (the y component) of the actor
Getters/Setters
You should declare public getter and setter functions for the position and size, respectively, and they must have the following signatures:
const Vector2& GetPosition() const;
const Vector2& GetSize() const;
void SetPosition(const Vector2& position);
void SetSize(const Vector2& size);
Notice how we are using const references for Vector2
s to avoid making copies.
Implement these getter and setter functions (you can implement them inline in the declarations if you want to).
GetRect
Next, add the following function declaration:
SDL_FRect GetRect() const;
The implementation of this function needs to create an SDL_FRect
on the stack (i.e. do not allocate it with new
), assign the x
, y
, w
, and h
values based on the position/size members, and return the SDL_FRect
.
Keep in mind that our actor position is the center but SDL_FRect
uses the top left corner for the x
/y
values, so you’ll have to calculate it.
Make sure your code still builds.
Actor
For now, the Actor
will just have a Transform
in its protected member data.
Then, you need two versions of a GetTransform
function in the public section:
// This version allows us to modify the transform
Transform& GetTransform();
// This version is for cases where we don't need to modify
const Transform& GetTransform() const;
The implementations of both functions are the same; they just need to return the transform member variable. But we made one version that allows for modification of the transform, so you can later write code like:
myActor->GetTransform().SetPosition({50.0f, 30.0f});
Make sure your code still builds.
Tracking Actors in Game
The game needs to keep track of all the actors in the game. So, add a std::vector<Actor*>
to the private data in Game
. You’ll need to add the appropriate includes for this.
CreateActor
Whenever we dynamically allocate a new actor, we also need to add it to the Game
’s std::vector
. But it would be annoying to have to manually add it to the vector every time you create one, so we will make a public member function in Game
called CreateActor
:
Actor* CreateActor();
This function needs to:
- Dynamically allocate an
Actor
, saving it in a pointer - Add the actor created in (1) to the
Game
’sstd::vector
of actors - Return the actor pointer
LoadData and UnloadData
Add two private member functions to game: LoadData
and UnloadData
. Both functions take no parameters and return void
.
When you add a declaration of a function in the header file, the quickest way to add a stub implementation of the function is to let CLion create the implementation for you. If you mouse over the declaration, you’ll see that CLion knows you are missing an implementation:Then, right-click (or two finger tap or Ctrl+tap on a Mac), select “Show Context Actions”:
Finally, select the “Generate Definition” option, which will create the definition in the .cpp file:
Using this feature will save you a lot of time!
For now, the implementation of LoadData
can be empty. Call this function in Game::Initialize
, right before you return true.
In UnloadData
, you need to loop over the Game’s std::vector
of actors, calling delete
on each pointer. Then, after the loop, clear()
the vector. This makes sure we aren’t leaking memory! Call this function at the start of Game::Shutdown
.
Creating Your First Actor
Now in LoadData
, call CreateActor
to make an actor for the ball. You want this actor’s position to be in the center of the window and a size that’s 15x15. Keep in mind to change the position or size, you’ll have to chain your call to SetPosition
or SetSize
off of GetTransform
, like GetTransform().SetPosition
To make it easy to later modify the ball, save the Actor*
you get from CreateActor
into a private member variable for the ball.
You won’t see the ball yet, but make sure your code still builds!
Drawing
SDL rendering uses a back buffer (which we’ll cover later in the semester). For now, just know that every frame, the game code must clear the back buffer, draw the scene, and “present” the back buffer.
In GenerateOutput
, do the following:
- Set the render draw color to black
(0, 0, 0, 255)
usingSDL_SetRenderDrawColor
: https://wiki.libsdl.org/SDL3/SDL_SetRenderDrawColor - Clear the rendering target using
SDL_RenderClear
: https://wiki.libsdl.org/SDL3/SDL_RenderClear - Set the render draw color to whatever color you want your Pong actors to be. I chose cyan, which is
(0, 255, 255, 255)
, but feel free to use whichever color you like! - Loop over all the actors in the
Game
’sstd::vector
. For each actor, callGetTransform().GetRect()
, saving it in a local variable, then callSDL_RenderFillRect
: https://wiki.libsdl.org/SDL3/SDL_RenderFillRect - Present using
SDL_RenderPresent
: https://wiki.libsdl.org/SDL3/SDL_RenderPresent
You should now see a ball in the center of the window, like this:If you don’t see the ball in the center, put a breakpoint on the
SDL_RenderFillRect
line. If it doesn’t get hit at all, try to put some more breakpoints to figure out why not? If it does get hit, check the what values your SDL_FRect
has. It should have x=392.5, y=292.5, w=15, h=15. If it doesn’t, put a breakpoint in your GetRect
function to figure out why it’s not giving the expected value. Are your position and size variables correct? Is there something wrong with your calculations in GetRect
?
Adding the Walls
Now create three more actors for the left wall, right wall, and top wall. Their sizes will be different based on the sides, but you want it to be 15 pixels thick (which you should make a constexpr
. You should take advantage of the window width/height constants, as well! You also should save these pointers in member variables in Game
.
You should now have a left, top, and right wall:
Adding a Paddle
Finally, create an actor for the paddle. It should have a width of 100 and a height of 15, and be near the bottom of the screen, horizontally centered. You’ll also want a member variable pointer for this.
You should now have a paddle:
Make sure you push your code. You’re now ready to make things move in Part 3.