16
\$\begingroup\$

I am thinking of writing myself a simple 2D game. It will not shine with perfect graphics or gameplay at first, but I'd consider it my first step in PC game development. So, imagine such simple sprite-based 2D game (like Heroes IV or Startcraft BroodWar).

I want the gameplay to support day/night with the according lighting changes and at the same time it will be madness to have to create sprites for every lighting nuance. So, I decided adding a semi-transparent layer on top of other objects will be enough.

The issue with this solution is if I have a light source object in the game (like the hero wearing a torch, or a burning building), there must be a lighter area around it, right? Since I am putting my semi-transparent layer over everything, how would you suggest to achive the torchligt visual effect I want? Maybe redraw that layer adding 'gaps' or differently colored areas based on the lighting effect?

\$\endgroup\$
5
  • 2
    \$\begingroup\$ Mask may be the way to go \$\endgroup\$ Jan 9, 2012 at 19:27
  • 5
    \$\begingroup\$ "Torchlight" in the title can be confusing due to the game named Torchlight. \$\endgroup\$
    – Tetrad
    Jan 9, 2012 at 19:32
  • 4
    \$\begingroup\$ @Tetrad Should be ok as long as its not capitalized. As for me, Torchlight didn't came to mind at all until reading your comment. \$\endgroup\$ Jan 9, 2012 at 19:37
  • \$\begingroup\$ Nonetheless, I suggested an edit to make title more specific. \$\endgroup\$ Jan 9, 2012 at 19:44
  • 2
    \$\begingroup\$ possible duplicate of How is 2D lighting implemented? \$\endgroup\$
    – bummzack
    Jan 9, 2012 at 21:03

3 Answers 3

12
\$\begingroup\$

I don't know what you're programming in, but this is how I handled it in XNA:

  1. On the draw call, a List<Light> object is created/cleared.
  2. During the tile draw loop, each tile is checked to see if it has any Lights associated with it. If it does, the Light objects are appended to the List<Light>.
  3. The tiles are drawn onto their own RenderTarget2D.
  4. After the tile loop, the list of Lights is iterated through and drawn on their own RenderTarget2D using a texture I made that looks like this:
    Light Texture (Note: I used the R, G and B values here but you should probably use the alpha channel in your actual texture.)
  5. Using a custom shader, I render the tile surface to the screen and pass in the lighting surface as a parameter which gets sampled for the "darkness" value at each pixel.


Now, there's a few things to note:

Regarding Point 4:

I actually have two custom shaders, one to draw the lights to the lighting render target (step 4) and another to draw the tile render target to the screen using the lighting render target (step 5).
The shader used at point 4 allows me to add (what I call) a "luminosity" value. This value is a float that gets multiplied against each pixel in the texture before it's added to the render target so that I can essentially make lights brighter or darker.
At this point, I also take into account the light's "scale" value which means that I can have large or small lights using only one texture.

Regarding Point 5:

Think of the lighting render target as essentially having a value for each pixel from 0 (black) to 1 (white). The shader essentially multiplies that value against the RGB values for a pixel in the tile render target to make the final drawn image.

I also have some more code here where I pass in (to the shader) a value to be used as the day/night overlay colour. This is also multiplied into the RGB values and included in the lighting render target calculations.


Now, this won't allow you to do things like block light from going around objects and whatnot but, at least for my purposes, it's simple and works well.

I've written more detailed blog posts here and here which may help you. I don't have time right now, but if you want I can go into more detail here on gamedev.

Oh, and here's a look at it in my map editor:enter image description here

\$\endgroup\$
8
  • \$\begingroup\$ Generate and clear a list every frame may not be too costly? \$\endgroup\$ Jan 9, 2012 at 21:13
  • \$\begingroup\$ @Gtoknu Doesn't make any noticeable difference in my game. The list is just holding references to objects that already exist in memory so it's not like every light is being recreated or anything, just one list. \$\endgroup\$ Jan 9, 2012 at 21:21
  • \$\begingroup\$ you may be right, not totally, but you're. Of course, you're not creating full objects, but you're adding pointers to references, still a low usage, but still consuming. You're doing in a good way, but i think it may be better if you put the list outside the draw method, since do logic in update is much faster than in draw \$\endgroup\$ Jan 9, 2012 at 21:44
  • \$\begingroup\$ @Gtoknu Sure, you can declare the list wherever you want but the point is that the lights are associated with the tiles. The tile Draw method is where you find out if a tile has lights or not. I don't loop through all drawn tiles in the Update method so I'd be adding extra overhead for that. Also, XNA tries to guarantee that Update will be called 60 times per second so it may sacrifice Draw calls for this, which means that this code would actually get called less often. \$\endgroup\$ Jan 9, 2012 at 21:56
  • \$\begingroup\$ All that said, it's a moot point because I eventually moved the lights into their own lists based on spatial partitioning and just drew all lights in sections that intersect the screen. I didn't talk about that in my post for lack of time, but it's mentioned in the second blog post I linked. \$\endgroup\$ Jan 9, 2012 at 21:56
9
\$\begingroup\$

Normally, lighting in 2D games is done by having a Normal Map for all of your sprites, then calculate the 3D lighting effects on your 2D sprites. This is known loosely as "2.5D". I would however not recommend doing this in your first game as it is complex.

Here's an awesome video of someone who has done this in XNA: http://www.youtube.com/watch?v=-Q6ISVaM5Ww

That said, there are probably ways to cheat and get a pseudo-lighting system that could work with various assumptions.

\$\endgroup\$
2
  • \$\begingroup\$ Already saw this video before, +1 for mentioning such incredible technique. \$\endgroup\$ Jan 9, 2012 at 19:54
  • \$\begingroup\$ Thank you for the advice and the video link. Indeed a map seems to be a solution. I myself would attempt to use the map on my top layer to create a 'gap', or to change the effect it has on the underlying objects. \$\endgroup\$ Jan 10, 2012 at 8:32
5
\$\begingroup\$

It's hard to suggest an approach if you're not completely specific about the effect you're trying to achieve. Details such as if the lights should be obstructed by the environment or not, what's the point of view of your game, to what extent should the light interact with the environment, etc.

I'll drop my two cents though. See if this tutorial by Catalin Zima entitled Dynamic 2D Shadows fits your bill. As you can see, the light has a radius and does not go through obstacles. You could animate the radius and the color a little to make it look closer to a real torch light.

enter image description here

In this case, the light acts as sort of overlay on top of your scene, but does not interact with it to the same extent as in John's example, although it does take obstacles into consideration.

Edit

Catalin links to another article he used as reference, but the link is broken. Here's an updated link.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks, I think this is not the effect I am after, but still thanks for sharing :) \$\endgroup\$ Jan 10, 2012 at 8:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .