Legacy GM Shadow and Lighting [SOLVED]

  • Thread starter jap pagcaliwangan
  • Start date
J

jap pagcaliwangan

Guest
So Ive gotten this shadow and light engine to work
.
The problem now is, I cant seem to draw the shadow and the light on one surface. Currently, Im drawing the shadows on the application surface.

Here is the project file: https://drive.google.com/open?id=0B2SZdyVcA3UVRmNCS0t0TkE5MVk

Here is the code:


obj_shadowandlightengine
Create Event
/// shadow variables
surf = surface_create(room_width,room_height);
shadow = surface_create(room_width,room_height);
surfscale = 1/8;

shON = true // this variable enables and disables the shadow system.
lightRad = 150
pl = obj_player


plX = pl.x
plY = pl.y
plXOld = 0
plYOld = 0

// First border Line

direct1Mod = 0
direct2Mod = 0

trace1 = true
trace2 = true
trace3 = true
trace4 = true

extender = 150 // extra shadow space for close lighting

point3X = 0
point3Y = 0
point4X = 0
point4Y = 0

point1X = 0
point1Y = 0
point2X = 0
point2Y = 0


mover1 = 0
mover2 = 0

Step Event
/// draw_shadows
plX = pl.x
plY = pl.y



/*
if (place_meeting(x,y,obj_rayKey)) {

shON = true
}
else {shON = false}
*/

if (shON = true) {
if (point_distance(plX,plY,x,y) < lightRad) {inLight = true}
else {inLight = false}

if (inLight = true) {
while (trace1 = true ) {
direct1 = point_direction(plX,plY,x,y) + direct1Mod
point1X = plX + lengthdir_x(lightRad + extender,direct1)
point1Y = plY + lengthdir_y(lightRad + extender,direct1)
direct1Mod+=0.1
if (!collision_line(plX,plY,point1X,point1Y,id,true,false)) {trace1 = false}
}
while (trace1 = false && trace3 = true) {
point3X = plX + lengthdir_x(mover1,direct1-0.1)
point3Y = plY + lengthdir_y(mover1,direct1-0.1)
mover1 += 0.1
if (collision_circle(point3X,point3Y,1.1,id,true,false)) {trace3 = false}
}

while (trace2 = true ) {
direct2 = point_direction(plX,plY,x,y) + direct2Mod
point2X = plX + lengthdir_x(lightRad + extender,direct2)
point2Y = plY + lengthdir_y(lightRad + extender,direct2)
direct2Mod-=0.1
if (!collision_line(plX,plY,point2X,point2Y,id,true,false)) {trace2 = false}
}
while (trace2 = false && trace4 = true) {
point4X = plX + lengthdir_x(mover2,direct2+0.1)
point4Y = plY + lengthdir_y(mover2,direct2+0.1)
mover2 += 0.1
if (collision_circle(point4X,point4Y,1.1,id,true,false)) {trace4 = false}
}
}

if (plXOld != plX || plYOld != plY) {
trace1 = true
trace2 = true
trace3 = true
trace4 = true
mover1 = 0
mover2 = 0
direct1Mod = 0
direct2Mod = 0
plXOld = plX
plYOld = plY
}
}

Draw Event
//meteor shadows
if (point_distance(plX,plY,x,y) < lightRad) {

draw_triangle_color(point1X,point1Y,point2X,point2Y,point3X,point3Y,c_black,c_black,c_black,false);
draw_triangle_color(point2X,point2Y,point3X,point3Y,point4X,point4Y,c_black,c_black,c_black,false);

}

draw_self()

//player light
surface_set_target(surf);
draw_clear(c_black);
with(pl){
draw_set_blend_mode(bm_src_color);
draw_sprite_ext(spr_light,0,x*other.surfscale,y*other.surfscale,1.5,1.5,0,c_white,lightstr);
draw_set_blend_mode(bm_normal);}
surface_reset_target();
draw_surface_ext(surf,0,0,1/surfscale,1/surfscale,0,c_white,1);
 

jo-thijs

Member
Hi and welcome to the GMC!

I would first like to point out that restarting the game does not seem to free surfaces.
You'll have to do this bit yourself manually, because currently, if you die enough (which isn't too much),
the screen starts freaking out.
Continue even further and the game crashes with an out of memory error.

Secondly, you don't check if the surface still exist before using them.
This causes the game to throw an error when you minimize the window and then maximaze it again.
GameMaker gets rid of the surfaces then and you'll try to draw to an unexisting surface.

Thirdly, why do you let every asteroid draw the light around the player to the surface?
This should only be done once (every draw event) by a controller object or the player object.

Fourthly, why do you want to draw the shadows to the light surface?
It is way easier to just have some controller object draw all the shadows before the asteroids are being drawn.
You can control the order of draw event execution by giving specific depth values to objects.

Fithly, you use draw_set_blend_mode incorrectly.
To use bm_src_color, you need to use draw_set_blend_mode_ext.
After some testing, it looks like GameMaker treats bm_src_color as bm_subtract here.
Seeing as this is used only to draw a sprite to a black background, it will leave the entire surface black,
but will decide the alpha values at every point.

This can be used to skip surfaces altogether.

How about you try this?
Delete the draw event of obj_shadowandlight and delete all the events of obj_shadowcontrol.
Then set the depth of obj_shadowcontrol to 10 and put this in its draw event:
Code:
///Meteor shadows
draw_set_color(c_black);
with obj_shadowandlight
    if (point_distance(plX,plY,x,y) < lightRad) {
        draw_triangle(point1X,point1Y,point2X,point2Y,point3X,point3Y,false);
        draw_triangle(point2X,point2Y,point3X,point3Y,point4X,point4Y,false);
    }
and this in its end draw event:
Code:
///Draw the light
var lightstr = 0.5;
var scale = 2;
var b = 61 * scale;
draw_set_color(c_black);
draw_rectangle(0, 0, room_width, obj_player.y - b, false);
draw_rectangle(0, 0, obj_player.x - b, room_height, false);
draw_rectangle(0, room_height, room_width, obj_player.y + b, false);
draw_rectangle(room_width, 0, obj_player.x + b, room_height, false);

draw_set_blend_mode_ext(bm_zero, bm_src_alpha);
draw_sprite_ext(spr_light, -1, obj_player.x, obj_player.y, scale, scale, 0, c_white, lightstr);
draw_set_blend_mode(bm_normal);
And finally, put an instance of obj_shadowcontroller in the room.
 
J

jap pagcaliwangan

Guest
Hi and welcome to the GMC!

I would first like to point out that restarting the game does not seem to free surfaces.
You'll have to do this bit yourself manually, because currently, if you die enough (which isn't too much),
the screen starts freaking out.
Continue even further and the game crashes with an out of memory error.

Secondly, you don't check if the surface still exist before using them.
This causes the game to throw an error when you minimize the window and then maximaze it again.
GameMaker gets rid of the surfaces then and you'll try to draw to an unexisting surface.

Thirdly, why do you let every asteroid draw the light around the player to the surface?
This should only be done once (every draw event) by a controller object or the player object.

Fourthly, why do you want to draw the shadows to the light surface?
It is way easier to just have some controller object draw all the shadows before the asteroids are being drawn.
You can control the order of draw event execution by giving specific depth values to objects.

Fithly, you use draw_set_blend_mode incorrectly.
To use bm_src_color, you need to use draw_set_blend_mode_ext.
After some testing, it looks like GameMaker treats bm_src_color as bm_subtract here.
Seeing as this is used only to draw a sprite to a black background, it will leave the entire surface black,
but will decide the alpha values at every point.

This can be used to skip surfaces altogether.

How about you try this?
Delete the draw event of obj_shadowandlight and delete all the events of obj_shadowcontrol.
Then set the depth of obj_shadowcontrol to 10 and put this in its draw event:
Code:
///Meteor shadows
draw_set_color(c_black);
with obj_shadowandlight
    if (point_distance(plX,plY,x,y) < lightRad) {
        draw_triangle(point1X,point1Y,point2X,point2Y,point3X,point3Y,false);
        draw_triangle(point2X,point2Y,point3X,point3Y,point4X,point4Y,false);
    }
and this in its end draw event:
Code:
///Draw the light
var lightstr = 0.5;
var scale = 2;
var b = 61 * scale;
draw_set_color(c_black);
draw_rectangle(0, 0, room_width, obj_player.y - b, false);
draw_rectangle(0, 0, obj_player.x - b, room_height, false);
draw_rectangle(0, room_height, room_width, obj_player.y + b, false);
draw_rectangle(room_width, 0, obj_player.x + b, room_height, false);

draw_set_blend_mode_ext(bm_zero, bm_src_alpha);
draw_sprite_ext(spr_light, -1, obj_player.x, obj_player.y, scale, scale, 0, c_white, lightstr);
draw_set_blend_mode(bm_normal);
And finally, put an instance of obj_shadowcontroller in the room.

I tried this. But the screen is just black. Also, is it possible to draw the shadow and light on 2 different surfaces but controlled by one object?
 

jo-thijs

Member
Yes, that is possible, but that would just waste resources as you don't actually use those surfaces.
Are you sure you did what I said and got a black screen?
It's pretty dark, but it should not be black.
You can brighten things by changing lightstr in the draw end event of obj_shadowcontroller to 1
or set the scale there to a higher value.
 
J

jap pagcaliwangan

Guest
Yes, that is possible, but that would just waste resources as you don't actually use those surfaces.
Are you sure you did what I said and got a black screen?
It's pretty dark, but it should not be black.
You can brighten things by changing lightstr in the draw end event of obj_shadowcontroller to 1
or set the scale there to a higher value.

I tried changing lightstr and even the alpha. its just black. I know the game is still working coz of the game restart function changes the logs on the compiler.
 

jo-thijs

Member
I just downloaded your project again and applied all the changes I posted and it does work for me.
Either you did not do as I said, or your system is having trouble using blend modes.
The blend modes I use are not on the risky blend mode list.

Can you upload the current state of your project again?
 

jo-thijs

Member
You've merged obj_shadowandlight and obj_shadowcontroller.
Create a new object (obj_shadowcontroller) with depth 10 and move the draw events from obj_shadowandlight to that object (obj_shadowcontroller).
Then put that object (obj_shadowcontroller) in the room and test again.
 
J

jap pagcaliwangan

Guest
You've merged obj_shadowandlight and obj_shadowcontroller.
Create a new object (obj_shadowcontroller) with depth 10 and move the draw events from obj_shadowandlight to that object (obj_shadowcontroller).
Then put that object (obj_shadowcontroller) in the room and test again.
Its working now. Thank you! quick question tho. You didnt use any surfaces right? and if not, how come? and is are there any differences between using and not using surfaces?
 

jo-thijs

Member
You're welcome!

I didn't use any surfaces indeed.
They were unnecessary and then they are better avoided.
Surfaces can use up a lot of resources and if you don't use them well,
they can be pretty unreliable (deleted when minimizing the window for example).

Surfaces should only be used if you really need to save some drawn stuff for later use or for some complex operation.
They can sometimes also be used for optimizing a game, but again, you'd need to be carefull for the surfaces being able to get deleted.

In your case, you only needed to draw shadows, objects and then some overlay on top of everything.
No need for saving anything there, you just need to manage drawing order.
 
J

jap pagcaliwangan

Guest
You're welcome!

I didn't use any surfaces indeed.
They were unnecessary and then they are better avoided.
Surfaces can use up a lot of resources and if you don't use them well,
they can be pretty unreliable (deleted when minimizing the window for example).

Surfaces should only be used if you really need to save some drawn stuff for later use or for some complex operation.
They can sometimes also be used for optimizing a game, but again, you'd need to be carefull for the surfaces being able to get deleted.

In your case, you only needed to draw shadows, objects and then some overlay on top of everything.
No need for saving anything there, you just need to manage drawing order.

I see. but is there anyway the light effect can affect the shadow? like you know how the light is a circular gradient? is there a way for the shadow to also have the gradient?
 

jo-thijs

Member
There is, but how should that gradient look like?
Does it depend on the distance to the nearest point of the object?
Does it depend on something else as well?
 
J

jap pagcaliwangan

Guest
There is, but how should that gradient look like?
Does it depend on the distance to the nearest point of the object?
Does it depend on something else as well?
i figured it out. thanks.
 
Top