Due date: Wednesday, September 4th, 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).
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 and restore it by loading from a previously save state. 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 count of Objects to stream.
Then, loop - go through all Objects and serialize each to stream. Except 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 Fruit off screen before deleting them, they 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, that 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.
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.
int i, float f, char c) : i(i), f(f), c(c) {}
Object(
Object() {0;
i = 0.0f;
f = '0';
c =
}
// Serialize function.
void serialize(std::stringstream *p_ss) {
int num_attributes = 3;
reinterpret_cast<char*>(&num_attributes),
p_ss -> write(sizeof(num_attributes));
reinterpret_cast<char*>(&i), sizeof(i));
p_ss -> write(reinterpret_cast<char*>(&f), sizeof(f));
p_ss -> write(reinterpret_cast<char*>(&c), sizeof(c));
p_ss -> write(std::cout << "Object::serialize(): attributes serialized: " <<
std::endl;
num_attributes <<
}
// Deserialize function.
void deserialize(std::stringstream *p_ss) {
int num_attributes;
reinterpret_cast<char*>(&num_attributes),
p_ss -> read(sizeof(num_attributes));
std::cout << "Object::deserialize(): attributes to deserialize: " <<
std::endl;
num_attributes << reinterpret_cast<char*>(&i), sizeof(i));
p_ss -> read(reinterpret_cast<char*>(&f), sizeof(f));
p_ss -> read(reinterpret_cast<char*>(&c), sizeof(c));
p_ss -> read(
}
// 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;
20, 2.99f, 'Z');
Object A(
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 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;
reinterpret_cast<char*>(&num_out), sizeof(num_out));
ss.write(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;
reinterpret_cast<char*>(&num_in), sizeof(num_in));
ss.read(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
If you add new Sprites to RM.loadSprite()
is called). In this case, dragonfly.log
and tries to identify the offending line number.
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, combine objects until you get to the full game.
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 ubiquitous messages to help with debugging.
Use Sword's keyboard input as a reference. While State
class).
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 showing: A) your code compiling, B) your game extension as it is played, C) your code to support your game extension, D) your save/load working in-game, and E) your code to support safe/load. The video should be 10 minutes or less. It does not need to be polished, just complete. Recording via Zoom can work well. The video can be included in the submission or hosted online and with a link to follow.
Before submitting, "clean" your project:
in Visual Studio:
Build
-> Clean solution
vs-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 the Dragonfly library, header files, nor the 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 September 04, 2024 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