- Title: 2D directional/radial light and shadows engine (Now with 2 possible light drawing methods)
- Description: An example of how surfaces can be used to obtain good looking directional or radial light plus realistic directional shadows.
- GM Version: GM8
- Registered: Yes
- File Type: .gmk
- File Size: 1MB - 3.3MB
- File Links:
- .GMK FILE (1 MB)
- EXECUTABLE (3.3 MB)
Screenshots:

This is how it works:
METHODS:
There are 2 methods when drawing the light:
- Shapes Method: Draws many superimposed shapes to form a desired source of light. This can be very slow.
- Sprites Method: Draws a sprite to form a desired source of light. This is much faster and can support many different light shapes, but requires sprites.
SURFACES:
- There are 3 surfaces that are very crucial and are called "darkness_surface", "flashlight_surface" and "shadow_surface" (flashlight surface is also used to draw the lamp).
- The darkness_surface is the "final" surface that is actually drawn on the screen.
- The object obj_engine refreshes the darkness_surface every step (in the begin step event) by drawing a rectangle that covers the screen. Otherwise, the light that is being drawn would sum up every step and give a weird unwanted effect.
- The light is drawn on the flashlight_surface after the darkness_surface is refreshed.
- The shadow is drawn on the shadow_surface.
- The shadow_surface is subtracted [code: "draw_set_blend_mode(bm_subtract)"] from the flashlight_surface, so that the light does not reach the other side of the blocks.
- Finally the flashlight_surface is subtracted from the darkness_surface, so that in the darkness alpha value is lower where the light is.
- If the engine is using shapes method, the flashlight_surface is refreshed and this process (drawing light) is repeated a number of times equal to the number of "levels of light"(Depends on light graphical quality).
DRAWING IN SHAPES METHOD:
- In the Directional light the levels are triangles and in the radial light they are circles.
- One corner of the triangle is the origin of the light and the other two are projetions of it towards the beam angle.
- The levels of light exist so that the center of the beam/sphere is more lightened than its borders.
- My scripts draw the inner levels first and the outer later.
- The alpha value is equal in all levels and it depends on the number of levels. (value=1/numberoflevels)
DRAWING IN SPRITES METHOD:
- A sprite with a certain shape is drawn where the light is desired.
- In this sprite the alpha value must be lower where less light is desired and higher where more light is desired.
SHADOWS:
- Shadows in this example only works for rectangles.
- Before the light is subtracted from the darkness, the shadows are subtracted from the light.
- The shadows are drawn as 2 primitives with 4 vertexes each, 2 are opposite corners of the rectangle and 2 are its projections on the opposite direction of the lightsource
- Also, after the primitives the body of the rectangle is also drawn.
HOW TO PROJECT SOMETHING:
To project a point in space I use trigonometric functions (a.k.a. sin and cos). This is used both in shapes method and when drawing directional shadows. This is how it works:
This triangle is the solution to 99% of your projection problems. Let's say the point (x,y) in question is point A in the drawing. And you want to project it to B. You know the distance you want to project it, it's the hypotenuse (h), and you know the angle 'A', you just want to know how much is the distance 'b' and 'a' so that you can simply sum them to the 'xy' values.
Sine equals 'opposite side / hypotenuse', and Cosine equals 'adjacent / hypotenuse'
That is great because you know two of three things in each equation:
sin(angle_you_want_to_project_in_RADIANS)= -a/projection_lenght(The negative sign exists because in gamemaker "y" values grow downwards while in trigonometry it grows upward)
cos(angle_you_want_to_project_in_RADIANS)= b/projection_lenght(Gamemaker uses angles in radians iside 'sin' and 'cos' functions, I suggest converting your degree angles using 'degtorad' function)
Now, if you multiply these 'sin' and 'cos' values by the projection_lenght (hypotenuse) you will obtain the value of x and -y. Final code is as follows
projected_x=x+projection_lenght*cos(degtorad(angle)) projected_y=y+projection_lenght*-sin(degtorad(angle))I really hope that helps.
Code Comments: There are several code comments on my scripts, they have all been translated to english
Graphic Quality: I was very satisfied with the final looks of the light and shadows in both methods, but in shapes method the engine is borderline unplayable in most "casual PC's". In my good computer every room runs at 30 fps in "high" quality. In my bad laptop every room besides the first 2 are unplayable even in "very poor". Therefore, the sprites method is much more viable.
Obs.: The existance of a shadows_surface is not necessary because the shadows can be drawn in subtract mode directly in the flashlight_surface, but it makes things a little bit less organised.
Obs.: This works with multiple lightsources at once, I totally forgot to include that in the file. Well, nevermind, you will have to trust me, or to go test it yourself. Hah.
Thanks for reading. Rock on.
Updates:
- Now with 2 different methods!
- Entire codes translated to english!
If you liked this you can thumbs up this post to help support the topic. Down here ▼
Edited by silverlq, 06 July 2011 - 12:14 AM.













