JoelLikesPigs

Ludum Dare 45

Nothing's lighting effect explained and tutorial

ludum dare - 6.gif

I've had a few people ask me about how I handle the lighting in my entry Nothing. I get asked wether it's a shader or some other coding magic. So I thought I'd take the time to explain it and procvide the code and explain it the best I can.

This lighting effect is inspired by the one used in Minit and I honestly wanted to just see if I could recreate it. From this is where my visuals in my own entry came from.

minit example.PNG

I'm using GameMaker studio 2 in this tutorial - so for any none gamemaker users I'm not sure how useful this will be for you but maybe you'll find it interesting regardless.

* Lets start things up by first creating a new object*

create event

//we're going to be using surfaces to recreate this effect

//step one create a surface the size of the current room and assign it a name
lighting = surface_create(room_width, room_height);

//next fill the surface with our specified color - in my game I use a slightly blueish black but this can be replaced with any color
surface_set_target(lighting);
sdraw_clear_alpha($21280D, 0);
surface_reset_target();

end step event

//normally most objects make use of a step event, but because we want to ensure the lighting effects are done last we use a step end event

if (surface_exists(lighting)) {  //surfaces can be volatile, so you should always check if they exist before drawing to one

surface_set_target(lighting);


// The next few steps set the dark overlay
draw_set_color($21180D);
draw_set_alpha(1); // here is an optional line that reduces the darkness of the room
draw_rectangle(0, 0, room_width, room_height, 0);

// Setting the blend mode to 'subtract' is what allows us to "cut holes" out the surface we made
gpu_set_blendmode(bm_subtract); 

draw_set_color(c_white); //anything drawn in white will now remove the overlay in that position



//minit lighting system

draw_circle(mouse_x, mouse_y, 100, false);  // here is where the magic happens 




// Reset all of your draw stuff back to normal again
gpu_set_blendmode(bm_normal);
draw_set_alpha(1);
surface_reset_target();


} 
else
{
   // sometimes surfaces can fall apart randomly so here we
   // Create a new one if there were any issues
    lighting = surface_create(room_width, room_height);
    surface_set_target(lighting);
    draw_clear_alpha(c_black, 0);
    surface_reset_target();
}

draw event

// Draw the surface or create a new one if the old one borked out


if (!surface_exists(lighting)) 
{
    lighting = surface_create(room_width, room_height);
}
else 
{
    if (view_current == 0) 
    {
        gpu_set_colorwriteenable(true,true,true,false);
        draw_surface(lighting, 0, 0);
        gpu_set_colorwriteenable(true,true,true,true);
    }
}

ok so what's this do?

Well if you were to take this code and run it right now you wouldn't get that sweet dithering effect you'd see in Minit and instead see this:

ludum dare - t1.gif

Currently its a simple circle - it's a neat effect and I even use it my game to create the pinhole transition used at the beginning, but thats not why we're here. To get the dithering effect we need to be drawing some sprites with the dithering built in. specificially something like these:

lighting sprites example.png These are just 4 simple sprites with varying levels of dithering - with the first sprite being entirely white and the rest getting progressively more transparrent (note: to show this better the transparent pixels in the above are represented with black pixels)

so now we have the sprites, how do we use them?

simply replace the line: drawcircle(mousex, mouse_y, 100, false); // here is where the magic happens with a nested for loop: for(var my = -5; my <= 5; my++) //now the magic really happens { for(var mx = -5; mx <= 5; mx++) {

        var dist = point_distance(mouse_x, mouse_y, mouse_x + mx*16, mouse_y+ my*16);
        {
            if(abs(dist) <= 2*16) draw_sprite(s_lighting, 0, (floor(mouse_x/16) + mx)*16, (floor((mouse_y-24)/16) + my)*16);
            else if(abs(dist) <= 3*16) draw_sprite(s_lighting, 1, (floor(mouse_x/16) + mx)*16, (floor((mouse_y-24)/16) + my)*16);
            else if(abs(dist) <= 4*16) draw_sprite(s_lighting, 2, (floor(mouse_x/16) + mx)*16, (floor((mouse_y-24)/16) + my)*16);
            else if(abs(dist) <= 5*16) draw_sprite(s_lighting, 3, (floor(mouse_x/16) + mx)*16, (floor((mouse_y-24)/16) + my)*16);
            else
            {
            }
        }

    }
    mx = 0;
}

The aboe simply checks if the current position is x amount of tiles away from whatever origin point - in this case the mouse pointer. my tiles are 16 * 16 in size which is why everything is done with a check of 16.

now what we get is: ludum dare - t2.gif

And there we have it a simple effect done simply.

You can add some other things too like having a sinWave effecting the distance to give off a pulsing / flickering effect.

Personally I find this effect only really looks good with a limited color pallet but feel free to try it out in whatever you're working on, try it out and send me some screenshots with with different colors and sprites and if you use it show me on twitter @JoelLikesPigs

Hope that explains that - hit me up with any questions if you're lost on anything.

Ludum Dare 47

Drawing the games I've reviewed

Just for a bit of fun I've started drawing the games I've played and reviewed - If I've commented on your game I've probably also drawn something too.

I Plan on doing this some more this weekend

ldjam.png

I also spent some time redoing my own games (https://ldjam.com/events/ludum-dare/47/4-minute-work-week) title page too:

4MIN WORK WEEK.png