- Home /
How can I achieve this particular 2D lighting effect?
Hi all, I am currently working on a 2D game but have ran into an issue with lighting. Here is a screenshot of the game:
As you can see, the character has a point light around him. However, I would like to achieve this effect (edited above image to show what I mean):
The wall, as highlighted in the image, should be blocking the light. Can anyone help with this?
As the light is a 3D one, could you not just put it closer to the player and fiddle with range/intensity [by also adding some actual depth to your wall with an ortho camera]? The effect can't easily be achieved with culling layers etc as it's of course all wall so I think the only way it can be done is to move the light and add depth to your sprites. Where's the light actually located - is it a spotlight co$$anonymous$$g from around where the camera is?
Answer by KulestarUK · Nov 10, 2014 at 11:42 PM
The short answer: You'll need to add some form of depth using box colliders and "popping out" that wall so it's at the very least above the depth your light is at.
Why? Unity is a 3D engine. 2D lights don't exist. Think of using 2D in Unity as drawing a wall on a sheet of paper, and then shining a torch at it. As we all know, the wall won't block the light. So, instead, we need to "pull out" that wall - like placing a block on top of your paper and paint the wall on top of it. This can simply be done by a box collider and move your wall sprite forward. A little like this (viewed in 3D for simplicity):
That's not quite a full solution however! From the looks of it, your character probably heads down towards the bottom of the wall, i.e. this region here:
When this happens, you'll probably want that wall to light up. The simple solution is change the depth of your wall by script. In effect, when your player is down at the bottom of the screen, the wall is flat against the background. Then you can introduce full shadows etc to get some nice lighting effects (if you're after that of course!).
Changing the depth of your wall
To prevent any confusion, "top" and "bottom" refer to the vertical positions on the screen!
Your player probably moves vertically on the screen via the Y axis in 3D space. This means that there will be a particular Y value that the player will be at when their at the top of the screen, and another Y value that they will be at when their at the bottom of the screen, or more specifically, at the bottom part of that wall. These values can be easily found based on the dimensions and position of your wall as a separate object.
So, after separating out your wall as described above, add a script to the wall. This script needs to be able to access your player GameObject, so make sure it has a reference available to that. In that script, do something along these lines (rough C#):
float MinDepth=1f;
// This limits how far "out" of the screen the wall can pop. Max is DepthRange+MinDepth.
float DepthRange=9f;
float WallHeight;
float WallBottom;
void Start(){
// Get the "vertical height" (on the screen) of the wall - assumes a scaled quad with your image on it
WallHeight=transform.localScale.y;
// Find the "bottom" of the wall first. That's the Y coordinate of the very bottom edge of the wall. The walls transform point is in the middle of the wall, so that's why we take half of it's height to get to the bottom.
WallBottom=transform.position.y - WallHeight/2f;
}
void Update(){
// Get the players Y position - that's their vertical position on the screen:
float playerY=ThePlayerGameObject.transform.position.y;
// Next, a simple interpolation. How far "up" the wall the player is as a percentage:
float relativePosition=(playerY-WallBottom)/WallHeight;
// Clip it:
if(relativePosition<0f){
// Beyond the bottom of the wall (right at the bottom of the screen)
relativePosition=0f;
}else if(relativePosition>1f){
// Beyond the top of the wall (not possible in your image, but provided for clarity anyway)
relativePosition=1f;
}
// Map it to our depth values and apply:
transform.position.z=(relativePosition * DepthRange) + MinDepth;
}
This will result in no visual effects to the player. The lighting however reacts correctly - as the player approaches the bottom tip of the wall, it starts gracefully fading in. Make sure you watch what's going on in the scene view to get the best idea of what this script actually does!
Hi there, this method sounds interesting. However, I'm not too sure how to code this. Do you have anything in $$anonymous$$d to point me in the right direction?
Wow, that's amazing. I will definitely try this. Will let you know how I get on!
Any luck with this? If it helped don't forget to accept the answer so anybody else looking for a similar answer can find it :)
Hey $$anonymous$$ulestar. Apologies, yes I did in fact get it working using your method - the $$anonymous$$m I started focusing on other elements immediately afterwards and I simply forgot to let you know! Will mark as answer.
To everyone else looking for something similar, this method described worked the best! But some extra work is required to get it working with multiple light sources.
Answer by Cherno · Nov 10, 2014 at 09:01 PM
I have no experience with 2d programming, but in theory, couldn't you just have everything in the game world (characters, terrain, items etc) in the lowest layer, then put the lighting effect in the layer above that (or, if the effect isn't scripted, just the black image with the hole in the middle), and in the layer above that have black areas where the walls or anything else that blocks light are. Taht way, the black wall areas would always be visible.
Hi there, thank you for replying. I think I understand what you mean. However, just to clarify, when I said the character has a point light around him, I meant an actual 3D point light and not a sprite.
Well, that doesn't matter, does it? As long as there are black areas above everything else (maybe even render it with a seperate camera!) it should work. When you said 2D, I assumed that you meant actual sprite-based 2D, not just a top-down perspective.
Layering wouldn't work in this case as it looks like the player can go around the bottom of the wall (in which case it would need to be lit) - you could change layers when the player gets near that point, but that would result in an ugly lit/not lit transition
Yeah, by layers I didn't mean the Layer system Unity uses for it's objects, but rather a general way of deciding which objects are drawn above or below others. in a strictly 2d sprite-based context. With 3D, of course things would just have their actual y positions in the right order (or be sufficiently tall).
Ah right ok, I read that as Unity layers - in which case our answers are pretty similar :P