assets | ||
css | ||
data | ||
src | ||
.gitignore | ||
index.html | ||
package-lock.json | ||
package.json | ||
README.md | ||
SubmissionNotes.md |
Homework 6 - OOP and Design Patterns
General Description
In this assignment, you will practice with classes, inheritance, and Object-Oriented Programming (OOP) patterns by implementing a simple shooting game. The assignment will also familiarize you with loading and playing sounds, timeouts, reading UML class diagrams, and writing code that interfaces with pre-existing code (the main.js
written by the instructor).
NOTE: There could be unwanted delays in the demo due to the fact that images and sounds are loaded. Please ignore delays (they were not meant, and if you run your code locally, they probably will not be there).
The application consists of a shooting game, with different elements represented by different classes. The image above shows the game and an overlay identifying the different components (i.e., classes) of the game.
The mouse acts like a gun that you can shoot with the left button of the mouse. When you shoot, a bullet hole appears for 2 seconds and then disappears. At every gunshot, the bullet counter (top right) is decremented by one. If you hit a target, the target disappears, and the score is incremented. Once all bullets are used, you can no longer shoot a target, and the trigger produces the sound of an empty gun. By pressing the spacebar, you reset the score and reload the gun.
Getting started with Vite and the code stubs
The files index.html
and main.js
contain a minimal stub to get you started.
To start developing with Vite.js, open the terminal and type once
npm install
npm run dev
The app will be previewed on the local server on port 3000
.
Requirments
- Implements all the classes and methods as specified in the UML diagram below.
- You can modify
main.js
and any other file. - You can also add more classes, functions, or methods to classes, etc. But, if you do so, you have to document them with a UML diagram to be included in your final submission.
Overview
You are required to implement several classes across several files, as seen in the image above. The diagram above is full of details: read it carefully.
Classes
ScoreDisplay
The ScoreDisplay
appears on the screen as a textual score with the Arial font size 25 (top left) and a set of images of bullets (top right) representing the shots left in the gun. Each of the bullets is drawn using the same bullet.png image (all images are in the folder data
).
Bullet
Each Bullet
is drawn using the bulletHole.png
image. It appears only for two seconds when the gun is shot, and then it becomes invisible. You can use setTimeout
to make the image disappear.
Gun
The Gun
is the mouse. To draw it, hide the cursor with the noCursor
function and then draw instead the image cursor.png
at the location of the mouse. To load and play sounds with p5.js take a look at this example. The sound files are shot.mp3
and empty.mp3
, depending on whether there are still shots available. Finally, when you shoot with the gun, add a random CURSOR_SIZE
noise (or a similarly small amount) to alter the bullet's final hitting location.
Target
Target
(base class) and the derived classes TeddyTarget
, DuckTarget
, SquirrelTarget
represents shootable targets. The Target class takes care of everything, except for loading the correct image and returning the number of points associated with each derived target. TeddyTarget gives 1 point, DuckTarget 3 points and SquirrelTarget gives 5 points. Targets return points only if visible (e.g. not previously hit).
TargetFactory
Instead of creating directly the Targets, we use a factory class. Inside there is a static getInstance
method (the factory is a singleton). The other two methods should return an array of targets either randomly or based on specified names. The maximum number of targets is 5 (see the Constants.js
file).
Patterns
Factory and Singleton
TargetFactory
is both a singleton and a factory.
- A singleton requires the class to be unique; hence, it implements the static method
getInstance
, which returns the only available instance of the class. - The factory methods (
getTargetsByName(targetNames, targetWidth, y)
andgetRandomTargets(numTargets, targetWidth, y)
) require specifying the targetWidth, the y location of the target on the screen, and either an array of names (e.g., ['teddy', 'duck', 'squirrel', ...]) or a number of desired random targets.
Observer
A Subject
is an observable object. Observers who subscribe
to a subject are notified when an event happens. For example, both the ScoreDisplay and the targets will be notified anytime the gun shoots. The ScoreDisplay will also be notified whenever a target is hit (Target
is both a Subject and an observer).
To be an Observer, an object must implement the method update
, which takes as input a string with the source of the event and a variable number of parameters grouped with the rest operator.
For example, the gun, when shot, will trigger an event similar to this one:
this.notifySubscribers('gun', x, y, remainingShots);
... and score display will receive and handle the event:
update(source, ...others) {
if (source == 'gun') { // observer of a gun
// ... others contains the data we need
} else if (source == '...') // observer of something else...
//...
}
// ...
}
Constants
Few constants are specified in the Constants.js
file. These are useful for drawing with correct dimensions. Feel free to include and use the constants in this file.
About grading
The maximum score for this assignment is 100 points, divided this way:
- Git: Submit your local repository with your code (the hidden .git folder). There should be at least 10 commits in a branch called
develop
, which is finally merged back tomain
(10%). - Gun shoots bullets, which are correctly displayed (10%).
- Targets are correctly implemented with inheritance and can be shot (20%).
- ScoreDisplay is correctly updated (10%).
- Singleton pattern correctly implemented (10%).
- Factory pattern correctly implemented (20%).
- Observer pattern correctly implemented (20%).
If the program does not compile (e.g., not a valid JavaScript file), the score is 0. If it runs, the score is inversely proportional to the number of errors. If the code has runtime errors (crash), there is a 20-point penalty, plus a penalty for any additional part that cannot be checked.
How to submit
- After completing your code, make sure to fill out the Submission Notes with your basic info and indicate whether you received any help. Feel free to add any relevant information.
- Zip the folder of this repository containing your solution.
- Submit the homework using the class submission system. Choose
HW6
. - For any problems, feel free to contact the professor or TA via Discord.
NOTES
- Only submissions made through the system will be considered (e.g., no direct emails to the TA or Prof).
- You can resubmit as many times as you want; only the last submission will be considered.
- Submissions after the deadline (even a few minutes) will receive a penalty of 20%. Submissions made after 24 hours from the deadline will be ignored (score will be 0).
- Keep a screenshot that proves your completed submission.
- Coding style may be considered in grading.