Skip to content

saoirse-defi/milestone2-vanilla

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Against All Odds Milestone Project

Introduction

This repository represents my submission of milestone project 2 for Code Institute. For this milestone project I have created a 2D spaceship survival game built using JavaScript & HTML Canvas. Developed using vanilla JavaScript, no additional libraries were used. I have taken inspiration from a childhood video game called Geometry Wars, specifically one of it's game modes called Pacifism. As a child, I spend countless hours on this mode trying to beat my friend's high scores. The concept is simple, you don't have any projectiles so the only way to survive is to pass through gates in order to destroy nearby ships and to thin the ever increasing hoarde. The goal is to survive for as long as possible and rack up the highest score you can. Similarly to other arcade games, there is no possible way to beat the game. You must simply try and survive for as long as you can. The amount of enemies that spawn increases as the game progresses, reducing the margin for error. Cusomisation has been implemented within this application. The user has the ability to change the background image and game difficulty while waiting at the start screen. Local storage is then used to track states of user customisation between sessions.

Considerations

Geometry Wars 2: Pacifism was released on traditional games consoles with a large amount of screen real estate & a dedicated controller. It is a game that requires pin-point accuracy which unfortunately couldn't be achieved for smaller screens in this implementation. While implementing the mobile version of this application, I have encountered several limitations that dramatically reduce the enjoyment one gets from playing this game. In the start menu on desktop, the user is able to select their desired difficulty but for mobile version I have decided to limit game speed to amateur and to hide the visible of this element ensuring the user cannot access it. Due to the small screen, there is a reduction in the amount of time the player has to react to enemy sprites. Touch drag was selected to move the player sprite. At the early stages of design, this control method seemed like this best of a bad bunch, but it too lacks the necessary accuracy to play the game as intended. In order to appreciate this game as it was designed, it is recommended to play this application on a desktop/laptop with a mouse. The improved accuracy due the mouse & the increased screen size allows you to increase the game's difficulty.

UX

Project Goals

My main goal for this Code Institute milestone project was to authentically emulate a video game from my childhood (Geometry Wars 2: Pacifism) within the browser using vanilla JavaScript. I also wanted to successfully capture the gameplay feeling from the original with learning about vanilla JavaScript & HTML5 canvas.

Player Goals

The target audience for this game is users of any age.

The player's goals are:

  • That the game is fun to play & keeps the player coming back to beat their high score.
  • Controls that are intuitive & easy to learn.
  • Sprite & Background designs that inspire emotion.
  • Audio cues that outline specific game events to provide feedback.

Against All Odds has achieved these player needs by:

  • Controls were designed with simplicity in mind while retaining the original game's gameplay feeling.
  • The amount of buttons within the application have been kept to a minimum.
  • Controlling the player sprite is done by simply moving the cursor to where you would like the user to travel (for mobile devices, this will be accomplished by dragging your finger across the screen).
  • The user is notified of significant game events using audio cues.
  • These audio cues occur at game start, game end, whenever a user successfully clears a gate & whenever gems are collected.
  • Making it easy to restart the game loop with minimal downtime by the click of a single button.

Developer & Business Goals

  • The main developer goal for this project is to learn as much as possible about JavaScript & HTML5 canvas.
  • To add another project that the developer is passionate about to their portfolio.
  • To create a well-designed application that is free of any bugs which would cause the user stop playing prematurely.
  • Designing a satisfying game loop that is fair to the player.

User stories

  1. As a user of this web application I want:

    • A satisfying game loop that keeps me coming back in order beat my high score.
    • Sound effects to let me know when important game events take place.
    • Sound design that doesn't get boring to listen to over time.
    • Consistent frame rate.
    • The ability to change the difficulty if needed.
  2. As a games platform looking to add this application to their library I would want:

    • A great game loop that will keep our users hooked.
    • Sound design that doesn't get boring/annoying to the user over time.
    • Sprite designs that capture the user's imagination and generate an emotional response.
  3. As a parent of a child user I would like:

    • A game free from gratuitous violence & profanity.
    • Sprites design that doesn't make the children scared.

Sprites

When designing UX for a game, selecting the correct sprite image is essential as it can dramatically change the appearance & feel of your finished application. For my search, I decided to browse several game development marketplaces to see which sprite images were available for free. As the theme of the game is science fiction, it naturally limited my search.

Player Sprite (Human Mining Station)

As mentioned above, the player sprite follows the cursor around the canvas. This introduced a UX issue that would look disconcerting to the end user. Depending on where the user clicked on the canvas, the sprite would appear either upside down or at the incorrect angle. Due to this, circular sprite was chosen for the player. This allows the sprite to appear the same, no matter the angle or direction of user input.

Player Sprite

Enemy Sprite (Alien Drone Ship)

When choosing the enemy sprite, I wanted the design to give the user a sense of mortality and dread. The ship had to look like it belonged to a terrifying alien race hellbent on conquering human civilisation. As the enemy sprites travel in hoards, the sprite design needed to look good in proximity to other enemy sprites.

Enemy Sprite

Gate Sprite (Used to thin Alien horde)

This sprite was designed and created by myself using Microsoft Paint emulating the gate sprites from the original source material. The 2 orange circles at the edge of the sprite are deadly mines which end the game if touched. The user must pass through the center of the gate in order to clear the surrounding area of enemy sprites.

Gate Sprite

Background

In 2D game development backdrops are often used to add depth, give context to the canvas & individual game elements. They provide something for the user's imagination to work with. As this game is a 2D space arcade, the natural choice for background images would be horizons from outer space. Within the background image array, there are several images of different nebula & starry backdrops.

Default Background

As the default background, I have chosen an image of a horizon in the depth of outer space. It contains no planets or nebula, further emphasising the cold darkness of the battlefield that the user finds themselves in.

Background Array

This array is responsible for storing background image file names as strings. This array will later be used to cycle through different backdrops adding another element of user customisation.

Sound

All sound effects & background tracks were recorded for this project by DJ green except the gem collected SFX which was sourced from a free game development marketplace

Bandcamp Player (Soundtrack)

At the start screen, the user is able to choose their soundtrack from an embedded Bandcamp playlist. All backing tracks have been produced by DJ Green. Expressed permission has been given for the use of all audio used in this project.

Game Start Sound Effect

When the user starts or restarts the game, this sound effect will be played to let the user know it's game time.

Gate Destruction Sound Effect

This sound effect was custom-made to provide the user with aural feedback whenever a gate is successfully destroyed. When choosing the sound design for this effect, we wanted to emmulate the sound of an explosion in space. It is well known that sound waves cannot travel through the vacuum of space, but this was our thought process when designing the sound effect.

Gem Collected Sound Effect

This sound effect was sourced from freesound.org in order to provide feedback to the user whenever they have successfully collected a gem. It was chosen for it's arcade-like sound while also being satisfying to listen to repeatedly. It was essential to choose a sample which wouldn't get annoying over time.

Implementation

This section will outline the technologies & processes used in the design & implementation of this application.

HTML Canvas

The Canvas is an HTML element used to draw graphics via scripting. In this case, our scripting language will be JavaScript. The JavaScript code can access the area of the canvas allowing for dynamically generated graphics. Many things can be applied to the canvas including graphs, animations, image composition & video games.

Animation Loop

In order to create animation through JavaScript & HTML5 Canvas, the animation function must call itself recursively using requestAnimationFrame. This principle of recursion refers to a function which calls itself creating a loop, in this case an animation loop. Each time the animation function is called, a single frame is drawn on screen.

User Customisation

For this milestone project, I have implemented 2 separate elements of customisation for the user. The user is able to apply different backdrops to the canvas. Also, they have the ability to increase or decrease the game's difficulty by having control over the speed of enemy sprites. The states of these custom elements are then saved for later sessions using local storage.

Background

A collection of backdrops have been prepared and strings of their file locations have been stored within an array. At the start screen, this array is then used for user customisation in order to change the background image.

Difficulty

At the start screen, a slider is provided to the user to allow for some further customisation. The difficulty variable is linked to the enemy speed. The user has the ability to choose from 5 different difficulty levels. In order to prevent users selecting the lowest difficulty in order to get a high score, the amount of points generated from each game event are directly correlated to the difficulty variable.

Design Choices

Physics Engine Choices

Originally I had chosen to create this application using a purpose built JavaScript library (ie. PixiJS). Although after seeking advice from my mentor, he advised me that using vanilla JavaScript would be a better option as a learning exercise. Looking back on this decision, I believe it was a great choice. It has allowed me to better study the intricacies of the JavaScript call stack & the HTML5 Canvas.

Hit box (Hit marker) Detection

Hit Detection is probably the most crucial element of a satisfying video game. Below you can see an image of how the hit detection has been implemented within this application. For the gate sprites, four points are used to implement hit detection. Two inboard hit markers which are used by the player to clear the gate & two outboard markers, one on either side of the sprite which will kill the player upon contact. Once per frame, the distance between the player and these four hit markers are calculated allowing them to be used for hit detection.

Gate Hit Markers

User Input Choices

During the design process of a video game, choosing the correct control scheme is an essential as it's the only physical connection that the user has with the application. Two different control methods had to be designed, one for desktop and another for mobile.

Desktop Controls

Due to the nature of JavaScript's event listening system, a choice between 2 player input methods on desktop had to be made. The decision was between the traditional WASD directional input or using the mouse click to move to position. Between these two, the 'point and click' was chosen due to its ease of use. Unfortunately after user testing, it was found that this directional input from the user didn't have the correct gameplay feel due to the lack of accuracy & predictability. In the final implementation, the JavaScript event listener 'mousemove' was used. This directional input allows for greater control as the player sprite smoothly follows the cursor.

Mobile/Tablet Controls

For mobile & tablet implementation, a new method of user input was needed to account for the touch screen. When thinking of possible options, touch drag was the only one that stood out as enjoyable to use. It was a logical transition to use the JavaScript 'touchmove' event listener as 'mousemove' was implemented successfully on the desktop application.

Visual Choices

Start Menu Design

In order to reduce latency from the perspective of the user, the start screen and the game are both drawn on the same canvas but at different times. I have allowed the user control the player sprite within the start menu. This gives them time to get accustom to the controls before the game starts.

Tutorial Element

Featured in the bottom right corner of the screen, I will see an icon shaped with a graduation cap. When the user clicks the icon, a new tab will bring the user to YouTube in order to watch a short demo video of how the game is designed to played.

High Score Element

Located in the top right corner of the canvas element, this element checks local storage for high score upon page load and after each player death.

Bandcamp Player

This is an embedded iframe which streams a playlist from Bandcamp. When choosing the correct method for providing the user with a background track, the embedded Bandcamp player was the only logical choice due to the sound file's size. Applying too much compression to the audio would result in a terrible listening experience.

Difficulty Output

In the top left corner of the canvas, the user is able to select from one of five difficulties. Each difficulty has a specific string which it displayed to the user describing each level of difficulty taking inspiration from game such as Halo & Doom.

Game Over Modal Design

In order to notify the user of death, a modal has been designed to display user points, the cause of death & whether a high score was achieved. Here, the user can click one of two buttons, one to restart the game & one to return to the start screen.

Fonts

Two fonts were chosen for this project; hero font & secondary font. Both of these fonts look very different but were chosen for their science fiction design attributes.

Hero Title Font

The hero font selected is name Orbitron. It was chosen as it fit nicely within the theme of science fiction and space exploration. It's similar to certain fonts used for corporation logos in old sci-fi movies.

Secondary Font

The secondary font chosen is called Dot Gothic 16. It was designed to be pixelated in order to mimic how text used to look on older CRT monitors such as those found in vintage arcade cabinets.

Icons

Tutorial Icon

When a user tries a game for the first time, the rules maybe unclear at first. This is why in addition to the explanation underneath the hero title, a link to a tutorial video has been provided. This link will open a new tab displaying a short YouTube video of the game being played. When choosing an icon for this tutorial link, it was essential to pick something that is universally known. I decided to use a graduation cap in the final implementation as it is widely know to represent tuition or school.

Colours

HSL Colour Change Effect

After learning about the HSL colour wheel, I decided to use the animation loop to further effect. By creating a variable called hue, I was able to increment it with each animation frame which in turn would cycle through the colour wheel. This caused a beautiful colour change effect which I have applied to the hero title and other elements.

Key Elements (Classes & Functions)

Classes

This project consists of three distinct classes which allow us to create instances of different types of sprites.

Player Class

The Player class will only ever create one instance per game. Upon game start, the instance of the player class is positioned in the center of the canvas.

Enemy Class

Instances of the enemy class are created once per second at a randomly selected corner of the canvas. As the game progresses, the number of enemy sprites generated increases. They are attracted to the player sprite and travel at a constant speed allowing the user to guess where they will be frame by frame.

Gate Class

Instances of the gate class are positioned randomly on the canvas, this allows the game to stay fresh as no two games are the same. Each gate is give a random value of rotation velocity from a given range. This allows each gate to feel different and increases the visual appeal to the user.

Functions

Initial Spawn Function

This essential function allows for a large amount of enemy & gate sprites to be created before the game ever begins. It is called as soon as the DOM has been loaded successfully. It creates 500 instances of both enemy & gate sprites then places them into their respective arrays for later use.

Game Loop Function

This function is called every time the animate function is called recursively. It is responsible for the spawning of enemy & gate sprites on screen. It also looks after the majority of array management, hit detection & totaling the user's score.

Check Record Function

Upon player death, this function is called in order to compare the user's score against the highest score recorded in their local storage. If they have successfully achieved a high score, the user will be notified within the game over modal.

Add Commas To number

Some scores achieved in this application can reach into the billions hence a function was needed to add commas into large numbers. This function uses template literals and was sourced from Stack Overflow.

Performance

Image Resizing & Compression

All background images were compressed using the website outlined below. This significantly reduces load times when the user calls the changeBackground function as each image takes less time to load.

Autoprefixing

The CSS style rules have been Autoprefixed to maintain uniformity of style rules across all browsers.

Testing

User Testing

A large amount of repeated user testing is carried out to ensure that every element, class or function is providing the intended result. This is done by isolating the intended piece of code you would like to test and interacting with it in as many ways as possible.

Gate Hit Marker Testing

Accurate hit detection is essential for any game worth it's salt. Without it, the user cannot trust the application to perform predictability. In order to test this in isolation, enemy spawning was disabled. This allows the tester to accurately map each hit marker without having to avoid enemy sprites. The tester then passes over specific point of the gate sprite to see if the desired effect is achieved. For example, the tester will target a mine on a specific side of the gate sprite. They will then test the outer limit of this point's hit detection and edit the location of this hit marker is needed.

Validation

This developer used W3C HTML, W3C CSS & JSHint validation services in order to check the validity of their code.

Performance Testing

Cached Sprites

During the application initialisation, two separate arrays are created and fills 500 sprites each. One array is for enemy sprites & the other for gate sprites. This allows sprites to appear on screen the moment the user starts the game.

Compressed images

As with any web application, large image file sizes can significantly affect page load times. Due to this, all background images were compressed to 50% their size. This amount of compression was chosen as it lead to a dramatic reduction in load times when the user wished to change the background without severely affecting image quality.

Reusing sprites once they have left the screen

Once enemy/gate sprites are destroyed and removed from the canvas, they are added back into the cache for later use. This allows for the player to keep playing as long as possible without running out of pre-loaded sprites. If all 500 cached sprites were to appear on screen at once then this could be an issue. But this is not a major concern as with that many sprites on screen it would be impossible for the user to stay alive along with the extremely low frame rate.

Screenshots of finished project

Homepage

Homepage

Game Over Modal

Modal

Bugs Discovered:

Overlap detection for swarm behavior

At the start of the project when designing sprite behavior, I wanted to implement overlap detection. Each enemy sprite in the swarm would calculate the distance to the enemy closest to them. This distance would be used to move the 2 enemy sprites apart preventing any sprite overlap from happening causing the enemy bundle together into a swarm with an ever-increasing radius. Unfortunately this feature was removed from the final implementation as it put too much strain on the call stack per animation frame. Enemy sprite movement looked jittery and frame rate would drop significantly as the amount of enemies on the screen. This is due to the exponential number of function calls the swarm would need for each frame.

Hit box not following gate rotation

When implementing rotational movement for gate sprites, I noticed that the position of certain hit markers were mistranslated. Using some simple trigonometry, I was able to calculate the rotation transformation needed. This is outlined within the update method of the gate class.

Gate detection not consistent

In initial development only 1 hit marker was used to clear the gate, its location was at the dead center of the sprite. This caused some inconsistencies during gameplay as the user would sometimes not pass through the hit marker precisely enough and the gate sprite would remain on screen instead of successfully detonating. A second hit marker was added to make it easier for the user to clear the gates. This was a crucial improvement as it significantly helped the gameplay feeling and made gates more consistent.

Frame rate slowing down

Animation in JavaScript uses a function called requestAnimationFrame in combination with a process called recursion to generate each frame seen on screen. Due to this, too much complex logic required within each frame will cause the frame rate to drop. The JavaScript call stack can only handle so many calls per frame. When designing the main game loop, it is essential to reduce nesting as much as is practical. In the early development stages, this application suffered significantly from this issue. The code was then streamlined to remove any unnecessary nesting and a significant increase in frame rate was observed.

Prevent Default Behavior During Touch Event

When working with a touch screen, developers will always encounter the same problem, touch drag in the y-axis will lead to either unwanted scrolling or a page reload. In order to overcome this issue, we needed to prevent default browser events from being initiated.

Memory Leaks

During early stages of development, low frame rate & random application crashes were significant factors. After consulting with my mentor, it was advised that I refactor my game loop function to avoid any unnecessary nesting while being mindful of how garbage collection is being taken care of.

Deployment

Hosted Domain

Against All Odds was deployed using a domain name purchased from namecheap.com and uses their dedicated hosting subscription.

Github Pages Deployment Procedure

This project was developed using Gitpod, committed to git and pushed to Github using the built-in function with Gitpod.

To deploy this page from Github pages from its Github repository, the following steps were taken.

  1. Log into Github.
  2. From the list of repositories on the screen, select saoirse-defi/milestone1-bad-arts-1.0.
  3. From the menu items near the top of the page, select Settings.
  4. Scroll down to the Github Pages section.
  5. Under source click the drop-down menu labelled None and select Master Branch.
  6. On selecting Master Branch, the page is automatically refreshed, the website is now deployed.

At the moment of submitting this milestone project, the default branch is version1.2 which is the latest version.

How to run this project locally:

To clone this project into Gitpod you will need:

  1. A Github account
  2. Use the Chrome browser

Then follow these steps:

  1. Install the Gitpod browser extensions for Chrome
  2. After installation, restart the browser
  3. Log into Gitpod with your Gitpod account
  4. Navigate to the Github project repository
  5. Click the green 'Gitpod' button in the top right corner of the repository
  6. This will trigger a new Gitpod workspace to be created from the code in Github where you can work locally

To work on the code within a local IDE such as VScode:

  1. Follow this link to the Github repository
  2. Under the repository name, click 'clone' or 'download'
  3. In the clone with the https section, copy the clone URL for the repository
  4. In your local IDE, open the terminal
  5. Change the current working directory to the location where you want the cloned directory to be made.
  6. Type 'git clone', and then paste the URL copied in step 3

git clone https://www.Github.com/USERNAME/REPOSITORY

  1. Press enter. Your local clone will be created.

Further reading and troubleshooting on cloning a repository can be found here Github.

Credit

Using Local Storage

General HTML5 Canvas Knowledge

Manipulating Canvas Elements

General JavaScript Event Loop Knowledge

General JavaScript Event Loop Knowledge

JavaScript Import/Export

Enemy AI Movement

Prototypical Inheritance

Function that adds commas to large numbers

Media

Music & SFX created by DJ Green
Gem Collected SFX from Free Sound
Player & Enemy Sprites sourced from Game Dev Marketplace
Tutorial video created & uploaded by Saoirse Frawley

Wireframes

Start Screen

Desktop

Desktop Start Screen

Tablet

Tablet Start Screen

Mobile

Mobile Start Screen

Game Screen

Desktop

Desktop Game Screen

Tablet

Tablet Game Screen

Mobile

Mobile Game Screen

Game Over Modal

Desktop

Desktop Game Over Modal

Tablet

Tablet Game Over Modal

Mobile

Mobile Game Over Modal