How to create dynamically lit 2d pixel art scenes with Unity
Tutorial: This video shows you how create dynamic light effects for your pixel art scene in Unity using normal maps created with SpriteIlluminator.
Tools used in this tutorial:
Transcript of the video
Imagine a vivid pixel art day in your 2D side-scroller or RPG game. What if night falls on the town? Which atmosphere would you like to create using Unity and its advanced lighting? How would that look like?
Here you can see how the characters reflect Unity's light sources for dark objects in your scene, amid lights like the windows, and how animated lights like flickering lanterns contribute to the atmosphere. All those things you can learn in the coming up 30 minutes to make your game look more appealing.
Sounds good? Let's get started!
For this tutorial, I used the beautiful pixel art called GothicVania Town, which is available for free from the Asset Store.
After importing it into my project, I cut and pasted it to a folder outside my project because I just want to generate sprite sheets out of selected sprites. I also delete all Unity meta files for clarity reasons.
For generating the sprite sheets, I use a software called TexturePacker. By using sprite sheets, you keep the size of your project small and you can also share the same material for multiple sprites in Unity. For TexturePacker, a free trial is available from the download link below.
Now, if you go inside TexturePacker, you can simply drag in all the sprites you want to use in the scene into the software. For now, I will just create a sprite sheet containing buildings, town objects, and two tiles for the ground.
To make the sprite sheets readable by Unity, I also need to import the TexturePacker importer, which is also available for free from the Unity Asset Store.
Inside TexturePacker, I set the path to the Unity project folder where I would like to save the spreadsheets and its corresponding data file. After clicking on the publish spreadsheet button, all the sprites I selected are available from within Unity from a single sprite sheet, and all of the original sprites have kept their name inside the sprite sheets, which makes them easily identifiable.
I quickly adapt the import settings so the sprites appear in clear pixel art style, and now I can directly drag the object from my sprite sheet into the scene. I dragged in the houses just for demonstration purposes, but I deleted them again because I want to start creating our scene by placing ground tiles.
So, I create a new tile map which I call ground map, and I also create a corresponding tile palette which I can use to paint the ground tiles. I call it ground tiles and store it in a new folder under the assets folder.
Now I can drag in my ground tiles from the sprite sheet into this new palette. The tiles need also a folder where we can save them, and now I go on and paint the ground for my Gothic town using the brush tool of the tile palette.
I also need to import the TexturePacker importer, which is also available for free from the Unity Asset Store. Inside TexturePacker, I set the path to the Unity project folder where I would like to save the spreadsheets and its corresponding data file. After clicking on the publish spreadsheet button, all the sprites I selected are available from within Unity from a single sprite sheet, and all of the original sprites have kept their name inside the sprite sheets, which makes them easily identifiable.
I quickly adapt the import settings so the sprites appear and clear pixel art style, and now I can directly drag the object from my sprite sheet into the scene. I dragged in the houses just for demonstration purposes, but I deleted them again because I want to start creating our scene by placing ground tiles.
So, I create a new tile map which I call ground map, and I also create a corresponding tile palette which I can use to paint the ground tiles. I call it ground tiles and store it in a new folder under the assets folder. Now, I can drag in my ground tiles from the sprite sheet into this new palette. The tiles also need a folder where we can save them.
Now I go on and paint the ground for my Gothic town using the brush tool of the tile palette. I continue creating the scene by dragging in houses from the sprite sheet and placing them at a certain distance from each other. Between the houses, I place some objects of different types, and in front of the objects, I place the street lamps at a certain distance again, which will lighten the scene later on.
To duplicate objects, you press simply Ctrl+D. And after I finished placing my objects in the scene, I organize them in a new folder in the hierarchy.
I continued by creating a new spriting sorting layer to order all the objects in the scene. I call it town, and I assign all the objects in my town objects folder to this sorting layer. Then I go on and place my tile map on this town layer with an order and layer of zero, the houses on one, the objects on two, and the street lamps on three, so the street lamps are in front of all the other objects.
The last step is to create the background. I will create a folder in the project window which I call background, where I store the textures I downloaded previously from the asset folder outside the project.
The textures are the middle ground and the background, and I will go on and adapt the import settings in the inspector for both textures so they are displayed in good-looking pixel art. Then I would create a folder in the hierarchy where I store both textures. I call this background too, and drag in one texture after the other.
Now, I arrange the layers on the default sorting layer by placing the middle ground before the background on an order in layer of 1. And after aligning the background behind the middle ground, I scale both textures up by a factor of 2 so the whole scenery fits better to the rest of the town setting.
Then I duplicate both textures so they fill the whole background. You can do this by pressing Control+D and align them by dragging them while holding Ctrl. And because this is a night scene, I want to give the textures a darker tint color, so I select all of them and go to the color wheel in the sprite renderer and I choose a dark violet so we have a nice dark background.
I finished the creation of the scene by saving it inside the scene folder. To make our sprite objects in the scene affected by light sources, we need to do two things.
First, we have to generate a second sprite sheet, a so-called normal map, for all the objects in the scene. We generate a normal map by painting our objects with different shades of red, green, and blue to tell Unity from which angle the light would hit our sprite if it was a 3D object.
If we include this normal map, combined with a sprite sheet in a new material, Unity knows exactly how the scene light affects the usually unlit sprites. There are different solutions for creating normal maps available, but if you wanted quick and easy, I recommend SpriteIlluminator.
You can download the free trial of the software from the link below. The advantage is that we already have all the sprites for our town packed in a single sprite sheet, so we can go on and simply drag in the sprite sheet from our Unity project folder into a new SpriteIlluminator project.
SpriteIlluminator offers you different possibilities to determine how the lights should affect your objects. In the lower left corner, you can change between different views for your sprite map. For seeing how lights would affect your textures, it's best to work in the lit texture view.
I start by selecting the front sides of the houses using the rectangle tool. Holding shift while drawing the next rectangle gives you the opportunity to create multiple selections at once. For the triangle part of the last building, I used the polygon tool to complete the selection. Then I click on angle and select on the ball the angle which corresponds to the direction the lights hit my selection.
I adapt the brush size, opacity, and hardness, and start painting the front side of the houses. After finishing, I check the result in the normal map view and also move the light source in the lit texture view round to visualize the light effect.
Then I continue by selecting the rooftops using the polygon tool and paint them in a different angle and corresponding color. And I proceed in a similar way with the left side of the houses. I check the final result and the normal map display, and I save the project for later usage.
For the ground tiles, the process is very similar: selecting the front size with the rectangle tool and painting it, and after that selecting and painting the upper side in a different color. For the rest of the objects, I choose a quick and easy approach. I select them all using the polygon tool, and then give them an automated normal map visualization. I do this by experimenting a little bit with the bevel tool and its parameters.
After finishing my normal map for all the objects, I select export normal. SpriteIlluminator stores the normal map in the Unity project folder under the same name as the source texture and simply appends an underscore and n
to it, letting Unity know that this is a normal map.
Now we can quickly implement it by creating a new material. Under albedo, we select our main sprite sheet, we use the normal map we just created under the normal map entry, and set the rendering mode to fade.
If we now select all the objects which should be affected by lights, including the tile map, and assign the new material to them, everything is dark. Good night!
Let's change this by adding a point light to our scene and by adapting some of its parameters including the range, blend mode, position, and color. As you can see, in less than 50 minutes, we have created a whole sprite scene which can be illuminated using whatever light source you want.
If you want windows in your town to also emit light from the inside, our material also needs an emission map. This is a third spreadsheet which tells Unity which parts should be colored even if there are no lights.
To create this quickly, please use an image editing program of your choice. My choice here is Krita, and I have created a new file in the size of my texture and dragged in my town sprite sheet from the Unity project folder.
We'll need a second layer which will become our emission map. Now, I select the window parts of the first layer, which is our original sprite sheet, using the magic wand and the rectangle tool while holding down the shift key. Then I fill those selections using a light and saturated yellow-orange, not on the original sprite sheet layer but on the one I just inserted.
After that, I invert the selection and fill the rest with black. I save the Krita project for later usage and export my emission layer to the Unity spreadsheet folder as a PNG file.
We can now select the emission map by checking the emission checkbox in our material and by selecting the emission map we just created. And as you can see, all the dark houses now emit light out of their open windows.
The final and most interesting step for our scene is adding the lights to street lamps and windows.
Let's start by creating a street light folder in the hierarchy, and inside this folder, we create another folder for street lamp number one. In there, I use a combination of point and spot lights. The spot light is for the light which is going directly downwards, while the point light should simulate a more diffused lighting in all kinds of directions.
I cannot tell you what are the best parameters for your specific scene, I just go on and follow my intuition by experimenting with range, color, spot angle, and intensity parameters of the light source. For the spot light, a rotation of approximately 90 degrees around the x-axis is recommended.
Then I'll give both lights a nice saturated yellow-orange color, and I also enhance the intensity of the spot and adjust its range and spot angle. Then I go on and similarly experiment with the point light for spreading some weaker light on all the objects around the street lamp, and that simply it is all ready for the street lamps.
For the window lights, I duplicate a street lamp light, rename it, and drag it to the window's position. I will follow the principle "less is more" here and emit just a decent amount of light from the window.
If you should experience weird effects between the scene lights, make sure that the number of pixel light counts in the quality settings of your project is set high enough. If not, enhance them, but be aware that this setting might not be accessible if you're using the new shader graph materials.
The point light should shine on a small frame around the window, so I reduce its range. Then I play around with its y & z values and give it a higher intensity.
The spotlight should shine from inside the window in a downwards angle on the street, so I adapt the rotation
The final and most interesting step for our scene is adding the lights to street lamps and windows.
Let's start by creating a street light folder in the hierarchy, and inside this folder, we create another folder for street lamp number one. In there, I use a combination of point and spot lights. The spot light is for the light which is going directly downwards, while the point light should simulate a more diffused lighting in all kinds of directions.
I cannot tell you what are the best parameters for your specific scene, I just go on and follow my intuition by experimenting with range, color, spot angle, and intensity parameters of the light source. For the spot light, a rotation of approximately 90 degrees around the x-axis is recommended.
Then I'll give both lights a nice saturated yellow-orange color, and I also enhance the intensity of the spot and adjust its range and spot angle. Then I go on and similarly experiment with the point light for spreading some weaker light on all the objects around the street lamp, and that simply it is all ready for the street lamps.
For the window lights, I duplicate a street lamp light, rename it, and drag it to the window's position. I will follow the principle "less is more" here and emit just a decent amount of light from the window.
If you should experience weird effects between the scene lights, make sure that the number of pixel light counts in the quality settings of your project is set high enough. If not, enhance them, but be aware that this setting might not be accessible if you're using the new shader graph materials.
The point light should shine on a small frame around the window, so I reduce its range. Then I play around with its y & z values and give it a higher intensity.
The spotlight should shine from inside the window in a downwards angle on the street, so I adapt the rotation around the x-axis and move it along the z-axis further in the background.
If I'm satisfied with the parameters for the lights, I try out the effects by moving an object from the scene around. If it's necessary, I either adapt the light source parameters or the z-axis value of my object in the scene.
Now I go on and use my new window light as a template for the other windows in the scene. I duplicate and rename it, and depending on the size and position of the windows, I adapt the lights' range, spot angle, and intensity of the point light first.
Then, I do the same thing with the spotlight to add a direct shine on the building's face or the sidewalk for the tavern. By combining the tint color of the emission map and our Unity light system, we created a realistic atmosphere for the scene.
For the final touch, I would now like to add some small animations to the streetlights. I start by copying the lights from the first street lamp to the other lamps in the town. I'd like to give one of the street lamps a flickering light touch.
First, I select the game object which is parent of the lights and switch to its animation view. Here, I click on create a new animator, which also creates and saves a new animation clip.
I reduced the number of samples to eight in order to slow down the speed of our animation, and I continue by pressing the record button. Now, I can add light effects by changing the intensity properties of the spot and the point light to have starting keyframes for both lights.
Let’s slightly adapt their intensity right at the animation start, then I go on and copy those keys to, let's say, frame 15 and 16, and in frame 16, I reduce their intensity to 0-2 to completely turn the lamp off.
Next, I copy keyframes to frame 20 and enhance the light intensity a lot, just to reduce it again to barely visible in frame 23. By copying the keys from 23 to 35, the light stays dark, and in frames 36, I copy the frames from the start to set everything back to normal in the animation's last frame.
And if we now hit play, you will see that the streetlamp flickers in a nice way and looks somehow broken.
You can now simply continue by using this principle in order to animate the tavern window and all the other lights in your town.