Make a retro game with custom sprites using JavaScript

Mars Attack

Mars Attack is a side scrolling shooter game inspired by the classic games of the 80’s. What is impressive about this game is that it does not use any pre-built graphical assets.

The entire game graphics is generated using only code:

  • Mountains are drawn using random lines
  • Stars are just random points
  • Ship, Enemies and Rockets are drawn using in-code defined sprites

By implementing this game you will learn important programming and game development concepts such as:

  • How to implement a game with multiple scenes
  • How to implement a game loop
  • How to organize your code into modules

This game is using in-code defined sprites. If you are not familiar with these, please consult Working with custom in-code defined sprites tutorial.

Generating mountains from random lines

The red mountains from Mars are the first element that we will code in our game. Our mountains are generated using random numbers and then drawn using line segments.

Tip: To keep the code simple, we will use plain random numbers, but if you are experienced programmer, you can try to use Perlin noise for a more natural look.

To simulate the scroll effect on the mountains, we will store the line segments in an array.

let segments = generateSegments(15);

Inside the loop function, we will iterate over these segments and update their .x coordinate. We also need to verify if the segment leaves the screen completely on the left and in that case remove it and add it at the end of the array.

Run the following program and enjoy a long flight over mountains.

Adding a landing ramp at the end

The goal of our game is to arrive to the landing zone safely while destroying the attackers on the way. To ensure that we have where to land, we will place a landing ramp at the end of a mountain sequence.

The ramp is nothing else but a flat segment at the end of the array. Since segments are objects, we will add the .ramp = true property to this segment to distinguish it from the rest.

We also implemented two different scroll behaviors in the code. In one situation, the scroll stops when the ramp reaches middle of the screen, in the other the scrolling continues, and new ramps will appear from time to time.

Please check these behaviors and see which one do you like more for the game. In our example, we selected to go forward with the first behavior.

Better décor: Solid mountains and stars

It is time to render the décor using a more realistic approach. Instead of the wire frame look used until now for the mountains, we will go ahead and fill the mountains with a solid color – to better resemble the red mountains of Mars.

To do this, we will use vertex command instead of line. Check the drawSegments function for more details.

Stars are also added to the décor using plain random points.

Notice that we also implemented a gradual scrolling slow down as the ramp approaches.

Adding player ship using in-code defined sprites

By adding the player ship, our game begins to resemble the final product. We also took the opportunity to organize the code into modules.

Modules

By using modules, we can group together related functions, keeping the entire code base manageable. Modules as well as JavaScript classes are great ways to encapsulate functions.

To use a module, you have first to require it. After that, all functions in that module will be available as methods on the object returned by the require function.

In-code defined sprites

The other important addition in this step is the player ship. This is implemented using in-code defined sprites.

The same sprite command that can be used to load pre-built sprites, can also be used to define sprites starting from a series of images. In our case images are created from text stored in the Microsoft MakeCode format.

In-code defined sprites can be created directly in code or by first drawing them on paper and then transferring them in the code. Please refer to the other examples for more details on this great feature.

Adding enemy, collisions with enemy

By adding enemies our game becomes playable. You can now navigate the ship on the screen trying to avoid collision with the enemy ships.

Note that the enemy ships are also drawn using in-code defined sprites.

For this game we defined only two types of enemy ships. Please feel free to add as many types as you want. Try to define multiple frames for them to have them animated.

Tune scrolling speed. Scroll faster when accelerating

Now we will take the time to do a few optimizations to the code and also try to give the game a more realistic look by tuning the scrolling speed.

In our implementation the mountains and stars will scroll faster when the player will accelerate (e.g. press the right arrow key).

At the same time, if the player decelerates the scrolling speed will be reduced to a minimum.

Another option is to allow the player fly backwards by allowing the décor to scroll in the other direction as well. You can try to implement this option if you think will improve the game mechanics.

Ship shoots rockets

We will now add armament to our ship. Each time the player presses the Space key, our ship will shoot a rocket.

Please note that for shooting, we are using the keyWentDown command as opposed to keyIsDown used for navigation.

if (keyWentDown(32))
{
    ship.shoot();
}

This allow us to shoot only one rocket when we press the Space key. To shoot another we need to release the key and press it again.

Adding explosions. Ship can now land!

A big effect in space shooting games are the explosions. We also need to implement explosions in our game to give the collision between a rocket and an enemy vessel more realistic.

Explosions are also implemented using in-code defined sprites. Notice that we only define one sprite with three frames and then we are randomly displaying this several times at the coordinates of the collision.

Landing

To allow our ship to land, we will also implement landing. The landing verification function is checking if the ramp (e.g. a line) intersects the bottom part of our ship (e.g. a rectangle).

Restart the game from keyboard

Our game is almost ready. We will only need to implement an easy way for the player to restart the game in case of a collision or if wants to replay it.

To restart the game, the player can press ‘R’ at any time or at the end of the game.

Added intro screen. Option to collide with mountains

To make the game more polished we added an intro screen that display some basic instructions to the player and gives the option to start the game in easy mode or expert mode.

In expert mode the player requires extra skills as he needs to navigate not only around enemy ships but also around mountains. In this mode the ship can crash if hits a mountain.

Collision between ship and mountains is implemented using collisionLineRect built-in function which detects collisions between a line and a rectangle. We need to invoke this function for all visible segments of our mountains.

It is also a good idea to increase the duration of our game by adding more segments to the generated mountains using segments = generateSegments(100);.

Conclusions

Our game is ready! If you liked it, please go ahead and use the “Save a copy” command to fork this game under your account. We invite you to further extend this game and then share your new version with friends and family (using the share command).

These are a few suggestions for possible extensions:

  • Add more types of enemy ships
  • Make enemies also shoot rockets or drop bombs
  • Implement finite ammunition for the ship
  • Implement a daytime level with different colors

Happy coding!

Read more blog articles Browse JavaScript projects

About codeguppy

CodeGuppy is a FREE coding platform for schools and independent learners. If you don't have yet an account with codeguppy.com, you can start by visiting the registration page and sign-up for a free account. Registered users can access tons of fun projects!


Follow @codeguppy on Twitter for coding tips and news about codeguppy platform. For more information, please feel free to contact us.