The Idea
The source of my inspiration for this project was a reddit post on /r/gamedev:Is it a bad idea to just remake an existing game in order to develop Programming and Gamedev skills?
I liked this idea and the first game that came to mind was Jetpac on the ZX Spectrum which I played a lot when I was a kid. I wasn't looking for the complete retro look and feel, just a similar game mechanic, removing the main obstacle from writing a game, Getting Started.
TL;DR
Here is the playable result on itch.io. Let me know what you think:
The Scope
I'm normally not available to participate in game jams but I set myself the same criteria of getting a project done in 48 hours. This is a great way to focus you on what is and isn't important. It also means there is an end to the project, when the time is up.Again, I chose Unity as my game engine of choice. C# is actually my day job and I love it so that takes the learning out of the language out of the equation and I can focus on stuff I want to learn, which is writing games.
I also wanted this to be completely open source (MIT) so I had to chose assets that fit with this license, i.e. no commercial assets. The full source code is available on GitHub.
The Graphics
I didn't want to spend my time creating artwork so I went to my go-to site kenney.nl and found his Space Shooter Extension asset pack which had pretty much everything I needed:The original ZX Spectrum game started out by assembling parts of your rocket. This asset pack had rocket parts, perfect:
It also had an alien that had a couple of frames for walk and fly. Not a Jetman but it was in the spirit of what I was trying to do and I liked the idea of using a single asset pack so I went with it:
Finally, I needed the ground and platforms, a meteor, an explosion effect and a fuel pickup. Kenney, as usual, did not disappoint:
Tilemaps
A new feature was added to Unity 2017.2, Tilemaps. I knew it was overkill for what I needed to do but as this was a learning exercise I thought I'd dive in and create the level as a tilemap.
It's a pretty nice workflow and I was able to setup the familiar 3 staggered platforms, the ground and all collisions in a matter of minutes, after watching this tutorial:
Physics2D Colliders
To get the player to land on the ground and the platforms and move smoothly I had to add colliders to the player graphic. I created a Capsule2D collider that has a rounded top and bottom, which prevents snagging on corners according to the Unity forums.The same collider is used to determine if a rocket part or fuel is bring picked up, or if a the player hits a meteor. The meteor was originally a Circle2D but I changed it to a Polygon2D collider to make the collisions more accurate:
Physics2D Layers
As well as specifying the physics shapes that should register collisions it was important to identify which objects could collide against which other objects. For this the you go to Edit->Project Settings->Physics2D and check the boxes in the Layer Collision Matrix that are needed:
I split my layers into Player, Enemy, Rocket, Ground and Collectable and set the checkboxes accordingly. For example, this allows the player to collide with collectables (fuel, rocket parts) but not the enemy.
Physics2D Forces
Adding a RigidBody2D component to the player allows for easy movement by calling AddForce. This Unity tutorial was very informative for getting the basic Jetman movement working.using UnityEngine; using System.Collections; public class CompletePlayerController : MonoBehaviour { public float speed; //Floating point variable to store the player's movement speed. private Rigidbody2D rb2d; //Store a reference to the Rigidbody2D component required to use 2D Physics. // Use this for initialization void Start() { //Get and store a reference to the Rigidbody2D component so that we can access it. rb2d = GetComponent<Rigidbody2D>(); } //FixedUpdate is called at a fixed interval and is independent of frame rate. Put physics code here. void FixedUpdate() { //Store the current horizontal input in the float moveHorizontal. float moveHorizontal = Input.GetAxis("Horizontal"); //Store the current vertical input in the float moveVertical. float moveVertical = Input.GetAxis("Vertical"); //Use the two store floats to create a new Vector2 variable movement. Vector2 movement = new Vector2(moveHorizontal, moveVertical); //Call the AddForce function of our Rigidbody2D rb2d supplying movement multiplied by speed to move our player. rb2d.AddForce(movement * speed); } }
The final code was slightly different but that's because I wanted walking to be slower than flying so I needed to slow the movement down when walking on the ground or a platform.
Particle Systems
When the rocket takes off and the player's jetpac fires I wanted an effect that was more like rocket plumes than just simple sprites being enabled and disabled. Unity's particle system is fully featured and is capable of doing everything I was looking for, and more.
The key parts that I used were the Color over Time and Size over Time features of the particle system. Also the particle itself is the explosion effect png from Kenney's assets. The color over time comes from a gradient which blends from white to red to yellow and finally a grey smoke:
For size over time I just reduce the sprite size using the following curve:
Finally I enable collisions with the ground using a GroundPlane visual which just allow the rocket jet particles to bounce off the ground when taking off or landing.
Once I had this worked out for the rocket I copied the particle system to the player's jetpac and also to leave an icy trail behind the meteors (see the blue to black gradient at the foot of the Gradient Editor Window above).
The last touch was to add a burst particle effect when the player first starts their jetpac that creates 30 particles at time 0 and no more over time. The effect also doesn't loop:
Wrap Around Edges
The original game allowed the player to move off the sides of the screen and appear on the other side of the screen. The same thing also happened for enemies (in this case meteors). After doing a series of calculations on a FixedUpdate() to determine whether the sprite was off the screen to the left or the right I realized there was a much simpler way. I could just place two BoxCollider2D objects just off screen to the sides and then in the OnTriggerEnter2D():
Here is the complete code for handling the wrap around for the player and meteors. The screen is 45 units wide which is where the 45 comes from:
void OnTriggerEnter2D(Collider2D collider) { var xd = transform.position.x < 0 ? 45 : -45; collider.transform.position += new Vector3(xd, 0, 0); }
Drop Zone
The original game automatically dropped the rocket parts and the fuel when the player is over the rocket base. To implement this I created another BoxCollider2D which covered the screen area above the rocket base as shown here:
When the player enters the zone the OnTriggerEnter2D callback is called and we check whether it's the DropZone and then if were carrying anything we drop it at that point. We also add 100 points to our score which is maintained by a central GameController object. Here is the code:
private void OnTriggerEnter2D(Collider2D collider) { if (collider.tag == "DropZone" && carrying != null) { gameController.Score(100); Dropoff(collider); } }
User Interface
All I really needed was a Start Game button so I didn't create anything else. For the graphic I used another Kenney Asset, the UI Pack, specifically the Adventure pack:
I also needed to show the current score, the high score and the number of lives left. Kenney provided an excellent Space Font asset which does the job perfectly:
DOTween
In a previous project I used LeanTween and so this time I thought I'd try a different tweening package called DOTween. DOTween's unique selling point is that it is fast and doesn't create garbage for the garbage collector unnecessarily.
It uses a fluent style syntax which allows for composition of animations with properties in a readable way:
At this point I'm undecided about which I prefer. The LeanTween syntax seems more consistent but DOTween seems to have more functionality overall.
It uses a fluent style syntax which allows for composition of animations with properties in a readable way:
sprite.DOFade(0, 0.3f) .OnComplete(() => GameObject.Destroy(enemy));
At this point I'm undecided about which I prefer. The LeanTween syntax seems more consistent but DOTween seems to have more functionality overall.
Sound Effects
I had determined a shortlist of sound effects on my Trello board for this project:
The Pickup, Dropoff, Player Die and End Game sounds came from the Kenney's audio packs.
The Jetpac sound was a bit more tricky since I wanted it to loop as the player holds down the thrust jetpac key. Creating a looping sounds was harder than I thought. No matter how I trimmed the sounds I could find in Kenney's audio files I could never get it to sound like one continuous loop. I had to go to freesound.org and found the perfect loop there instead by a creator called qubodup:
For the rocket launch itself I found that NASA has put many of its audio and visual resources into the public domain with a non-commercial license. What better than the sound of a real rocket launching: