Saint Louis University |
Computer Science 1050
|
Dept. of Math & Computer Science |
Read this entire document carefully. There are a lot of details to discuss, and we will use this to provide an explanation of the project, advice for how to make progress, and a summary of what is required and what is optional, together with our grading rubric. I recommend that you read through the entire document before beginning, and then refer back to it throughout your work on the project.
This assignment is more technical in nature. Therefore, we are going to ask you to work with one other student in developing your software, and we will devote some class time to support the partnerships. We will allow students to form their own partnerships; please let the instructor know if you wish to have help in finding an unpaired partner.
It is vital that both students contribute to the development of the project. Please make sure you adhere to the policies on academic integrity in this regard.
Our inspiration for this project is the 1980s arcade game Missile Command, originally created by the Atari company. You may read about the history of the game on the Wikipedia article. To get a feel for the game, we strongly suggest you spend some time (but not too much!) playing this online version of the original game.
The basic flow of the game has a cold-war era theme with randomly generated balistic missiles raining down from the sky which will explode if they reach the ground. The player controls the firing of counter-missiles that can explode in the air to destroy the incoming missiles before they reach the ground.
Although we will not be recreating a complete version of the original game in this project, we hope to implement many of the core aspects of the game. This will require significant effort and the key to success will being able to manage the complexity with incremental software development. That is, it is difficult to imagine doing the entire project if you focus on the entire project at once. Instead, we will want you to focus on small, incremental goals to build up more and more functionality as you go. We will also help guide you through this process with the rest of this document.
Although we have focussed greatly on graphics in Processing this semester, the graphics is actually the easiest part of the project. The real challenge is to properly manage the state of a game to produce. To help separate the programming tasks, we will follow a relatively common software design approach known as MVC, which stands for model-view-controller (if interested in reading further details, see wikipedia). We separate the software responsibilities as follows:
Model Although there is a graphical part of the program, at its core what really matters is an internal simulation of the process. That is, at any moment in time, there are some number of active missiles and active counter-missiles, and each such object has its own set of parameters that define its current state. So we begin our design by ignoring the actual graphics and focussing on what information we need to keep track of in order to model the internal simulation. We will associate with each call of Processing's draw() function a single unit of time in our simulation. With each advancement of time, we want to update the locations of existing missiles, consider introducing new missiles, and test for collisions.
View If we keep an accurate model of the internal simulation, then creating a graphical view of the model should be relatively straightforward. For example, we will consider for a missile that started at one position and which currently is at another position, how that should be represented graphically. We can consider how incoming missiles look different than the counter-missiles fired by the player.
Controller Finally, we need to consider how the user will interact with the software, and how that interaction should be represented in our model. For example, when the user presses the mouse button in missile command, this should introduce a new counter-missile into our model. Note that we don't directly worry about how that gets drawn, we simply reflect the change in our model (with the updated view of that model generated separately).
To help in the design of our program, we will rely on a technique known as object-oriented programming, which is supported by Processing. In particular, you are to define a Missile class that defines how a single missile is modeled and rendered. We will later see that if you can successfully define the behavior for one missile, it will become relatively easy to structure the program to include many simultaneous missiles and counter-missiles in our model.
As a first goal, consider the following simulation of a single missile falling from the sky. (This demonstration can be restarted by pressing the 'r' key while the canvas has the focus.)
Before you tackle the full missile command game, we want you to successfully implement such a simple simulation of a single missile. The structure of the above program is implemented as follows, relying on a Missile class that you must create.
float ground = 650; Missile m; void setup() { size(700,700); m = new Missile(); } void draw() { background(0); stroke(255,255,0); strokeWeight(1); line(0,ground,width,ground); m.advance(); m.render(); }
m = new Missile();
This is an invocation of the constructor of the class. It is
responsible for initializing the internal representation of a new missile.
m.advance();
This is called once for each simulated unit of time during the
game. The purpose of this call is to update the internal model
of the missile to reflect the passage of that time unit.
m.render();
This is called once for each frame of the animation, and its
responsibility is to render the missile based on hits current
model. In our first demo, the rendering of the missile includes
the entire red line from its starting location to its current
location, and a bright white dot that highlights the current
location.
When using object-oriented programming, all of the information about a single missle will be stored as instance variables defined within the Missile class. For example, to keep track of the starting location, you might either choose to use two distinct numeric values, such as startX and startY, or if you wish to use more abstraction, you can use Processing's PVector class, declaring a single PVector as
PVector start; // the starting location of this missileWith that definition, you would refer separately to the fields start.x and start.y for the individual coordinates. To track the motion of the missile, we recommend the following variables:
PVector start; // the starting location of this missile PVector target; // the location of the missile's target int totalSteps; // total number of time steps needed from start to target int steps; // number of time steps since the startWith this internal model, the role of advancing a single time step is quite easy: we just increment the steps variable. At any point in time, to determine the current coordinates of the missile, Processing's map() function can be used to separately interpolate the x- and y-coordinates, based on the numbers of time steps out of the total number of steps needed when going from start to target. As we noted before, with this proper modeling, the rendering of the actual missile is trivial, as its simply a red line from start to current locaiton, and a white point at the current location.
To manage the complexity of the program, we strongly encourage you to do incremental development of the project, getting one piece of functionality working before moving on to additional features. For example, you should not move on to attempt additional aspects of the program until you have been able to implement a Missile class to support the simple simulation given above with a single missile. Once that is done, we recommend the following progression of achievements.
Explosions
In our first version, although we plotted the line that
represents the ground, a missle continues to moving forward when
reaching the ground. However, for the real game, it is important that
when a missile reaches its target, we model an
explosion. Continuing with our MVC framework, we want to make
sure to think carefully about the underlying model for an
explosion, and what variables we will need to introduce in the
Missile class to be able to represent the state of the
explosion. The model that we want is that when a missile reaches
its target, it stop moving forward and instead turns into a
fireball with a radius that slowly grows and then shrinks. In
our implementation, we choose to have the explosion expand from
radius 0 to 60 pixels and then back from 60 to 0, changing one
pixel per time step.
To represent the state of the missile, you will in some way need to add additional variables so that you can properly differentiate between the four phases a missle may go through: motion, growing explosion, shrinking explosion, inactive. Note that we will need to have a clear way to designate missiles as inactive after an explosion completes so that we can remove them from our overall model. For the above animation, we rely on a presumed boolean named active as a field of the missile so that we can create a new missile after the old one is done. We do so by adding the following additional lines at the end of the top-level draw() function:
if (!m.active) { m = new Missile(); }
Counter Missiles
As our next goal, we wish to add functionality that would allow the player of the game to fire a counter missile. In terms of our implementation, it might be tempting to think of a counter missile as an entirely different type of object. However, we can greatly simplify our efforts by realizing that counter missiles can be viewed simply as another type of missile. It differs from the standard missile in the following ways:
Here is a demonstration of the next checkpoint to reach, which allows the user to fire a single missle with a mouse press. In this demonstration, please note that the counter missiles do not destroy the incoming missile (we'll add that functionality later).
To achieve this level of functionality, we suggest you modify the Missile class to accept an additional boolean named counter that identifies whether a missile is a counter missile (or standard inbound missile). Then, modify the rest of the program to support an additional missile variable that represents a possible counter missile, and use the mousePressed() function to create a new counter missile based on the user's action.
As an aside, the blue crosshairs in our example above are not associated with a missile; we simply draw that at the current mouse location in each frame. Also, if you would like to turn off the rendering of the default cursor when mousing over the canvas, Processing supports a noCursor() command (although this feature doesn't seem to work perfectly when embedding our demo within a web browser).
Note well: in the above demonstration, we only support one counter missile at a time, and if the user presses the mouse while a previous counter missile is still active, the new missile replaces the previous one.
Collision Detection
In our previous demonstration, the counter missiles did not actual destroy any incoming missiles. Our next goal is to add such functionality. The desired rule is that an incoming missile is destroyed if the tip of that incoming missile comes in contact with an active explosion of a counter missile.
Since at any moment in time an explosion, if active, is just a circle, it is relatively straightforward to see if the current missile location falls within that circle. To implement this, we recommend the following design:
Within the Missile class, consider implementing a method with signature such as
boolean impacts(Missile counter)that returns true if an inbound missile's current position is within the ball of an exploding counter missile and false otherwise
From within the the main draw() function, immediately after updating the positions of the two missiles, test if there is an impact and if so, set the incoming missile to be inactive so that it is no longer rendered.
Multiple Missiles
Thus far, we have supported only a single incoming missle and a
single counter missile. The next big challenge is to adapt our
framework to support a large number of simultaneous missiles. We
will suggest the same technique to support multiple incoming and
multiple counter missiles, but to begin will focus just on the
incoming missiles.
Ideally, the Missile class defines how each individual missile behaves, and there will be no need to change that class definition. Instead, we need to do additional bookkeeping within the main flow of the program. Most notably, we wish to maintain a collection of missiles. Although there are more advanced data structures we might consider, we recommend using an array of missiles. For the sake of simplicity, we might safely assume that there will never be more than 1000 simultaneously active missiles, and thus declare an array that is capable of storing up to 1000 missiles.
However, we do not want to make the assumption that there won't
be more than 1000 missiles over the entirety of a game. Instead,
we wish to keep a count m of the number of
active missiles, and then to ensure that those active
missiles are stored at indices 0 through
Once we support many simultaneous missiles, we will need to use loops whenever we wish to process the missiles. For example, we will use a loop to call advance() on each missile, and a loop to call render() on each missile.
Finally, for the game play we must decide how often to introduce new incoming missiles. To provide some variation, we recommend relying again upon a random number generator to control the release of new missiles. For each unit of time, we consider releasing one new missile, with probability p for some p. We find a release probability of 0.01 to be decent, although this could be increased or decreased to adjust the difficulty level of the game.
For the counter missiles, we do not want any randomness in their release. But we do wish to allow the user to fire more rapidly, resulting in several active counter-missiles. We recommend a similar technique, with an array of counter missiles (not to be confused with the separate array of incoming missiles).
Here is a demonstration of a more complete version of our game. It includes all of the functionality previously described, as well as some extra "bells and whistles" beyond the minimum requirements.
Beyond the minimum requirements, there should be ample opportunity to have fun with this project and expand the functionality and usability of the software. Possibilities include (but are not limited to):
Allow player to use up/down arrows to adjust the spawn rate of incoming missiles (and thus the inherent level of difficulty)
In the official game the "x" that markes the target location for a fired counter missile rapidly blinks until the time at which the counter missile reaches that spot. Implement that effect
Track and display the player's "hit rate" which we define as the fraction of incoming missiles that are destroyed before reaching the ground.
Track and display the player's "efficiency" which we define as the average number of incoming missiles that are destroyed per counter missile.
Our demonstration has an unadorned line to represent the ground, but the original game has more decoration with cities and batteries. Add to the artistic nature of the game.
In our version of the game, all counter missiles are fired from the same location on the ground. In the original game, there are three different firing locations and by default, when a counter missile is fired, it comes from that launching pad which is physically closest to the target. Implement such behavior.
Our demonstrated version of the game goes on indefinitely. The original game includes six cities that are each destroyed if struck by a missile. The game ends if all six cities are destroyed. Introduce such a feature.
The original version of the game progresses in rounds, presumably with some fixed number of incoming missiles that occur within a round, before there is a pause and the beginning of a new (harder?) round. Implement such a framework.
Our demonstration allows for an unlimited number of counter missiles. The original game has a finite number of such missiles (per city, per round). Implement such a mechanism.
In the original game, some of the incoming missiles will occassionaly split into multiple trajectories. Implement such a feature.
For this project, only one member of a partnership needs to submit the work, but both members of the team should be clearly identified within the header of the source code and the readme file. We ask that you submit the following three files:
The .pde file that contains the actual Processing source code you've written
A separate 'readme' text file, as outlined in the general webpage on programming projects. Of particular note for this project, your readme file must include:
Names of both members of your partnership.
A brief summary of how each partner contributed to the work.
An explicit bulleted list that explains what functionality you have included within your implementation. You should clearly note any required aspects that were not completed, as well as any optional bells and whistles that have been completed.
Please see details regarding the submission process from the general programming web page, as well as a discussion of the late policy.
The assignment is worth 70 points, which will be assessed as follows:
We will consider the possibility of some extra credit for additional Bells and Whistles.