Outline | Previous: Intro | Next: Platforms
Create a project, import assets, and set some configurations.
- 1.1) Start a Project
- 1.2) Import Assets
- 1.3) Configure Sprites
- 1.4) Select an Aspect Ratio
- 1.5) Configure Camera
- 1.6) Auto Save Script
Start a 2D project and save the scene.
How
Download the software:
- Download Visual Studio Community 2017.
- There is an optional workload for 'Game development with Unity' which you should include.
- Download Unity 2017.
- The free Personal edition has everything you need.
- You may be prompted to register / sign in.
Start a new project:
- Open Unity and create a project:
- Select '2D' when creating a new project.
- Enter a name/directory - the other options can be left at defaults.
Save the current scene:
- File -> 'Save Scenes'.
- Save it as Assets/Scenes/Level1.
New to Unity? Familiarize yourself with the UI.
Here is an article from Unity with an overview of the editor. https://docs.unity3d.com/Manual/LearningtheInterface.html
What are the limitations of Personal / Community editions?
Almost none.
Unity Personal edition includes all the same engine features and performance that the Pro edition does. There are a few minor differences; for example with the Personal edition you cannot set your own splash screen. You can always upgrade later.
VS Community edition is very similar. Everything we will need is included for free with the Community edition.
Both Unity and Visual Studio have a clause in the agreement which says you must upgrade when you / your company starts to make enough money. Unity requires a paid upgrade once you make $100,000 per year, and Visual Studio once you make $1,000,000 per year.
Can I use Unity 5.*, Monodevelop, or older versions of VS instead?
No, Unity 5.* will not work with this tutorial. We will be using the Timeline Editor which was not fully released until 2017. Because of this, you may get stuck on those sections, but the rest of the tutorial should work correctly.
Yes, you can use Monodevelop, any version of Visual Studio, or any other IDE.
What does 2D (vs 3D) impact in Unity?
Presenting the 2D vs 3D option when you create a new project suggests that this is a significant choice. It's not, really... 2D just changes default settings on things like your camera. Unity is a 3D engine; when creating 2D games, you're actually creating a 3D world where everything is very flat, but the camera looks straight ahead and the only rotation in the world is around the z axis.
What's a scene?
The Scene represents a collection of GameObjects and components (defined below) configured for a game level or menu screen. For this tutorial, we are starting by creating part of Level 1. Level 2, the menu, and other UI screens will be saved as separate scenes. You can switch scenes via the SceneManager. We will cover this later in the tutorial.
Can I name folders differently?
Absolutely. Anywhere we mention a folder you can name it as you please, with one exception; "Editor", which is a special folder name with Unity. Anything under that directory is run only when testing in the Unity editor.
More about special folder names from Unity.
YouTube | Source before | Source after
Import sprites and sounds into your project.
How
- Download all the assets for this tutorial.
- Create directory Assets/Art:
- In the Project window, right click in the Assets directory and select New Folder.
- You can use F2 to rename.
- Drag/drop all the assets (images and sounds) into the folder you just created.
- If you have a zip file, you may need to unzip to a temp directory before drag/drop will work.
What's a sprite / sprite sheet?
A sprite is an image used in 2D games and for UI. They may represent an object, part of an object, or a frame of an entity's animation, etc.
A sprite sheet is a single image file that contains multiple individual sprites. The sheet may use these sprites to represent different frames for an animation or to hold a collection of various object types (as is the case here).
Who made the art for this tutorial?
We are using:
- Kenney.nl's Platformer Characters 1
- Kenney.nl's Platformer Pack Redux
- Kenney.nl's Jumper Pack
- Kenney.nl's Kenny Fonts
- Kenney.nl's Digital Audio
- BoxCat Games's Epic Song
- ExplosiveJames made the Hammer
Can I use my own art?
Of course. This tutorial only assumes that you are using sprites. You can use any art you'd like in the game.
For sounds, we don't have many - just enough to introduce how they may be added to a game. Unity supports many formats that you could use, including wav and mp3 you could use.
Can I use sprite sheets?
Yes, but Unity occasionally has render issues while using sprite sheets. Sprite sheets are an optimization technique that games use. Unity has a sprite packer feature that can be used to automatically create sprite sheets. Once you are in the optimization phase of your project, you could look into the sprite packer to try and gain anything that might have been lost from using individual sprites instead.
Can I use Vectors?
No. Unity does not support vector graphics out of the box. You could look in the Asset Store for a third party solution.
YouTube | Source before | Source after
Update each sprite to use a full rect mesh and point filter mode.
How
Select all the sprites:
- Search by Type: Texture (not sprite!)
- Click on one and use Ctrl+A to select all.
Update import settings:
- In the Inspector, set:
- Mesh Type: Full Rect
- Set Filter Mode: Point (no filter)
- 'Apply' changes.
What's mesh type / full rect?
When a sprite is rendered to the screen, a combination of a mesh (like that used for 3D objects) outlining the sprite and transparency is used to draw the picture on-screen. Tradeoffs here are beyond the scope of this tutorial.
- Tight will attempt to better outline the sprite, using more polygons in the mesh.
- Full Rect will use 2 triangles per sprite.
When using tiling on a sprite, Unity recommends updating the sprite sheet to use 'Full Rect'. I don't have an example of issues that may arise from using 'Tight' instead, but here is the warning from Unity recommending 'Full Rect':
What's filter mode / point?
Using Point filter mode gets us closer to pixel-perfect sprites and prevents some visual glitches.
Filter mode of Bilinear or Trilinear blurs the image a bit in attempt to make smooth lines. Often for a 2D game, we want control down to the pixel and this effect is not desirable. Here's an example with the character sprite we will be using:
For sprite sheets, each object is often touching the one next to it. Filter Mode Point prevents blending between one sprite and its neighbor. The blending that occurs with other modes besides Point may lead to random lines showing up on-screen. For example:
YouTube | Source before | Source after
Change the aspect ratio to 5:4 in the Game window and build settings.
How
Game window:
- In the Game window:
- Change 'Free Aspect' to 5:4.
Build settings:
- Open menu File -> Build Settings.
- Select the desired platform and click 'Player Settings'.
- In the Inspector:
- Set the supported resolution or aspect ratio.
PC:
Web GL:
Why use a fixed aspect ratio?
We are building a game with a fixed display. The camera is not going to follow the character, which will simplify the game and level design for this tutorial. With a fixed aspect ratio, we can design a scene without any camera movement and be sure everyone has the same experience.
The white box here represents the area that players will see:
Different resolutions will scale the display larger or smaller, but everyone will see the same amount of the world.
5:4 was an arbitrary choice; use anything you'd like.
YouTube | Source before | Source after
Update the camera size and background color.
How
- In the Hierarchy window:
- Select the 'Main Camera'.
- In the Inspector:
- Set Size: 10
- Change the 'Background' color to black.
What's camera 'Size'?
By default, 2D games use 'Projection: Orthographic'. This means that the camera does not consider perspective, which is the ability to see more of the world the further it is from your eye.
For an Orthographic camera, the amount of the world visible is driven by a special 'Size' property. 'Size' defines how much of the world is visible vertically. Then the aspect ratio is used to determine how much to display horizontally.
The amount of the world visible with a perspective camera, as used in 3D, is driven by its position.
We used size to zoom out so that more of the world is visible on-screen. In the Scene, the white box representing the viewable area has grown.
YouTube | Source before | Source after
Create an editor script which automatically saves every time you hit 'play'.
How
- In the Project window Assets folder:
- Right click Create -> New Folder
- Name it Code
- Create folder Assets/Code/Editor.
- In the Assets/Code/Editor directory:
- Select Create -> C# Script
- Name it AutoSave
- Double click to open the file in Visual Studio.
- Paste in the the following source code:
- Or view the full version with comments.
using UnityEditor;
using UnityEditor.SceneManagement;
[InitializeOnLoad]
public class AutoSave
{
static AutoSave()
{
EditorApplication.playmodeStateChanged
+= OnPlaymodeStateChanged;
}
static void OnPlaymodeStateChanged()
{
if(EditorApplication.isPlaying == false)
{
EditorSceneManager.SaveOpenScenes();
}
}
}
Explain the code
'using' clauses at the top of a file brings APIs into scope. Used for:
- UnityEditor.EditorApplication (so we can simply write EditorApplication)
- UnityEditor.InitializeOnLoad
- UnityEditor.SceneManagement.EditorSceneManager
using UnityEditor;
using UnityEditor.SceneManagement;
This is a Unity-specific, editor-only attribute that enables the script. The static constructor of any class with this attribute is executed before anything else in the game.
[InitializeOnLoad]
The name of this class is irrelevant, as we will never access it from another class. The only entry point is the static constructor via InitializeOnLoad.
public is optional here. Used for consistency.
public class AutoSave
{
This is a static constructor which will be called before the game begins c/o InitializeOnLoad.
static AutoSave()
{
Here we register for a Unity event which will call the method below when the game begins while in the Unity editor. This event also fires when the game is paused or stopped.
We don't need to deregister the event. Occasionally, Unity will terminate and restart this script, but otherwise we always want AutoSave to be running.
EditorApplication.playmodeStateChanged
+= OnPlaymodeStateChanged;
}
This method is called by the event registered above when the game is played, paused, or stopped.
static void OnPlaymodeStateChanged()
{
Since this event is called for more than just when play begins, we attempt to skip saving when it's clear it's not needed. Changes made while in play mode are not saved, so we know that save happens when play begins; therefore, we don't need to try when pausing or stopping.
This script would work without this if statement.
if(EditorApplication.isPlaying == false)
{
This is the Unity API for saving.
EditorSceneManager.SaveOpenScenes();
}
}
}
How do I know it's working?
AutoSave is a script which will only run while testing in the Unity Editor. Every time you hit play, the scene and project will save just before play begins.
You can confirm the save is working by noting the * in Unity's title. This * indicates unsaved changes and should now go away every time you click play.
Why is the 'Editor' folder name important?
Unity uses special folder names to drive certain capabilities. Any script under a folder named "Editor" will only run while testing in the Unity editor (vs in your built game).
Read more from Unity.
What's InitializeOnLoad?
InitializeOnLoad is a Unity-specific attribute that enables the script. The static constructor of any class with this attribute is executed before anything else in the game.
InitializeOnLoad is an editor-only script and is found under the UnityEditor namespace.
What's a C# attribute?
Attributes in C# are metadata added to classes, fields, or methods that may be queried by other classes. In the AutoSave script, InitializeOnLoad is used to ensure the static constructor on our AutoSave class is called when the game begins.
There are many standard C# attributes and Unity-specific attributes that may be used. Here are examples of several attributes you might use:
using UnityEngine;
using UnityEngine.Networking;
// Tells unity that this component only works
// if the GameObject also has a SpriteRenderer
[RequireComponent(typeof(SpriteRenderer))]
public class MyClassName : MonoBehaviour
{
// Tells unity this field can be modified
// in the inspector
[SerializeField]
// Limits the values you can enter
// in the inspector
[Range(1, 10)]
int count;
// Used for multiplayer games to sync
// method calls
[ClientRpc]
void MyMethod() { }
}
What's a C# static constructor?
A static constructor is a private static method named the same as the class, with no parameters and no return type.
Every object in C# may include a static constructor. This applies to static and non-static classes. A static constructor is guaranteed to be called once (and only once). The constructor will run before the first object is instantiated, a field is accessed, or a method is called (i.e., it happens before you touch the class). You never call the static constructor directly.
public class MyClassName
{
static MyClassName()
{
// This is executed once automatically, before we do
// anything else with MyClassName.
}
}
What's a C# delegate / event?
A delegate in C# is an object representing method(s) to call at a later time. You may encounter delegates under the following names: Events, Action, Func, and delegate. Under the hood, these are all implemented with a 'multicast delegate'.
When a method is added to a delegate to be called later, this is referred to as 'subscribing'. 'Multicast delegate' means that any number of methods may subscribe to the same delegate. We use += when subscribing so as not to overwrite any other subscribers.
EditorApplication.playmodeStateChanged += OnPlaymodeStateChanged;
If the owner of the delegate (EditorApplication in the example above) may outlive the subscriber, the subscriber should unsubscribe when it's destroyed. Also, any time you are no longer interested in future updates, unsubscribe. We do this with -= to remove our method and leave any remaining methods subscribed.
EditorApplication.playmodeStateChanged -= OnPlaymodeStateChanged;
Events are a common use case for delegates. For example, you may have a GameManager with a field for Points include an event "onPointsChange". Other components/systems in the game, such as Achievements and the UI, may subscribe to the onPointsChange event. When a player earns points, a method in Achievements is then called which can consider awarding a high score achievement, and a method in the UI is called to refresh what the player sees on-screen. This way, those components only need to refresh when something has changed as opposed to checking the current state each frame.
using System;
using UnityEngine;
public static class GameManager
{
public static event Action onPointsChange;
static int _points;
public static int points
{
get
{
return _points;
}
set
{
_points = value;
if(onPointsChange != null)
{
onPointsChange();
}
}
}
}
public class MyCustomComponent : MonoBehaviour
{
protected void Awake()
{
GameManager.onPointsChange
+= GameManager_onPointsChange;
}
protected void OnDestroy()
{
GameManager.onPointsChange
-= GameManager_onPointsChange;
}
void GameManager_onPointsChange()
{
// React to points changing
}
}
What about performance?
As an editor script, this logic is not included in the game you release. Saving is incremental, so there is very little time wasted when there is nothing new to save. Unless you're one of the lucky ones who never sees Unity crash, this script is absolutely worth the time tradeoff.
Testing / debugging tips
- Drag / drop a sprite into the scene:
- Zoom in to get a good look.
- Try changing settings, such as changing the filter mode, to see what is impacted.
- When finished, select the GameObject that was created in the Hierarchy window and hit Delete.
- Aspect Ratio may need to be set again later.
- Aspect Ratio is an editor setting. Certain events will cause the aspect ratio to reset, such as testing another project. Just note that this may happen, and change the aspect ratio back if it does.
- You could add more assets. We are especially light on sounds.
- You do not need to follow our guide exactly. Use your own art and sounds. When we start implementing mechanics, you can deviate there as well to create something unique.
How to setup a GIT repository
You may want to use GIT for your project's version control. I recommend this even if you are working alone, as when something goes wrong - and it will - you can diff against previous versions to help narrow down the issue.
Github is free for public open-source projects like the code used in this tutorial. Other services work basically the same, including Gitlab, which I use for free private repositories.
- On Github's website, click the plus to create a new Repository.
- Select Unity for the gitignore file.
Once the project is created, hit the Clone button to get the repository's URL.
- Install GIT
- You can use the command line tools or a GUI such GitExtensions.
The following steps are written for the command line, but if using a GUI just look for the same keywords.
- Open command prompt
- Change directory to your project
- Run the following commands:
git init
git remote add origin <Your repository's URL>
git pull
git push --set-upstream origin master
Now anytime you want to check in changes, run the following:
git add .
git commit
git push
Questions, issues, or suggestions? Please use the YouTube comments for the best fit section.
Support on Patreon, with Paypal, or by subscribing on Twitch (free with Amazon Prime).
License. Created live at twitch.tv/HardlyDifficult August 2017.