Asset - Scripts Fast Lights

Buff

Member
An efficient surface based 2d lighting and shadow system packed with features and optimizations.

Fast Lights 2 for GMS2 has been released! Check it out here.

Marketplace link.
Download the example executable.
Download the example project.
I tried to show off nearly all the features so you can see how to use them.

Should work on all platforms.
Successfully tested on Windows, Android, and HTML5.

Shadow casters can be defined by a sprite, an arbitrary set of points, or a path.

Lights can be any gradient sprite, I made mine in GIMP by drawing a shape, adding a white to black gradient and then blurring it with a gaussian blur.

Works well with physics objects and is designed with views in mind.
The light map surface only takes up the visible part of the screen and lights are not drawn on the outside of it.

There is a quality setting that allows the light map to take up less texture memory by reducing the size of the surface and scaling it up when drawing, greatly improving the performance, particularly on mobile devices. The surface can be blurred to improve the look of the shadows.

The system is object oriented, heavily relying on instance variables so there can only be one light per object and only one caster per object.

Free to use in your project, even for commercial games.

Docs:
light_init(width, height, light_size, ambient_color, quality, iterations)
Sets up lighting variables.
width: Width of the shadow map.
height: Height of the shadow map.
light_size: The size of the lightmaps, should be set to the largest light.
color: The shadow color, this also controls the darkness of the shadows.
quality: The lightmap quality, 0 to 1.
iterations: How many times a second to draw the lightmap. The default is the room speed but you might want to reduce this for increased performance. If it is too small, however, you will notice considerable stuttering.

light_draw(x, y, additive, shadows, blur_amount)
Draws the lights and shadows. It makse sense to only draw the surface at 0, 0 if you are not using view or view_xview, view_yview if you are.
x: The x location to draw the shadow map.
y: The y location to draw the shadow map.
additive: Whether to use additive blending or not.
shadows: Whether or not to draw shadows.
blur_amount: How blurry to make the shadows, numbers from 0 to 1 are good but you can go higher for more blurriness.
Blurry shadows generally makes a lower quality lightmap look nicer without the extra memory required for a full quality lightmap.
Important note: The object calling this function should have a lower depth than the casters.

light_create(light_sprite, light_scale, light_color, light_alpha, light_angle, shadows)
Creates a light and the local variables for it.
sprite: Gradient sprite to use.
scale: Scale of the sprite.
color: The light color.
alpha: Brightness of the light, from 0 to 1.
shadows: If this light should draw shadows.
Notice: An instance can only have one light attached to it.

light_destroy()
Must be called when a light is destroyed.

light_create_caster()
Creates a caster from the size of the object's sprite.
Notice: An instance can only have one caster attached to it.

light_create_caster_from_points(points_x, points_y)
Creates a caster from two lists of points.
points_x: ds_list of relative x positions.
points_y: ds_list of relative y positions.
Notice: An instance can only have one caster attached to it.

light_create_caster_from_path(path, xorigin, yorigin, xoffset, yoffset)
Creates a caster using a path, if -1 is passed the bounding box of the sprite will be used.
path: The path to use.
xorigin: The relative x point on the sprite where the first point on the path goes.
yorigin: The relative y point on the sprite where the first point on the path goes.
xoffset: The sprite's xoffset, allows you to use a different sprite offset.
yoffset: The sprite's yoffset.
Notice: An instance can only have one caster attached to it.

light_destroy_caster()
Destroys a caster

light_set_size(size)
Sets the maximum size of all lights and resizes the lighting surface after it has already been initalized.
size: Size of the square surface in pixels.

light_set_quality(quality)
Sets the light quality after the lights have been initialized.
quality: The quality of the lighting surfaces, from 0 to 1.

light_set_color(ambient_color)
Sets the ambient color, this also controls the brightness.
ambient_color: Color to make the shadows.

light_set_alpha(alpha)
Sets the brightness of a light.
alpha: From 0 to 1.

light_set_color(color)
Sets the color of a light.
color: The color of the light.

light_set_rotation(angle)
Sets the rotation of a light.
angle: Rotation in degrees.

light_set_sprite(sprite)
Sets the light to a new sprite.
If this sprite is larger than the sprite that the light was created with then it will be cut off.
If you need to change the size of a light you should destroy it and create a new one.
sprite: The sprite index to assign to this light.

light_set_size(size)
This will scale the light sprite.
Notice that this will not change the size of the surface the light is drawn on, only the sprite itself.
size: Scale of the light sprite.

light_draw_sprite_dark()
Draws the sprite in darkness.
Useful for objects that should be in shadow.
Use global._light_dark_color to do this manually.

draw_surface_blurred(surface, x, y, xscale, yscale, blur_amount)
Draw a surface with a blur.
x: The x location to draw the surface.
y: The y location to draw the surface.
xscale: The x scale of the surface.
yscale: The y scale of the surface.
blur_amount: How blurry the surface should be, numbers from 0 to 1 work well but even higher number can be used for increased blur.

Edit: Turns out it does work in HTML5, my demo just doesn't because of physics reasons.
 
Last edited:
D

Drasnus

Guest
This is very impressive, especially for a free asset, and you weren't kidding about it being optimized.

I'll be sure to play around with this sometime in a future project.
 
D

Dijkstra

Guest
Hello, Buff!
Is it any possibility to move shadows under the objects?
 

Buff

Member
Hello, Buff!
Is it any possibility to move shadows under the objects?
I chose not to implement this directly because it wasn't effect I was looking for, and is also pretty difficult to achieve. Nonetheless there are a couple of things you can try to get this effect.

If you set the depth of the casters higher than the shadow drawing object then they will be drawn on top of the shadows.

Unfortunately the casters are always illuminated in this case.

I imagine the effect that you are looking for is that the casters are only illuminated when a light is over them. Lights that don't have shadows (when the shadow parameter in light_create is false) do this. You can make a make a caster have a shadow and be illuminated by making two lights at the same position, a light with shadows and a light without.

This will cast a shadow and illuminate the object. The shadow will get blown out by the no shadow light so you might want to reduce its alpha a bit.

Hope this helps.
 
D

Dijkstra

Guest
I chose not to implement this directly because it wasn't effect I was looking for, and is also pretty difficult to achieve. Nonetheless there are a couple of things you can try to get this effect.

If you set the depth of the casters higher than the shadow drawing object then they will be drawn on top of the shadows.

Unfortunately the casters are always illuminated in this case.

I imagine the effect that you are looking for is that the casters are only illuminated when a light is over them. Lights that don't have shadows (when the shadow parameter in light_create is false) do this. You can make a make a caster have a shadow and be illuminated by making two lights at the same position, a light with shadows and a light without.

This will cast a shadow and illuminate the object. The shadow will get blown out by the no shadow light so you might want to reduce its alpha a bit.

Hope this helps.
Will it influence on performance?
Thank you alot, i'll try it in my project anyway.
 
D

Dijkstra

Guest
It will be an extra draw call per light but there shouldn't be a noticeable drop in performance just from that.
Buff, can you explain how to change lights depth, please? Just some points for me to follow:potato:
I tried it in step event, but nothing happens, also i tried object_set_depth - it's nothing to see here too.
What am I doing wrong?
 

Buff

Member
Buff, can you explain how to change lights depth, please? Just some points for me to follow:potato:
I tried it in step event, but nothing happens, also i tried object_set_depth - it's nothing to see here too.
What am I doing wrong?
You don't change the depth of the lights, you change the depth of the object that calls light_draw.
In my example this object is "main". To change the depth you can just set it in the object properties, or set the "depth" variable. Right now it is -100, but if you set it to 100, which is higher than the casters (which all have a depth of 0), then the shadows will be drawn underneath them.
 
D

Dijkstra

Guest
I imagine the effect that you are looking for is that the casters are only illuminated when a light is over them. Lights that don't have shadows (when the shadow parameter in light_create is false) do this. You can make a make a caster have a shadow and be illuminated by making two lights at the same position, a light with shadows and a light without.

This will cast a shadow and illuminate the object. The shadow will get blown out by the no shadow light so you might want to reduce its alpha a bit.
Hello! It's me again)
How you get this? What parameters did you used?
I tried to achieve the same effect as you had on screenshot, but it looks not so cool.
Here's my GIF: https://drive.google.com/file/d/0BzoWUCLfHlo1YWNVXzlnNUgwNEE/view?usp=sharing
 

Buff

Member

DukeSoft

Member
Neat! I'll play around with this for sure, I wonder how it compares to the Glare Engine. I feel like this might be a lot faster and versatile... I'll let you know :)
 
C

CannedSmeef

Guest
Hey, attempting to use this, but when I'm trying to add a light to the map, I get an error:

Push :: Execution Error - Variable Get -5._lights(100028, -2147483648)
at gml_Script_light_create (line 23) - ds_list_add(global._lights, id)
############################################################################################
--------------------------------------------------------------------------------------------
stack frame is
gml_Script_light_create (line 23)
called from - gml_Object_obj_player_CreateEvent_1 (line 4) - light_create(sp_light_spot, 1, c_white, 0.5, 0, true);
 

Buff

Member
Hey, attempting to use this, but when I'm trying to add a light to the map, I get an error:

Push :: Execution Error - Variable Get -5._lights(100028, -2147483648)
at gml_Script_light_create (line 23) - ds_list_add(global._lights, id)
############################################################################################
--------------------------------------------------------------------------------------------
stack frame is
gml_Script_light_create (line 23)
called from - gml_Object_obj_player_CreateEvent_1 (line 4) - light_create(sp_light_spot, 1, c_white, 0.5, 0, true);
Did you first initialize the lighting engine with light_init?
 
C

CannedSmeef

Guest
Hot damn that was a fast reply :D

Yes, I initialized the engine in an object called obj_shadows in the create event.

BTW, it works if I don't define a new light source, it just makes an error if I do
 
Last edited by a moderator:

Buff

Member
Hot damn that was a fast reply :D

Yes, I initialized the engine in an object called obj_shadows in the create event.
You caught me at a good time, unfortunately this is atypical.

Make sure that obj_shadows is being created before your lights. This can be tricky to notice sometimes but inside your room under the Settings tab is an "Instance Order" button that shows the list of instances in your room and the order they will be created in. Push obj_shadows all the way to the top, with the order buttons on the right, so that it is above the light objects.
 
C

CannedSmeef

Guest
Ah, thank you!

Smashing extension BTW, I don't think I've seen one as good as this before :D
 
T

TesloStep

Guest
Hello. After importing this asset i start struggling with lights that periodically stop working, while still in the POV.
so, this part of light_draw(), that calculates some sort of rectangle around light, seems be broken :

Code:
            _light_left = x - sprite_get_xoffset(_light_sprite)
            _light_top = y - sprite_get_yoffset(_light_sprite)
            _light_right = x - sprite_get_xoffset(_light_sprite) + _light_width * _light_scale
            _light_bottom = y - sprite_get_yoffset(_light_sprite) + _light_height * _light_scale
            var _light_inside = rectangle_in_rectangle(_light_left, _light_top, _light_right, _light_bottom,
                argument0, argument1, argument0 + global._light_width, argument1 + global._light_height) > 0
replaced it then with
Code:
            _light_left = x - sprite_get_xoffset(_light_sprite) * _light_scale
            _light_top = y - sprite_get_yoffset(_light_sprite) * _light_scale
            _light_right = x + (-sprite_get_xoffset(_light_sprite) + sprite_get_width(_light_sprite)) * _light_scale
            _light_bottom = y + (-sprite_get_yoffset(_light_sprite) + sprite_get_height(_light_sprite)) * _light_scale
            var _light_inside = rectangle_in_rectangle(_light_left, _light_top, _light_right, _light_bottom,
                argument0, argument1, argument0 + global._light_width, argument1 + global._light_height) > 0
and thing start working properly.

P.S. seems it was mostly catastrophic when 0 < _light_scale < 1

UPD: yeah, because in the light_create() you already multiplied by _light_scale :
Code:
_light_width = sprite_get_width(_light_sprite) * _light_scale
_light_height = sprite_get_height(_light_sprite) * _light_scale
 
Last edited by a moderator:

Buff

Member
Hello. After importing this asset i start struggling with lights that periodically stop working, while still in the POV.
so, this part of light_draw(), that calculates some sort of rectangle around light, seems be broken :

Code:
            _light_left = x - sprite_get_xoffset(_light_sprite)
            _light_top = y - sprite_get_yoffset(_light_sprite)
            _light_right = x - sprite_get_xoffset(_light_sprite) + _light_width * _light_scale
            _light_bottom = y - sprite_get_yoffset(_light_sprite) + _light_height * _light_scale
            var _light_inside = rectangle_in_rectangle(_light_left, _light_top, _light_right, _light_bottom,
                argument0, argument1, argument0 + global._light_width, argument1 + global._light_height) > 0
replaced it then with
Code:
            _light_left = x - sprite_get_xoffset(_light_sprite) * _light_scale
            _light_top = y - sprite_get_yoffset(_light_sprite) * _light_scale
            _light_right = x + (-sprite_get_xoffset(_light_sprite) + sprite_get_width(_light_sprite)) * _light_scale
            _light_bottom = y + (-sprite_get_yoffset(_light_sprite) + sprite_get_height(_light_sprite)) * _light_scale
            var _light_inside = rectangle_in_rectangle(_light_left, _light_top, _light_right, _light_bottom,
                argument0, argument1, argument0 + global._light_width, argument1 + global._light_height) > 0
and thing start working properly.

P.S. seems it was mostly catastrophic when 0 < _light_scale < 1

UPD: yeah, because in the light_create() you already multiplied by _light_scale :
Code:
_light_width = sprite_get_width(_light_sprite) * _light_scale
_light_height = sprite_get_height(_light_sprite) * _light_scale
Ah, it looks like you are correct, I must have missed that bit. I'm glad you were able to figure it out and I appreciate the input. I will update the package later.
 
T

TesloStep

Guest
Hello. Can you helps figured out, how to do something like star sky on the background, that sparkles even in the dark? i'm thinking about of huge bunch of very small lights, everyone represents a single star, but that seems a dangerous lack of resources, and also lights things that supposed to be in dark. Anything else?
 

Buff

Member
Hello. Can you helps figured out, how to do something like star sky on the background, that sparkles even in the dark? i'm thinking about of huge bunch of very small lights, everyone represents a single star, but that seems a dangerous lack of resources, and also lights things that supposed to be in dark. Anything else?
I wouldn't use lights at all. You can create a bunch of star instances that have a white sprite and set the depth of the star object to be less than that of the shadows that way they are drawn on top of the shadows.
 
T

TesloStep

Guest
You can create a bunch of star instances that have a white sprite and set the depth of the star object to be less than that of the shadows that way they are drawn on top of the shadows.
Then they will drawn over other objects, but they supposed to be on background.
 

Buff

Member
Then they will drawn over other objects, but they supposed to be on background.
Oh right. I see your plight. Well if your stars are only a few pixels large you could put some logic in them that makes them disappear if they collide with an object. This seems like a roundabout way of accomplishing your task but unfortunately there is no way of brightening up the shadow map without using a light. If you make the lights small enough you probably won't be able to notice the shadows they cast, or you could make a special light that shadows the objects but doesn't cast shadows. Creating a light for a lot of stars would be pretty computationally expensive though.
 
N

NeutralD

Guest
Hi,

Thanks for the asset! Quick question: is there any way to set the maximum length of a shadow?
 
Last edited by a moderator:

Buff

Member
Hi,

Thanks for the asset! Quick question: is there any way to set the maximum length of a shadow?
Hey, glad you're enjoying Fast Lights.

There's actually is a way to do this, there is a local variable used internally called _light_max_dist which sets the shadow length of a light. It is first initialized in the light_create function and set to the size of the light like so:
Code:
_light_max_dist = max(sprite_get_width(_light_sprite) * _light_scale, sprite_get_height(_light_sprite) * _light_scale)
You could set it to whatever you want after calling light_create and the shadows will be that length.
To test this I set _light_max_dist for light_mouse to 100 and the shadows stay that size:
upload_2017-5-1_20-48-49.png

Notice that _light_max_dist is set back to the default in light_set_sprite and light_set_scale, so if you use these functions you'll have to set _light_max_dist back to what you want.
 
Last edited:
G

gnemnij

Guest
Hi,
I tried EVERY extensions in the market trying to find one extension that can make shadow system in a split screen multiplayer game. All of them have their own reasons for me to give up on them and Fast Light seems to be my only chance now.

I duplicated the init_light and draw_light into two new scripts, thus two sets of global variables and created two "main" to controll them respectively, for my two players. I thought I can do this to draw two seperate surfaces for my two players so they can see the shadow they made seperately by using "view_current" stuff, but practically it doesnt work.

I would like to ask what would you do if you want to use this extension to create a split screen game, which players will have their own shadows? Thanks a LOT!
 

Attachments

Buff

Member
Hi,
I tried EVERY extensions in the market trying to find one extension that can make shadow system in a split screen multiplayer game. All of them have their own reasons for me to give up on them and Fast Light seems to be my only chance now.

I duplicated the init_light and draw_light into two new scripts, thus two sets of global variables and created two "main" to controll them respectively, for my two players. I thought I can do this to draw two seperate surfaces for my two players so they can see the shadow they made seperately by using "view_current" stuff, but practically it doesnt work.

I would like to ask what would you do if you want to use this extension to create a split screen game, which players will have their own shadows? Thanks a LOT!
Hello gnemnij,

Yes this is possible with a little bit of effort.
upload_2017-6-21_0-23-58.png

You had the right idea with creating multiple light maps but it wasn't necessary to duplicate the scripts. I'll explain what I did here.

First I added a new argument to light_init called num:
Code:
///light_init(width, height, light_size, ambient_color, quality, iterations, num)
/*
Sets up lighting variables.
width: Width of the shadow map.
height: Height of the shadow map.
light_size: The size of the lightmaps, should be set to the largest light.
color: The shadow color, this also controls the darkness of the shadows.
quality: The lightmap quality, 0 to 1.
iterations: How many times a second to draw the lightmap. The default is the room speed
            but you might want to reduce this for increased performance. If it is too
            small, however, you will notice considerable stuttering.
num: The number of light maps to create, useful for split screen views.
*/
And made global._light_map and array:
Code:
...

for (var i = 0; i < argument6; i++)
{
    global._light_map[i] = surface_create(global._light_width * global._light_quality, global._light_height * global._light_quality)
}

...
Now we can create multiple surfaces to draw on like so:
Code:
light_init(view_wview, view_hview, 1024, color, 0.25, 30, 2)
I then updated light_draw to also take a new argument, index:
Code:
///light_draw(x, y, additive, shadows, blur_amount, index)
/*
Draws the lights and shadows. It makes sense to only draw the surface at 0, 0 if you are not using view or view_xview, view_yview if you are.
x: The x location to draw the shadow map.
y: The y location to draw the shadow map.
additive: Whether to use additive blending or not.
shadows: Whether or not to draw shadows.
blur_amount: How blurry to make the shadows, numbers from 0 to 1 are good but you can go higher for more blurriness.
index: The light map index to draw.
And edited every use of global._light_map to use that index for the array:
Code:
...

if !surface_exists(global._light_map[argument5]) {
        global._light_map[argument5] = surface_create(global._light_width * global._light_quality, global._light_height * global._light_quality)
    }
    surface_set_target(global._light_map[argument5])

...

if _light_inside {
                if !_light_shadows {
                    surface_set_target(global._light_map[argument5])
                    draw_set_blend_mode(bm_add)
                    draw_sprite_ext(_light_sprite, 0, (x - argument0) * global._light_quality, (y - argument1) * global._light_quality, _light_scale * global._light_quality, _light_scale * global._light_quality, _light_angle, _light_color, _light_alpha)
                    draw_set_blend_mode(bm_normal)
                    surface_reset_target()
                }

...

surface_set_target(global._light_map[argument5])
                    draw_set_alpha(1)
                    draw_set_color(c_white)
                    draw_set_blend_mode(bm_add)

...

if surface_exists(global._light_map[argument5])
{
    if argument4 <= 0
       draw_surface_ext(global._light_map[argument5], argument0, argument1, 1 / global._light_quality, 1 / global._light_quality, 0, c_white, 1)
    else
        draw_surface_blurred(global._light_map[argument5], argument0, argument1, 1 / global._light_quality, 1 / global._light_quality, argument4)
}

...
Now in the draw event you can draw multiple light maps, but you only want to draw one per view:
Code:
for (var i = 0; i < array_length_1d(global._light_map); i++)
{
    if view_current == i
    {
        light_draw(view_xview[i], view_yview[i], true, true, blur, i)
    }
}
We can simplify that like so:
Code:
light_draw(view_xview[view_current], view_yview[view_current], true, true, blur, view_current)
We also have to update light_set_quality because it resizes the light maps:
Code:
...

for (var i = 0; i < array_length_1d(global._light_map); i++)
{
    surface_resize(global._light_map[i], view_wview * global._light_quality, view_hview * global._light_quality)
}

...
There, now we can draw two light maps in two different views. It looks like this is the part you have so far. Now here's the part you missed, we're going to add the concept of ownership to the lights so that they only draw on the light maps they belong to.

I'm going to add a new property to the lights in the light_create script called index:
Code:
///light_create(light_sprite, light_scale, light_color, light_alpha, light_angle, shadows, index)
/*
Creates a light and the local variables for it.
sprite: Gradient sprite to use.
scale: Scale of the sprite.
color: The light color.
alpha: Brightness of the light, from 0 to 1.
shadows: If this light should draw shadows.
index: The light map that this light should be drawn too. Choose -1 for all.
*/

...

_light_index = argument6

...
Now we can set the light index like so:
Code:
light_create(sp_light_point, 1.5, c_white, 1, 0, true, 0)
To make this work we have to go back to the light_draw script and edit the part where it draws the lights so that is only draws lights with the same index:
Code:
...

// Draw lights.
    for (var i = 0; i < ds_list_size(global._lights); i++) {
        var _light = ds_list_find_value(global._lights, i)
        with _light {
            // Only draw lights that belong to this light map.
            if _light_index != argument5 && _light_index != -1
            {
                break
            }
            var _light_left, _light_top, _light_right, _light_bottom;

...
I also made it so that it will always draw lights with an index of -1, that way you can draw a light on all light maps if you want.

Here is an example project with these new scripts. In the example I set some of the lights to draw on the left light map, some on the right, and some on both. You can probably just replace your old scripts with these new ones and then you will be able to assign lights to only draw on one light map.

Notice that in this example the light maps will be sharing all the same properties, quality, size, etc. This should be fine because I imagine you want the screens to be the same size, but I did foresee one issue with the iteration time. The iteration time will be reset by both light maps when they are drawn, this shouldn't be a problem if you set a high number of iterations in light_init, but just in case I turned global._light_time into an array too so that each light map gets their own.

Also I noticed that you are using Game Maker Studio 2, was Fast Lights easy to work with in the the new version?
 
G

gnemnij

Guest
Hello gnemnij,

Yes this is possible with a little bit of effort.
View attachment 10606

You had the right idea with creating multiple light maps but it wasn't necessary to duplicate the scripts. I'll explain what I did here.

First I added a new argument to light_init called num:
Code:
///light_init(width, height, light_size, ambient_color, quality, iterations, num)
/*
Sets up lighting variables.
width: Width of the shadow map.
height: Height of the shadow map.
light_size: The size of the lightmaps, should be set to the largest light.
color: The shadow color, this also controls the darkness of the shadows.
quality: The lightmap quality, 0 to 1.
iterations: How many times a second to draw the lightmap. The default is the room speed
            but you might want to reduce this for increased performance. If it is too
            small, however, you will notice considerable stuttering.
num: The number of light maps to create, useful for split screen views.
*/
And made global._light_map and array:
Code:
...

for (var i = 0; i < argument6; i++)
{
    global._light_map[i] = surface_create(global._light_width * global._light_quality, global._light_height * global._light_quality)
}

...
Now we can create multiple surfaces to draw on like so:
Code:
light_init(view_wview, view_hview, 1024, color, 0.25, 30, 2)
I then updated light_draw to also take a new argument, index:
Code:
///light_draw(x, y, additive, shadows, blur_amount, index)
/*
Draws the lights and shadows. It makes sense to only draw the surface at 0, 0 if you are not using view or view_xview, view_yview if you are.
x: The x location to draw the shadow map.
y: The y location to draw the shadow map.
additive: Whether to use additive blending or not.
shadows: Whether or not to draw shadows.
blur_amount: How blurry to make the shadows, numbers from 0 to 1 are good but you can go higher for more blurriness.
index: The light map index to draw.
And edited every use of global._light_map to use that index for the array:
Code:
...

if !surface_exists(global._light_map[argument5]) {
        global._light_map[argument5] = surface_create(global._light_width * global._light_quality, global._light_height * global._light_quality)
    }
    surface_set_target(global._light_map[argument5])

...

if _light_inside {
                if !_light_shadows {
                    surface_set_target(global._light_map[argument5])
                    draw_set_blend_mode(bm_add)
                    draw_sprite_ext(_light_sprite, 0, (x - argument0) * global._light_quality, (y - argument1) * global._light_quality, _light_scale * global._light_quality, _light_scale * global._light_quality, _light_angle, _light_color, _light_alpha)
                    draw_set_blend_mode(bm_normal)
                    surface_reset_target()
                }

...

surface_set_target(global._light_map[argument5])
                    draw_set_alpha(1)
                    draw_set_color(c_white)
                    draw_set_blend_mode(bm_add)

...

if surface_exists(global._light_map[argument5])
{
    if argument4 <= 0
       draw_surface_ext(global._light_map[argument5], argument0, argument1, 1 / global._light_quality, 1 / global._light_quality, 0, c_white, 1)
    else
        draw_surface_blurred(global._light_map[argument5], argument0, argument1, 1 / global._light_quality, 1 / global._light_quality, argument4)
}

...
Now in the draw event you can draw multiple light maps, but you only want to draw one per view:
Code:
for (var i = 0; i < array_length_1d(global._light_map); i++)
{
    if view_current == i
    {
        light_draw(view_xview[i], view_yview[i], true, true, blur, i)
    }
}
We can simplify that like so:
Code:
light_draw(view_xview[view_current], view_yview[view_current], true, true, blur, view_current)
We also have to update light_set_quality because it resizes the light maps:
Code:
...

for (var i = 0; i < array_length_1d(global._light_map); i++)
{
    surface_resize(global._light_map[i], view_wview * global._light_quality, view_hview * global._light_quality)
}

...
There, now we can draw two light maps in two different views. It looks like this is the part you have so far. Now here's the part you missed, we're going to add the concept of ownership to the lights so that they only draw on the light maps they belong to.

I'm going to add a new property to the lights in the light_create script called index:
Code:
///light_create(light_sprite, light_scale, light_color, light_alpha, light_angle, shadows, index)
/*
Creates a light and the local variables for it.
sprite: Gradient sprite to use.
scale: Scale of the sprite.
color: The light color.
alpha: Brightness of the light, from 0 to 1.
shadows: If this light should draw shadows.
index: The light map that this light should be drawn too. Choose -1 for all.
*/

...

_light_index = argument6

...
Now we can set the light index like so:
Code:
light_create(sp_light_point, 1.5, c_white, 1, 0, true, 0)
To make this work we have to go back to the light_draw script and edit the part where it draws the lights so that is only draws lights with the same index:
Code:
...

// Draw lights.
    for (var i = 0; i < ds_list_size(global._lights); i++) {
        var _light = ds_list_find_value(global._lights, i)
        with _light {
            // Only draw lights that belong to this light map.
            if _light_index != argument5 && _light_index != -1
            {
                break
            }
            var _light_left, _light_top, _light_right, _light_bottom;

...
I also made it so that it will always draw lights with an index of -1, that way you can draw a light on all light maps if you want.

Here is an example project with these new scripts. In the example I set some of the lights to draw on the left light map, some on the right, and some on both. You can probably just replace your old scripts with these new ones and then you will be able to assign lights to only draw on one light map.

Notice that in this example the light maps will be sharing all the same properties, quality, size, etc. This should be fine because I imagine you want the screens to be the same size, but I did foresee one issue with the iteration time. The iteration time will be reset by both light maps when they are drawn, this shouldn't be a problem if you set a high number of iterations in light_init, but just in case I turned global._light_time into an array too so that each light map gets their own.

Also I noticed that you are using Game Maker Studio 2, was Fast Lights easy to work with in the the new version?
Wow! You are so nice a developer to listen to my needs and help me out, thank you very much! I was right, not only for Fast Light is the best light/shadow extension in the market, and you are also the best dev behind the extension!
I havent got a chance to try your new scripts yet but I read your comments in the post, I think they should work in my project.
For your question about GMS2, it generally works perfect, only when some old view/camera related scripts are very hard to read in GMS2 since they are twisted in a strange way.
 
G

gnemnij

Guest
Hi!

Another issue I have ran into is sometimes the shadow from spot light isnt working. (point light is working perfectly) it casts shadow to some of the casters, but ignore some others. I noticed this happening when the light object is too far from the caster, I want to know if you have an idea how this is happening. Thanks!
 

Buff

Member
Hi!

Another issue I have ran into is sometimes the shadow from spot light isnt working. (point light is working perfectly) it casts shadow to some of the casters, but ignore some others. I noticed this happening when the light object is too far from the caster, I want to know if you have an idea how this is happening. Thanks!
Oh wow, I guess I didn't notice this before. There's a check in light_draw, on line 58, that makes it so the shadows are only drawn if the caster is in range of the light:
Code:
if rectangle_in_rectangle(bbox_left, bbox_top, bbox_right, bbox_bottom, _light_left, _light_top, _light_right, _light_bottom)
If the caster isn't in the light box its shadows aren't drawn. But it looks like the variables I set for the light box, on line 30, are just plain wrong.

The problem has to do with lights that aren't centered in their sprite, like the spot light. What makes this really difficult is when the light can rotate, then the light box has to move with it. I could just have the box be the size of the light surface but that would be slow for smaller lights, so I want it to be based on the size of the light.

Here is a modification I made to the light box variables on line 30:
Code:
var _light_left, _light_top, _light_right, _light_bottom;
if sprite_get_xoffset(_light_sprite) != sprite_get_width(_light_sprite) / 2 && sprite_get_yoffset(_light_sprite) != sprite_get_height(_light_sprite) / 2 {
    _light_left = x - global._light_size / 2
    _light_top = y - global._light_size / 2
    _light_right = _light_left + global._light_size
    _light_bottom = _light_top + global._light_size
}
else {
    _light_left = x - _light_max_dist / 2
    _light_top = y - _light_max_dist / 2
    _light_right = _light_left + _light_max_dist
    _light_bottom = _light_top + _light_max_dist
}
It will use a small light box for lights that are centered and a large one for lights that are not. I'll update the package and the downloads too.
 
C

Capptianm

Guest
this looks really professional and I thank you for letting people use this for free, but I am having some problems my currently noobish understanding of this asset is not sure how to solve.

in the light_create script, there is this piece of code on line 22

Code:
ds_list_add(global._lights, id)
when I test my game, it says
Code:
___________________________________________
############################################################################################
FATAL ERROR in
action number 1
of Create Event
for object interaction_test:

global variable <unknown built-in variable>(-1610512726, -2147483648) not set before reading it.
 at gml_Script_light_create (line 22) - ds_list_add(global._lights, id)
############################################################################################
--------------------------------------------------------------------------------------------
stack frame is
gml_Script_light_create (line 22)
called from - gml_Object_interaction_test_CreateEvent_1 (line 4) - light_create(sp_light_point, .2, c_blue, .5, 0, true)
can anyone help me fix this?
 

Buff

Member
this looks really professional and I thank you for letting people use this for free, but I am having some problems my currently noobish understanding of this asset is not sure how to solve.

in the light_create script, there is this piece of code on line 22

Code:
ds_list_add(global._lights, id)
when I test my game, it says
Code:
___________________________________________
############################################################################################
FATAL ERROR in
action number 1
of Create Event
for object interaction_test:

global variable <unknown built-in variable>(-1610512726, -2147483648) not set before reading it.
 at gml_Script_light_create (line 22) - ds_list_add(global._lights, id)
############################################################################################
--------------------------------------------------------------------------------------------
stack frame is
gml_Script_light_create (line 22)
called from - gml_Object_interaction_test_CreateEvent_1 (line 4) - light_create(sp_light_point, .2, c_blue, .5, 0, true)
can anyone help me fix this?
Hey Capptianm,

Make sure you initialize the lighting engine with light_init first, that will set up all the necessary variables.
 
C

Capptianm

Guest
I now have a new problem, sorry for this but I am just not sure how to use this asset 100% yet. when I start the game, it does not have any shadows or lights.
Code:
light_init(3000, 3000, .2, c_black, 1, 30)
 
light_create(sp_light_point, .2, c_blue, .5, 0, true)
(NOTE:the 3000 in the light_init is the size of the room)

(EDIT: this is also the only light I have in the room for testing purposes)
 

Buff

Member
I now have a new problem, sorry for this but I am just not sure how to use this asset 100% yet. when I start the game, it does not have any shadows or lights.
Code:
light_init(3000, 3000, .2, c_black, 1, 30)
 
light_create(sp_light_point, .2, c_blue, .5, 0, true)
(NOTE:the 3000 in the light_init is the size of the room)

(EDIT: this is also the only light I have in the room for testing purposes)
Do you have any casters made with light_create_caster?
 
C

Capptianm

Guest
Ok, only casters will cast shadows. Also you have to draw the lighting system with light_draw in the draw event.
sorry, but I am at a huge blank and am not understanding how to use this asset. could someone explain to me?
 

Buff

Member
sorry, but I am at a huge blank and am not understanding how to use this asset. could someone explain to me?
It's true that there is no tutorial, maybe there should be, but the example project is very complete and should show you everything you need to know about the asset. Have you looked at the example? It's linked in the first post.
 
C

Capptianm

Guest
I have looked at the example project once, but I did not think of looking at it now. thanks!
 
H

Hoon Kim

Guest
I love your asset, it was just perfect for me!
And I just have a simple question about an application; how can I make a laser light out of this? Or just be able to change the height of the light (and the sprite of the light), instead of the whole thing.
 

Havik

Member
(Sorry for the essay, I rambled a bit :D)

Hi there! First off I just want to say thanks for this - I've been trying to get some lighting system working for several days now (and also tried writing my own, which worked .. but was very simple). Finally found yours and so far, seems perfect :D

Edit 1: Solved my first issue so removed all that essay (if you even saw it - I'd forgotten to set the sprites to middle center rather than top left, hence my caster by points was offset).

I do have another question though - I'm looking to have lights that ignore some casters and don't ignore others (i.e a fake 'height'. Placing lights 'on top' of a box allows the light to go over the wall next to it).

Would this be easier achieved via adding something into the light draw that checks what light it is and then ignores certain casters? Or would it be better to sort of duplicate the whole script with a different set of lights and casters? (so both sets aren't affected by the other).

Cheers in advance!

Edit 2: Ok nevermind, still having issues with the 'caster by points'! My sprite tile is 64x64 but I only want to create a shadow from part of it (a 64x17 rectangle part of the sprite). I can draw the box seemingly ok but there is always a point that the box 'snaps' to when drawing the shadows. This is because the collision for the whole sprite is still 64x64, but I'm trying to draw the shadow from a different sized box. If I set the sprite collision to the same size then it fixes the issue - but I'm trying to make it so the shadow is drawn from a certain part of the sprite but the collision is the full sprite so they need to be different. Can work around this with a collision layer if needbe though so it's not a big deal.

Linked a few gifs to show what I mean: gyazo.com/f2cb97f17817f6bdcb187c47bb04719f (with example sprite) and gyazo.com/300c10291b75037dd0914ba5ca82f619 (just shadows).

The code I have for this is:
Code:
var pointsx = ds_list_create()
var pointsy = ds_list_create()

ds_list_add(pointsx, -32)
ds_list_add(pointsy, -32)

ds_list_add(pointsx, 32)
ds_list_add(pointsy, -32)

ds_list_add(pointsx, 32)
ds_list_add(pointsy, -15)

ds_list_add(pointsx, -32)
ds_list_add(pointsy, -15)

ds_list_add(pointsx, -32)
ds_list_add(pointsy, -32)

ds_list_add(pointsx, bbox_right - x)
ds_list_add(pointsy, bbox_bottom - y)

ds_list_add(pointsx, bbox_left - x)
ds_list_add(pointsy, bbox_bottom - y)

light_create_caster_from_points(pointsx, pointsy)
The sprite is middle centered and the rectangle box I'm drawing is 64X*17Y in size. Any idea why it's drawing that bottom corner at (0,32) in the shadow?

But the bigger thing I'm stuck on is this:
Basically I'm trying to make it so the light will draw on the 'front' of the wall but not the top (so set the box to just be the top :) ) I managed to get the shadow to work but haven't been able to get it to light up the non-casting part of the sprite (it just sits in the dark still even though no shadow is being drawn from it). Not sure if there is a way to achieve this without some funky multiple objects or something .. figured I'd ask here, thanks again!
 
Last edited:

Buff

Member
I love your asset, it was just perfect for me!
And I just have a simple question about an application; how can I make a laser light out of this? Or just be able to change the height of the light (and the sprite of the light), instead of the whole thing.
It sounds like you have the right idea, create a new sprite that is just a white line and use it as your light sprite in light_create(). You can color it red or any other color you wish.
 
N

NateTheGreat

Guest
EDIT: Snip. never mind, I pretty much figured out all of my issues.
 
Last edited:

Buff

Member
(Sorry for the essay, I rambled a bit :D)

Hi there! First off I just want to say thanks for this - I've been trying to get some lighting system working for several days now (and also tried writing my own, which worked .. but was very simple). Finally found yours and so far, seems perfect :D

Edit 1: Solved my first issue so removed all that essay (if you even saw it - I'd forgotten to set the sprites to middle center rather than top left, hence my caster by points was offset).

I do have another question though - I'm looking to have lights that ignore some casters and don't ignore others (i.e a fake 'height'. Placing lights 'on top' of a box allows the light to go over the wall next to it).

Would this be easier achieved via adding something into the light draw that checks what light it is and then ignores certain casters? Or would it be better to sort of duplicate the whole script with a different set of lights and casters? (so both sets aren't affected by the other).

Cheers in advance!

Edit 2: Ok nevermind, still having issues with the 'caster by points'! My sprite tile is 64x64 but I only want to create a shadow from part of it (a 64x17 rectangle part of the sprite). I can draw the box seemingly ok but there is always a point that the box 'snaps' to when drawing the shadows. This is because the collision for the whole sprite is still 64x64, but I'm trying to draw the shadow from a different sized box. If I set the sprite collision to the same size then it fixes the issue - but I'm trying to make it so the shadow is drawn from a certain part of the sprite but the collision is the full sprite so they need to be different. Can work around this with a collision layer if needbe though so it's not a big deal.

Linked a few gifs to show what I mean: gyazo.com/f2cb97f17817f6bdcb187c47bb04719f (with example sprite) and gyazo.com/300c10291b75037dd0914ba5ca82f619 (just shadows).

The code I have for this is:
Code:
var pointsx = ds_list_create()
var pointsy = ds_list_create()

ds_list_add(pointsx, -32)
ds_list_add(pointsy, -32)

ds_list_add(pointsx, 32)
ds_list_add(pointsy, -32)

ds_list_add(pointsx, 32)
ds_list_add(pointsy, -15)

ds_list_add(pointsx, -32)
ds_list_add(pointsy, -15)

ds_list_add(pointsx, -32)
ds_list_add(pointsy, -32)

ds_list_add(pointsx, bbox_right - x)
ds_list_add(pointsy, bbox_bottom - y)

ds_list_add(pointsx, bbox_left - x)
ds_list_add(pointsy, bbox_bottom - y)

light_create_caster_from_points(pointsx, pointsy)
The sprite is middle centered and the rectangle box I'm drawing is 64X*17Y in size. Any idea why it's drawing that bottom corner at (0,32) in the shadow?

But the bigger thing I'm stuck on is this:
Basically I'm trying to make it so the light will draw on the 'front' of the wall but not the top (so set the box to just be the top :) ) I managed to get the shadow to work but haven't been able to get it to light up the non-casting part of the sprite (it just sits in the dark still even though no shadow is being drawn from it). Not sure if there is a way to achieve this without some funky multiple objects or something .. figured I'd ask here, thanks again!
Hey Havik,

Let's take a look at each of your requests.

1. Certain lights only affect certain casters.
This is somewhat similar to what was described here, we need to add new property to the lights and shadows that conditionally draws them, let's call them layers. In the create event of a light create a new variable called _light_layer and set it to a number, say 0 for default.
Code:
_light_layer = 0
Let's do the same thing for the casters too, we'll set the layer in the create event to the same layer as the lights that we want affecting it.

Now for the actual drawing code we'll look at the light_draw script.
On line 30 where we create the temporary per-light variables for use in the casters, we'll have to add the current light layer as well, and set it to this light's layer.
Code:
 // Draw lights.
    for (var i = 0; i < ds_list_size(global._lights); i++) {
        var _light = ds_list_find_value(global._lights, i)
        with _light {
            var _light_left, _light_top, _light_right, _light_bottom, _light_layer_current;
_light_layer_current = _light_layer
...
Now down a bit on line 68 is where we start drawing the shadows, we'll guard this with a conditional based on light layer, just like the conditional for the rectangle_in_rectangle on line 67, in fact we can just tag this conditional on to that one with an and statement.
Code:
 // Draw shadows.
                        for (var j = 0; j < ds_list_size(global._light_casters); j++) {
                            var _caster = ds_list_find_value(global._light_casters, j)
                            with _caster {
                                if rectangle_in_rectangle(bbox_left, bbox_top, bbox_right, bbox_bottom, _light_left, _light_top, _light_right, _light_bottom) &&  _light_layer == _light_layer_current {
                                    draw_primitive_begin(pr_trianglestrip)
...
This way the shadow will only be drawn if the caster's layer and the light's layer match.

I made an example of this, here I set the _light_layer to 0 in the light_create script and light_create_caster_* scripts and then set the layer to 1 for the mouse light and rotating caster in the create event. This way the light mouse only creates shadows on rotating casters.

2. Malformed lights.
It looks like the problem here is the last four lines:
Code:
ds_list_add(pointsx, bbox_right - x)
ds_list_add(pointsy, bbox_bottom - y)

ds_list_add(pointsx, bbox_left - x)
ds_list_add(pointsy, bbox_bottom - y)
It creates two more points than you need. In fact the last point isn't necessary because light_draw draws the first point again already. All you need is:
Code:
var pointsx = ds_list_create()
var pointsy = ds_list_create()

ds_list_add(pointsx, -32)
ds_list_add(pointsy, -32)

ds_list_add(pointsx, 32)
ds_list_add(pointsy, -32)

ds_list_add(pointsx, 32)
ds_list_add(pointsy, -15)

ds_list_add(pointsx, -32)
ds_list_add(pointsy, -15)

light_create_caster_from_points(pointsx, pointsy)
3. Lights in front of walls?
I'm not sure what you mean here. Can you try explaining it in a different way?
When I try using your points on a 64x64 block, it only shadows the top part:
upload_2017-8-6_13-42-9.png
 
Last edited:
S

SmokinLettuce

Guest
Absolutely love this extension! I don't have a gigantic amount of the GML, but I've been able to do dynamic shadows and updates and all the good stuff fairly easily. I am curious, and lost, on how to create lighting only on the surface of my world. I'm making a game similar to Terraria and want the surface of the world to be lit up with everything below it being blacked out. Could you help me in anyway in doing this? I can email you or dropbox the project file to you and you can take a closer look at it if you'd like, thank you!
 
O

Oliver N. Hansen

Guest
Hello...

I feel kinda stupid for asking this but i need to know..

I really love this asset and stuff... and i have been trying to get the lighting to follow my character in my game. Guess you can imagine what im talking about.
I been trying for hours know without luck. I feel like a dumb 💩💩💩💩 for not know how to code it. hope i could get a little help here?
 

Nedenir

Member
I have a problem with shader, that in the android compilation works without any problem on two devices, which have completely different resolutions but for some reason shader flickers and loses objects on the third device Asus Zenfone Go.

Shows alternately either tilesets or draws objects never together, sometimes it goes normally but flickers again after a while.
 
N

nigmatech

Guest
Excellent script. Are you planning on adapting it to GMS 2?

Also a question - is there a way to get object status (is it in light or not)?
 
Top