
Due date: Wednesday, January 21st, 11:59pm
There are two main goals to this project:
To get used to making a game with
To understand object marshalling. For this project, that will be
done by adding save/load game functionality to the game using object
serialization. You will use
Overview | Details | Fruit Ninja | Save/Load | Sample Code | Tips | Submission | Grading
The first part of the assignment is to:
Visit the Dragonfly Web Page and briefly familiarize yourself with the contents.
Download the Dragonfly game engine and setup your development environment for the platform of your choice (Windows, Mac, Linux).
Important! For compiling programs with this version of the engine, you need the preprocessor directive
NO_NETWORKdefined. For Visual Studio, addNO_NETWORKunder the pre-processor definitions. These are under Menu → Project → Properties → C/C++ → Preprocessor. To add, select Preprocessor Definitions (the dropdown arrow), and click Edit. For a Makefile (Mac/Linux), edit have-DNO_NETWORKon the compilation line.
Complete the Fruit Ninja tutorial. The tutorial has all sprites and sounds needed for development as well as a description and working code for the completion of each step, but should still be done to ensure you understand Dragonfly and can complete the next "Extend" step.
Extend the
Note: You can copy and paste code snippets from the tutorial Web pages and solutions themselves. However, you cannot do the same (i.e., no copy and paste) from other Web sites. Similarly, you can consult with peers in the class for help and even look at each others' code, but as guideline, emailing code or copying and pasting code is not allowed from anywhere.
The second part of the assignment is to add the capability for the player to save the state of the game to a file (using Object serialization) and restore it by loading from a previously save state (using Object deserialization). Saving a game's state needs is done by the player pressing the "S" key and loading a game's state from a previously saved file is done by pressing the "L" key.
Here, saving a game's state preserves the exact state, down to the Object's position's and velocities for the Fruits and Sword and the values and wave status for the Timer, Points and Grocer. Basically, everything except for the Explosion and any particles (e.g., Faders). Consequently, when a game state is restored, the game world should go back to the exact position it was in when it was saved.
You should make a State class that is interested in
keyboard input. When the right key is pressed ("S" or "L"), it invokes
one of two methods:
In general, the save method serializes all Objects and writes them to
a file. Dragonfly has a serialize() method for all built-in Objects, so
any derived class only needs to serialize the base class (e.g., callin
gObject::serialize()) and then serializing any local
attributes. The serialization itself is done to a stream (see sample
code below).
Steps to do so:
First, record a count of the total number of Objects to stream.
Then, loop - go through all Objects and serialize each to stream. Except for the State itself and game engine particles (e.g., Fader objects).
Once the stream is prepared:
Delete all Objects (using markForDelete()). Except
the State itself, of course! Hint: if you move each Fruit off
the screen before deleting, it won't make an explosion.
Read stream from file.
Extract count from stream.
Then, loop for count:
new) appropriate Object.You do not need to save/load any Particles (e.g., the Fader Particles
that happen for exploding Fruit), Kudos objects or sound effects (i.e.,
if a sound is being played while s is being pressed, the
sound doesn't need to be saved). You also do not need to save/load the
splash screen (neither the
Overview | Details | Fruit Ninja | Save/Load | Sample Code | Tips | Submission | Grading
While there are many ways to write code to serialize an Object, the
recommendation is to use the STL stringstream - a versatile
container - since it provides: 1. buffer management - handles memory
allocation and resizing automatically; 2. flexibility - by treating data
as binary within std::stringstream, it handles a
wide range of data types; 3. portability - available on all compliant
platforms; and 4. error handling - provides mechanisms to check for
errors. Also, the std::stringstream for Object serialization, so your code
can build upon this.
Below is an example:
//
// Example using std::stringstream to serialize.
//
#include <iostream>
#include <fstream>
#include <sstream>
// Example class to serialize.
class Object {
public:
int i;
float f;
char c;
// Constructors.
Object(int i, float f, char c) : i(i), f(f), c(c) {}
Object() {
i = 0;
f = 0.0f;
c = '0';
}
// Serialize function.
void serialize(std::stringstream *p_ss) {
int num_attributes = 3;
p_ss -> write(reinterpret_cast<char*>(&num_attributes),
sizeof(num_attributes));
p_ss -> write(reinterpret_cast<char*>(&i), sizeof(i));
p_ss -> write(reinterpret_cast<char*>(&f), sizeof(f));
p_ss -> write(reinterpret_cast<char*>(&c), sizeof(c));
std::cout << "Object::serialize(): attributes serialized: " <<
num_attributes << std::endl;
}
// Deserialize function.
void deserialize(std::stringstream *p_ss) {
int num_attributes;
p_ss -> read(reinterpret_cast<char*>(&num_attributes),
sizeof(num_attributes));
std::cout << "Object::deserialize(): attributes to deserialize: " <<
num_attributes << std::endl;
p_ss -> read(reinterpret_cast<char*>(&i), sizeof(i));
p_ss -> read(reinterpret_cast<char*>(&f), sizeof(f));
p_ss -> read(reinterpret_cast<char*>(&c), sizeof(c));
}
// Display the object values.
void print() {
std::cout << "i: " << i;
std::cout << " f: " << f;
std::cout << " c: " << c;
std::cout << std::endl;
}
};
int main() {
// Create an Object.
std::cout << "A original: " << std::endl;
Object A(20, 2.99f, 'Z');
A.print();
// Create stringstream to hold serialized data.
std::stringstream ss;
// Serialize the Object.
A.serialize(&ss);
if (ss.good())
std::cout << "A serialized to memory." << std::endl;
else
return -1; // Error.
// Deserialize to new Object.
Object B;
std::cout << "B original:" << std::endl;
B.print();
B.deserialize(&ss);
if (ss.good())
std::cout << "B deserialized from A's data:" << std::endl;
else
return -2; // Error.
B.print();
return 0;
}There are many ways to do file I/O (reading and writing to a file) in
C++, so below is only one sample. Note, a bit of care needs to be taken
when handling std::stringstream since it needs to be
treated as binary data and not a normal text-based string.
//
// Example of write/read from file.
//
// Version 1.0
//
#include <fstream>
#include <iostream>
#include <sstream>
const std::string FILE_NAME = "objects.df";
int main() {
///////////////////////////////////////////
// CREATE STREAM
std::stringstream ss;
// Add number to stream.
int num_out = 4;
ss.write(reinterpret_cast<char*>(&num_out), sizeof(num_out));
std::cout << "Number written is: " << num_out << std::endl;
///////////////////////////////////////////
// WRITE TO FILE
// Open file for writing.
std::ofstream outFile(FILE_NAME, std::ios::binary);
if (!outFile.is_open()) {
std::cout << "main(): Error! Can't open file for writing." << std::endl;
return -1;
}
// Write stream.
std::stringbuf* pbuf = ss.rdbuf(); // Get pointer to stringbuf.
std::string str = pbuf -> str();
outFile.write(str.c_str(), str.size());
// Close file.
outFile.close();
///////////////////////////////////////////
// READ FROM FILE
// Open file for reading.
std::ifstream inFile(FILE_NAME, std::ios::binary);
if (!inFile.is_open()) {
std::cout << "main(): Error! Can't open file for reading." << std::endl;
return 1;
}
// Read entire file into stream.
ss << inFile.rdbuf();
if (!inFile) {
std::cout << "main(): Error! During reading." << std::endl;
return 1;
}
// Close file.
inFile.close();
// Extract number from stream.
int num_in;
ss.read(reinterpret_cast<char*>(&num_in), sizeof(num_in));
std::cout << "Number read is: " << num_in << std::endl;
// All is well.
return 0;
}Overview | Details | Fruit Ninja | Save/Load | Sample Code | Tips | Submission | Grading
The serialize() method for
all built-in objects (e.g., Object and
ViewObject). This base method serializes all attributes to
an std::stringstream memory stream. The corresponding
deserialize() sets attributes from a previously-serialized
stream. For this project, these built-in methods can be used, with the
derived classes only needing to serialize() and
deserialize() derived attributes. By default,
serialize() only puts modified (since the last call)
attributes into the stream. If you want to force all attributes to be
serialized, you can set the mask to all "1"s. Below is an illustrative
code fragment:
#include <climits> // for UINT_MAX
...
std::stringstream ss;
df::Object *p_o;
...
unsigned int mask = UINT_MAX; // all bits set
p_o -> serialize(&ss, mask));Serialize one Object at a time. Build a test case with just one Object in the world, say a single piece of fruit, and not the full game. Test that saving and loading just that one Object works. Repeat for each type of Object. Then, make tests with multiple Objects. Continue in this way until you get to the full game.
If you add new Sprites to RM.loadSprite() is called). In this case, dragonfly.log and tries to identify
the offending line number.
Use Sword's keyboard input as a reference. While State class).
Use debugging tools. You should have two main toolsets at the ready
for this project (and most coding projects): debugging via (print)
output and debugging via a debugger. For the latter, you should make use
of Visual Studio (if using windows) or gdb if using Linux
or Mac. For debugging using print output, the LogManager is your friend.
Get comfortable with C-style printf() commands and place
ubiquitous messages in your code to help with debugging.
Overview | Details | Fruit Ninja | Save/Load | Sample Code | Tips | Submission | Grading
Your assignment is to be submitted electronically (via Canvas) on the time and day due. You must hand in the following:
Source code:
.h files.A README.md file listing:
2a. PLATFORM (Windows, Mac or Linux), and how to compile (if anything special is needed), how to run (ditto), and anything else needed to understand (and grade) your game.
2b. GAME information providing a short description of the additional
10% extension you made to the
A VIDEO. You will create a video showing your project for the grader (i.e., this is not a trailer - this is information for grading). You'll narrate the video (your voice) as you record.
The video does not need to be polished. Think a bit about you want to say, then record. It should be no more than 10 minutes long, maybe even as short as 5 minutes.
There are a variety of recording tools for creating the video and you can use whatever tool you are most comfortable with. An easy one is to use Zoom and press record.
Before submitting, "clean" your project:
in Visual Studio:
Build -> Clean solutionvs-2022/.vs directoryvs-2022/x64 directoryin Linux/Mac: make clean
This will remove all the temporary (compiled) files. Failure to do may mean you file is too big to submit!
You do not need to submit any Dragonfly or SFML files.
Use zip to archive your files.
To submit your assignment (say, lastname-proj1.zip) via
Canvas:
Open: Assignment - Project 1 Click:
Upload
Click:Drag your zip file,
Select:lastname-proj1.zip
Click:Submit Assignment
When successfully submitted, you should see a message similar to:
SUBMITTED on January 21, 2026 3:53pm
Important - you must click the
Submit Assignment button at the end or your file will not
be submitted!
Overview | Details | Fruit Ninja | Save/Load | Sample Code | Tips | Submission | Grading
Tutorial - 20% : Doing the tutorial without any additional customization is worth just under 1/2 the grade. While you will have learned a substantial amount about the Dragonfly game engine, you will not have demonstrated an in-depth understanding of the code base nor ability to develop and debug in C++.
Customization - 35% : Extending or modifying the tutorial game with custom work is worth about a third of the grade. Doing so will show technical expertise and mastery of the basic concepts of using the Dragonfly game engine. This is essential in moving forward with subsequent class projects.
Save/Load - 35% : Support for saving and loading the game is also worth about 1/3 the grade. Doing this will also show technical C++ expertise but also specific knowledge and skill for marshalling C++ Objects. This is critical for supporting multi-player network features in subsequent class projects.
Documentation - 10% : Not to be overlooked is including the documentation provided, as well as having that documentation be clear, readable and pertinent to the assignment. This primarily includes the README described above but also any needed support document. Having well-structured and commented code is part of Documentation, too. Getting in the habit of good documentation is important for large software projects, especially when done in teams.
100-90. The submission clearly exceeds requirements. The tutorial game works without problems. The custom extensions exhibit an unusually high degree of effort, thoughtfulness, technical ability and insight. Game save/load works flawlessly for all game objects. Documentation is thorough and clear and code is well-structured, commented and readable.
89-80. The submission meets requirements. The tutorial game works without problems. The custom extensions exhibit substantial effort, thoughtfulness, technical ability and/or insight. Game save/load works well. Documentation is adequate and code is mostly well-structured, commented and clear.
79-70. The submission barely meets requirements. The tutorial game works without problems. The custom extensions exhibit marginal effort, thoughtfulness, creativity and/or insight. Game save/load works somewhat, but not completely for all objects and/or bug-free. Documentation is missing details needed to understand the contributions and/or to build the programs and code may be uncommented and/or unclear in parts.
69-60. The project fails to meet requirements in some places. The tutorial game may crash occasionally. The custom extensions are of minor scope, or exhibit perfunctory effort, thoughtfulness, technical ability and/or insight. Game save/load is not fully implemented and is not robust (i.e., has bugs). Documentation is inadequate, missing key details needed to understand the contributions and/or to build the programs and code is uncommented and unclear in large parts.
59-0. The project does not meet many of the requirements. The tutorial game crashes consistently. The custom extensions exhibit little or no evidence of effort, thoughtfulness, technical ability and/or insight or are missing. The save/load features do not work or are missing. Documentation is woefully inadequate or missing and code is difficult to read and understand.
Overview | Details | Fruit Ninja | Save/Load | Sample Code | Tips | Submission | Grading