In this article we want to present a few ideas of how to implement different visual effects in <canvas>-based HTML5 games. These examples will be based on effects we made in our game, Skytte. We will explain the basic ideas supporting them and provide the effects used in our work.
What You Will Learn
Before we get going, we want to set out the things I hope you’ll learn from this article:
- Basic game design: we’ll look at patterns that are commonly used to make games and game effects like: game loops, sprites, collisions, and particle systems.
- Basic implementation of visual effects: we will also explore the theory and some code examples supporting these patterns.
Let’s start with some common patterns and elements used in game development.
These are simply 2-D images that represent an object in the game. Sprites can be used for static objects, but also animated objects, when each sprite represents a frame of sequential animation. They can also be used for making user interface elements.
Usually games contain between dozens and hundreds of sprites. To reduce memory usage and the processing power needed to handle these images, many games use sprite sheets.
These are used to group a set of single sprites in one image. This reduces the amount of files in the game, resulting in reduced memory and processing power usage. Sprite sheets contain many single sprites stacked next to each other in rows and columns, and like the sprites they contain can be used statically or for animation.
Sprite sheet example. (Image credit: Kriplozoik)
Here’s an article from Code + Web to help you better understand the benefits of using sprite sheets.
It’s important to realize that game objects don’t really move on screen. An illusion of movement is achieved by rendering a snapshot of a game world to the screen, advancing the game time a small amount (usually 1/60th of a second), and then rendering things again. This is literally a stop-motion effect and is used in both 2-D and 3-D games. A game loop is a mechanism that implements this stop-motion. It’s the main component needed to run a game. It runs continuously over time, performing various tasks. On each iteration it processes user input, moves entities, checks for collisions, and renders the game (preferably in this order). It also controls game time that’s elapsed between frames.
Note that the above example is very simplistic. It uses variable delta time (the elapsed variable) and it’s recommended to upgrade this code to use fixed delta time. See this article for more details.
Collision detection refers to finding the intersections between objects. This is essential for many games because it’s used to detect if a player hits a wall or a bullet hits an enemy, and so on. When a collision is detected it can be used for game logic; for example, when a bullet hits the player, the health score is reduced by 10 points.
There are a lot of collision detection algorithms, and because it’s a performance-heavy operation, it’s important to choose the best method wisely. To read more about collision detection, algorithms and how they can be implemented, here’s an article from MDN.
Particles and particle systems
Particles are basically sprites used by a particle system. In game development, a particle system is a component that consists of a particle emitter and particles assigned to that emitter. It’s used to simulate various effects, like fire, explosions, smoke, and rain effects. Particles are emitted over time and each emitter has its own parameters to define various variables used for the simulated effect, such as velocity, color, a particle’s lifetime or duration, gravity, friction, and wind speed.
Euler integration is a method for numerically integrating the equations of motion. Each object’s position is calculated based on its velocity, mass and force, and needs to be recalculated for each tick in the game loop. The Euler method is the most basic and useful for games like side-scrolling shooters, but there are also other methods like Verlet integration and RK4 integration which are better for other tasks. Below we’ll show a simple implementation of the idea.
You need a basic structure to hold an object’s position, velocity and other movement-related data. We propose two identical structures, but each with different meaning in the world’s space: point and vector. Usually game engines use some kind of vector class, but the distinction between points and vectors is very important and greatly improves code readability (for example, you calculate distance not between two vectors, but two points, which is more natural).
Simply, it represents an element in two-dimmensional space with x and y coordinates which define where the point is located in that space.
A vector is a geometric object that has length (or magnitude) and direction. In 2-D games vectors are used mostly to describe forces (e.g. gravity, air resistance and wind) and velocities, as well as proscribing movements or how light reflects off an object. Vectors have many uses.
What follows are some very common functions used on the two-dimensional structures defined above. First, calculating the distance between two points:
The magnitude (length) of a vector can be calculated directly from the last line of the above function like this:
Normalization of vectors is also very handy. The function below resizes a vector so it becomes a unit vector; that is, its length is 1, but its direction is maintained.
Another useful case is to have a unit vector whose direction is pointing from one location to another:
The dot product is an operation on two vectors (usually unit vectors), which returns a scalar number representing the relation between angles of those vectors.
Vector dot product
The dot product is a length of a vector a projected on a vector b. A returned value of 1 means that both vectors point in the same direction. A value of -1 means that vector a points in the opposite direction of vector b. A value of 0 means that vector a is perpendicular to vector b.
Here’s an example of an entity class, so other objects can inherit from it. Only basic properties related to movement are described.
You can use pixels or meters as the unit in your game. We encourage you to use meters, as it’s easier to balance things out during development. Velocity, then, should be meters per second, and acceleration should be meters per second squared.
When using a third-party physics engine, you just store a reference to a physical body (or set of bodies) in your entity class. Then, the physics engine stores the mentioned properties, like position and velocity, inside each body for you.
Basic Euler integration looks like this:
Elapsed is the amount of time in seconds that has passed since the last frame (since the last call to this method). For games running at 60 frames per second, the elapsed value is usually 1/60 of a second, which is 0.016(6)s.
The article on delta time mentioned earlier also covers this problem.
To move objects, you can change their acceleration or velocity. Two functions shown below should be used for this purpose:
To move an object to the right you could do this:
Note that objects set in motion stay in motion. You need to implement some kind of deceleration to stop a moving object (air drag or friction, maybe).
Now we’ll explain how certain weapon effects are made in our HTML5 game, Skytte.
Plasma weapon in Skytte
This is the most basic weapon in our game, just one shot each time. There are no special algorithms used for this weapon. When a plasma bullet is fired the game simply draws a single sprite that’s rotated over time.
A simple plasma bullet can be spawned like this:
Blaster weapon in Skytte
This weapon is a little more complex. It also draws simple sprites as bullets but there’s some code that spreads them out a little and applies a random speed. This gives a more devastating feeling to this weapon, so players feel they can inflict more damage than with the plasma weapon and have better crowd control when among enemies.
The code works similarly to the plasma weapon code, but it spawns three bullets and each has a slightly different direction.
Ray weapon in Skytte
This one’s interesting. The weapon shoots a laser ray but it’s procedurally generated in each frame (this will be explained later). To detect hits, it creates a rectangular collider that deals damage every second for as long as it collides with the enemy.
Fig. 8: Rockets weapon in Skytte
This weapon shoots guided missiles. The rocket is a sprite with a particle emitter attached to its end. There’s also some more sophisticated logic, like searching for the nearest enemy or limiting the turn value for a rocket to give it less maneuverability. Also, rockets don’t start to seek for enemy targets immediately – they fly straight for a time to avoid unrealistic behavior.
Rockets in Skytte move toward their nearest neighbor. This is achieved by calculating the proper force needed for the projectile to move in any given direction. To avoid moving only in straight lines the force calculated shouldn’t be too big.
Assuming that Rocket is a class inheriting from the Entity class described earlier.
FlakFlak weapon in Skytte
Flak was designed to shoot many small bullets (something like a shotgun), which are little dot sprites. It has some specific logic to randomly generate the position of these dots within a cone-shaped area.
Flak weapon bullets cone area
To generate random points in a cone-shaped area:
The random.uniform() function returns a random floating point number between two values. A simple implementation could look like this:
ElectroElectro weapon in Skytte
Electro is fancy weapon that shoots lightning at enemies within a particular radius. It has a limited range but can shoot at several enemies at once and always hits successfully. It uses the same algorithm to draw curved lines to simulate lightning as the ray weapon but with a higher curve factor.
Procedural curved line
To create the laser beam effect and electro weapon we’ve developed an algorithm to count and transform the linear distance between the player’s ship and the enemy. In other words, we measured the distance between two objects, found the middle point and moved it randomly alongside the section. We repeat this action for every new section created.
To draw these sections we use HTML5 <canvas> draw function lineTo(). To achieve the glowing color we used multiple lines drawn over one another with more opaque color and a higher stroke width.
Procedural curved line
To find and offset a point between two other points:
Below are functions used in the above code:
Finding nearest neighbor
To find the nearest enemy for a rocket and the electro weapon, we iterate over an array of active enemies and compare their positions with the position of a rocket, or the shooting point in the case of the electro weapon. When a rocket locks on its target, it flies toward it until it hits or flies off the screen. For the electro weapon, it waits for a target to be in range.
A basic implementation may look like this:
These topics cover only the basic ideas that support them. We hope after reading the article you now have a better idea of how to start developing such things. Check out the resources below and try doing something similar yourself.
Integration Basics: Discusses implementing some basic intergation methods.
Amit’s Game Programming Information: A list of bookmarks compiled by Amit Patel, with a lot of information about various game programming patterns and methods.
Tuts+ Game Development: Collection of game development tutorials
Gamedev: Largest and most popular game development portal and forums.
gafferongames.com: Glenn Fiedler’s game development articles.
"What is a sprite sheet?”: Information and video about sprite sheets and how they work.
“2D collision detection”: From MDN’s game development pages.