Setting the Scene
I know that the code's just using concepts we've seen, but I don't much like the “reference to an array” syntax argument to
Asciimation::play. It seems a bit clunky.
And I wish I had MORE control over how the sprites move in the scene, like BOUNCING off the walls and such!
Let's make things better!
In this part, you're going to create a new class called Scene that will manage multiple sprites and their movements. This addition will allow you to have more complex interactions in your animation, such as bouncing off walls or even responding to user input.
The Vision
We're going to change the Asciimation::play function to take a Scene object instead of an array of Sprite objects. The Scene class will hold an array of sprites and manage their movements and interactions. The play function will now look like
void Asciimation::play(Scene& scene) {
// Time to sleep between screen refreshes
const int PAUSE_TIME_IN_MICROSECONDS = 40000;
Display display{MOVIE_WIDTH, MOVIE_HEIGHT};
// Play the movie by refreshing the screen with new data, every so often
int key = 0;
bool finished = false;
int numSprites = scene.getNumSprites();
while (!finished) {
clearFrame();
// Render all sprites
for (int i = 0; i < numSprites; ++i) {
renderSprite(scene.getSprite(i));
}
// Show the frame on the screen
copyToScreen(display);
display.refresh();
// Sleep between updates
usleep(PAUSE_TIME_IN_MICROSECONDS);
// Check to see if the user has entered 'q' to quit
key = display.checkForKeypress();
if (key == 'q') {
finished = true;
}
// Update the scene for the next frame
scene.advance(key, screenBounds_);
}
}
Here's what's changed:
- In the
forloop, we now callscene.getSprite(i)to get each sprite to render. We also don't move the sprites here anymore.Scene::getSpritereturns aconst Sprite&, which means we can't modify the sprite through this reference. This restriction is intentional, as theSceneclass will be responsible for updating the sprites' positions. Using a reference also saves needless copying of theSpriteobjects, which is especially important because copyingSpriteobjects is disallowed (the copy constructor is disabled).
- At the end of the loop, we call
scene.advance(key, screenBounds_)to update the scene for the next frame. This function is where the sprites will be moved and any interactions will be handled. We pass in any key that was pressed, in case we want the scene to be affected by user input.
Likewise, the our-movie.cpp file will change to create a Scene object and pass it to play:
void makeOurMovie() {
Scene theScene;
Asciimation ourMovie;
ourMovie.play(theScene);
}
Whoa! That code for
makeOurMovieis way simpler now! It's like a thing of beauty.
I love the way it doesn't need any comments because it's so obvious what it's doing. Less code FTW!
Your Task
Your task is bring this vision into reality by implementing the Scene class and modifying the Asciimation class and our-movie.cpp file as described above.
Unlike previous parts where we've often spelled out exactly what to do, this time you'll need to plan it out with your partner. To make our autograder happy, you are required to implement the Scene class in scene.hpp and scene.cpp files, and modify asciimation.hpp, asciimation.cpp, and our-movie.cpp as described above.
There's one exception: We know from experience that students struggle with member-initialization lists, and initializing an array in one is a bit outside what you've seen so far, so here's the code for a constructor that mirrors the existing behavior of the program:
Scene::Scene()
: sprites_{
{"mystery1.tsprite", 30, 5} // mystery image at column 30, row 5
} {
// It's moving to the right...
sprites_[0].setVelocity(1, 0);
}
This constructor initializes the sprites_ array with one sprite, just like the code we began with, and sets the sprite's velocity to move to the right. You can modify this constructor to add more sprites or change their initial positions and velocities as you see fit.
Couldn't I just initialize the array in the body of the constructor?
No. Once we're in the body of the constructor, the compiler will have already default initialized the array. Except that it will have failed because
Spriteobjects are not default constructible. So you must use the member-initialization list to initialize the array.
Actually, we could specify the initialization in the declaration of the array in the class definition (in the header). Then it would work exactly the same is it does in
our-movie.cppnow.
Yes, that would work, too.
Your goal at this stage is to add the Scene class, but otherwise have your code behave exactly as it did before. Once you've done that, you can move on to the next part where you can get creative with your scene!
We often give “Helpful Hints” at this point, but a goal for this part is to have you be a little more self sufficient.
Remember, though: You can always talk to your professor or grutors if you get stuck!
(When logged in, completion status appears here.)