Final paper for the 2020/21 2D game programming course. Create a fully playable game on any subject you choose.
Choosing a theme for the game is probably the single decision that will bear the biggest influence on the resulting game, so I decided to take as much time as needed. During the first few days of the decision process, I followed a few tutorials that offered advice for beginner and intermediate game developers, out of them I got the idea that a few points were key to make a good decision:
- The game had to be about a subject I liked myself. There are a few reasons for this, but the most obvious are that you will enjoy more the development process, and that you will be more familiar with what makes a good game.
- Be conservative, the development process will, very likely, be harder than you expect. If you choose an objective that is too ambitious, you are likely to either never finish the game or to have so many different aspects to worry about that you will not have the resources to implement every aspect properly.
- The most successful games are not necessarily the most innovative ones, usually they follow a well established pattern; "introduce a new twist while still falling inside a category that is familiar to players".
With these guidelines in place, I started looking at options, but, before we look at what these options were and the decision process, here is the final result.
An example screen on the RAMO space shooter.
A platformer game using the Sunny Land asset pack
The asset store has really good resources for 2D platformers, amongst them the Sunny Land pack.
Why? This is something that I kept thinking about while developing the Mario-like game for the previous exercises, this asset pack looked fun to work with. I had just developed a pretty complete platformer game, most of that experience would help develop a better game if I had chosen this option.
Why not? I felt like, after working in a platformer for almost two months, I could learn more from working with a different style game.
The classic Lemmings game, first in Amiga then ported to other systems.
Why? This was one of my favorite games back on its day, and I feel like it could still be very fun to play even in the original form, even more so with some twists.
Why not? This game introduces many aspects that are very different to the style that I developed before. I wasn't completely sure I could produce a good quality game in the amount of time available.
Classic break-a-brick game, the image is from the pioneer Arkanoid.
Why? This seems like a simple concept that should be fairly simple to implement. Once you have a playable game, you can introduce more variations to the powerups and bricks to make the game more interesting.
Why not? I feel like, with this style of game, most of the functionality could be implemented in-house, and I felt like experimenting with some packages that added functionality to the game, like particle systems, Cinemachine and other assets from the Unity Asset Store.
A variation of the brick breaking genre, bubble shooting in all its variations.
Why? Cool concept, very different from the platformer dynamic but it seems like it should be possible to keep it simple and create a playable game within the time boundary given.
Why not? It was a close call, there wasn't really a reason why not, finally I thought that the space shooter would be more dynamic and more fun to play.
Space shooters come in all variants, from the very simple, like in the image, to incredibly complex, but all of them are fun to play.
Why? Offers the possibility to build a fast-paced game with nice imagery and lots of visual effects. I couldn't really find many points against this theme. It could prove to be too complex, but it seems like it should be possible to provide, at the very least, a few playable levels in the amount of time provided. So this is the theme I decided to develop.
At the start of the game the player sees a title page, click/touch the screen or press enter and it will display the menu. The screen has instructions for this.
The menu lets users play from the start, select a level or quit. Buttons indicate their function.
During the game, the player can control the spaceship using the on-screen joystick and/or with the arrow keys. To shoot, both the space bar and a touch on the right side of the screen are valid.
ESC
pauses the game, displaying a dialog with instructions and three options, continue, start a new game and quit.
In this case, I think the visual part of the game is going to be very important, it makes sense to build the basic functionality first, without worrying too much about the looks, and then try imagery on to see what it looks like when the game is running.
The objective is to create a space shooter that runs in an open world where the player has to complete one mission per level. As the player approaches the level's objective, it will become harder to survive.
Levels should be progressively harder. The player should be able to learn about the enemies in earlier levels to help survive later levels.
We will start by creating a Player
GameObject and giving it a controller. From the beginning we are going to give the game a mobile first approach, so the game will get mobile controls and any actions available have to be operable by touch.
Next will come the backdrop the game is set against, we can keep the backdrop small and make it follow the player using some parallax technique.
After that we will add enemies with different characteristics.
Then we can create a UI.
Last step would be to add details, music, sound effects, improve the lighting, check physics work as expected and other post-production aspects.
Once we have a few basic blocks, creating new levels should be easy:
- Create a new scene.
- Drop a few components.
- Adjust a few parameters in the scene, from the editor if possible.
Ideally, that should be all that is needed to create new playable levels.
- The player's space fighter can decelerate or accelerate in 2D, while there is no player input, the spacecraft preserves its current speed.
- Each level is an open world, the player can choose to get farther away from the objective.
- Each level has an objective. Survival should get progressively harder as the player approaches the objective.
- The camera uses the perspective view, to give some depth to the game. But all game interactions happen on
z=0
making it a 2D game. - Background scrolls with the player.
- Enemies, except for the level's boss are auto-generated according to some parameters.
The objective is to have a multi-layer star background that gives an impression of depth, we are in space after all!
The game has two star backgrounds that are children of the main camera, since the camera follows the player, the background also does. To make a parallax effect, we can use the material's offset thus:
void Update()
{
MeshRenderer mr = GetComponent<MeshRenderer>();
Material material = mr.material;
Vector2 offset = material.mainTextureOffset;
offset.x = transform.position.x / transform.localScale.x * parallax;
offset.y = transform.position.y / transform.localScale.y * parallax;
material.mainTextureOffset = offset;
}
The parallax
variable is editable from within the inspector, giving it different values, we can adjust the speed of each layer until it feels adequate.
The star background was inspired by this video tutorial.
Having a slightly different backdrop for each level can help players quickly identify which level they are currently at, besides helping giving each of them a different atmosphere.
That would be difficult to achieve using just the star background, and we want the obstacles to be just a coincidental, not a defining part of the level, at least until later levels, where they can become NP characters and attack the player.
Being already in space, we can use nebula like images for this end, each level can have a different nebula as a backdrop, achieving two ends, first, it makes the level look better, then it helps the player quickly know where they are in the game.
Ideally, we want the nebula backdrop to scroll even slower than the farthest starfield background, on the 2.5D game that we are developing, we could use another parallax script to this end, but, since we don't care if the component disappears form the screen view, it is easy to achieve the same result placing the component far back and scale it up. This setup also makes it easy to configure the look and feel of the backdrop on different levels, and use Unity's built-in functions to modify it, for example, adding a bit of light, we can achieve effects like the following.
Nebula backdrops make it easy to create compelling level backdrops.
The game aims to be a side-scroller space-shooter with a twist, part of that twist is that the game takes place in an open world, I think that could let us add some interesting updates later on, for example, different missions inside one level.
At the moment, each level only has one mission, find the boss and destroy it, since the play takes place in an open world, we need to give the player some help accomplishing the task, some indication of wherein lays the boss, we can use an on-screen arrow that points in the bosses' direction to accomplish that. The result will look as follows:
The use of an on-screen arrow allows to have, at the same time, an open-end world with mission mechanics.
The types of enemies that we will encounter on the level can be classified in two groups, regular enemies and bosses.
This enemies are spawned at random intervals by a spawner game object that stays slightly ahead of the player as she moves through the level.
The spawner is slightly bigger than the screen height and spawns elements at random intervals, within a range, and at random points of the quad element that is made of. The spawning rate range can be adjusted from the editor to make levels progressively harder.
The basic regular enemies are capsule shaped ships that do not fire any shots and just try to collide with the player character. This characters use the Astar project for pathfinding.
As a material the capsules use material no.12 on the Yughues Free Metal Materials pack from the Unity asset store. To improve the look of the enemies I added a subtle point light, of the same color that the light sections of the material. The scenery is supposed to be an important part of the game, and the light from the capsules reflecting on the background objects adds a very interesting flavor.
To make the objects more dynamic, I added a Spinner
script that initially I had planned to use only on background objects, like asteroids, to make them spin on place.
The script is very simple but effective. It takes a speed
Vector3
initialization from the editor, or a default of (1,1,1)
if not set from the editor, and it uses it, inside the Update
method to rotate the transform thus:
transform.Rotate(speed * Time.deltaTime);
The second type of enemy created is a sphere shape with a metal texture that follows the player while keeping it's distance. The sphere has a satellite circling it that shoots the player at semi-random intervals controlled from the editor settings.
This enemy is harder to kill than the capsules so the spawner generates less of them, currently, the game is set to generate at a ratio of 0.2/0.8 favouring the capsules, though the generation does use a randomizer and we could have more of one type at any given time.
Sphere type enemy.
At the end of every campaign scene waits a boss. Currently the game only uses one design, but it would be easy to add more just by changing the model used.
Boss type enemy.
The bosses are highly configurable from within the editor, grab them from the prefab folder and drop them in a scene and, configuring a few parameters, like the number of shots, the frequency and the speed of the bullets, and you can have a boss that is easy, or almost impossible, to beat.
At the start of the game, is good to offer a simple interface that entices users to start the game as quick as possible. I opted for a simple interface with three options, play, select level and quit and some special effects, like a point light that moves around the scene to give the impression of time moving.
Main menu at the start of the game.
Secondary menu that lets users play any level on the game.
During the development of this game, I learned that we can use Time.timeScale
as an easy way to pause any game activities that are related with game time, which in this game means all of the activities, effectively giving us a way to pause/restart the game using one line of code.
Time.timeScale = 0;
An overlay is used to let the user decide what to do after the pause: quit the game, resume the game at the point we left it and restart a new game, from the menu page.
Pause overlay.
The game is paused while the screen is visible. In mobile clicking the back button triggers the pause.
The game is structured on progressively harder levels that should let the users get used to all game features as they play. Levels are short by design, and the player can choose levels from the start menu.
The levels are short by design. The aim in to recreate the dynamic of popular games like League of Legends where a play only lasts a few minutes.
A basic introduction to game play. Only enemy capsules and the final boss, both of them with very easy settings. The spawner generates one enemy capsule between 4 and 15 seconds each, and the boss only fires three shots simultaneously every 1 to 3 seconds.
There are some obstacle objects, but none of them is in the player's way.
Level 2 does not introduce any new elements but speeds up the game, starting to hint at the fast-pace dynamic. Spawning interval is set between 2 and 10 seconds and boss fires every 0.5 to 1 second, still only 3 bullets.
Introduces the new enemy type sphere and it speeds up the spawning interval to between 1 and 8 seconds. Boss fires every 0.5 to 1 second, 5 bullets that move a bit faster than on previous levels.
The boss and enemy spawner settings are similar to the previous level, the spawner has a slightly wider generation span along the Y
axis, to make enemies harder to detect, and a slightly lower maxWait
value.
The added element in this level comes from the higher number of asteroids being generated, that help hide enemies from the player's field of vision, giving them less time to react.
Being the final level at the moment, it presents the highest difficulties, spawner generates new enemies one each every 0.2 to 4 seconds, and boss fires 7 bullets every 0.1 to 1 second, the central bullet is aimed towards the player position on fire.
This level is an example of interesting variations that we can generate with the modular architecture that the game components have. By removing the Boss and so the mission element of a level, we can completely change the dynamic and create a melee style level where the objective is to survive as long as possible.
To make survival more complicated, there are two Spawner elements, one at the usual position in front of the player, generating enemies much quicker than during the regular game levels, and one back from the player, generating less enemies, but enough for them having to account for this extra difficulty.
Mayhem is a simple level where we have removed most of its elements but, due to its different nature, is a very fun level to play.
At the start of the development process I decided to have a singleton Game
script, that would act as the game manager and assign it to the UI
game object. This seemed to be what made most sense, besides being some kind of a Unity convention.
But during the development process, I found that having the UI
endure between scenes was giving me more problems than help, and it was adding a lot of boilerplate code. A lot of subscribing to scene load events and unsubscribing, I had to move UI related objects out of the main UI
game object, because they should not outlive their own scene's destruction, and I also found that it was hard to configure the UI objects in a manner that suited the scene, my C# had to check what scene we were on and, based on that, configure a group of objects that kept changing. It started to seem that it would make more sense to not have persistent UI objects and being able to configure them from within the editor.
At the same time, I was looking for ways to persist my player state between scenes, and sharing data between the player and the UI, data that didn't make sense to share, it all started to have code-smell.
At that point, I decided to give a new idea a try, since it seemed that I didn't need most of the UI objects to persist, and instead needed the Player
to be aware about it's state on the game, not only in the current scene, I decided to switch the DontDestroyOnLoad
from the UI to the Player, and created a new PlayerState
class dedicated solely to store, and share, information about the player's current state.
The change paid off immediately, PlayerState
turned out to be very simple, clear and effective, and Game
quickly lost half it's size and two thirds of its complexity, becoming much more manageable and easier to use and modify.
Since we started with a very modular game that takes place in an open world, there are many features we can add:
- Entire campaigns.
- Enemy types.
- Different player weapons.
- Player defenses, like a force field.
- Implementing a save mechanism.
- Implementing an online score mechanism for both points and time survived on the different survival style levels.
And these are just some ideas, the possibilities really seem endless.
The player's space fighter was crafted by Devekros and it is available at this link in the Unity Asset Store.
The built in UI elements come from the Techno Blue GUI Skin asset by 3d.rina from the Unity Asset Store.
The custom text was created following this video tutorial.
Some of the asteroids come from the free Asteroids Pack by Mark Dion on the Unity Asset Store.
Some of the asteroids were created by hand using Unity's Polybrush.
Metal texture from the Yughues Free Metal Materials asset by Nobiax / Yughues.
The menu scene uses the Deep In Space asset by Breitbarth for ambiance music.
The game uses music from the Absolutely Free Music asset by Vertex Studio on the following levels:
- Title page: track no.11.
- Level 1: track no.6.
- Level 2: track no.31.
- Level 3: track no.36.
- Level 4: track no.19.
- Level 5: track no.5.
- Credits page: track no.1.
- Extra Level, Mayhem: track no.32.
Gunshot and laser sounds from the Futuristic Gun Sound FX asset by MGWSoundDesign.
Explosion sounds from the Grenade Sound FX asset by MGWSoundDesign.
Scrolling star background video tutorial.
Cinemachine video tutorial.
Astar project website.
UI using screen camera instead of screen overlay tutorial. Removes clutter while developing.
Nice gradient for menu text on this tutorial.
Using Time.timeScale
to pause the game.
Touch controls tutorial.
Inspiration for the circular light movement from this tutorial.
Video editing tutorials for the demo file from this channel.