C
CoderJoe
Guest
GM Version: GM:Studio
Target Platform: ALL
Download: N/A
Links: My Blog Post on this
Old forum post
Summary:
Tutorial on how to make dynamic shadow. Based on code by flokkienathur from the archived forums.
Tutorial:
(BTW this is my first tutorial post. I hope it helps)
Hey there. On the old GM forums there was a post on dynamic light. It's pretty cool, however I wanted to make just dynamic shadows to give a different look. Some of the code is based on flokkienathur's post (linked above). I also did a blog post for this tutorial (also linked above).
The basic idea is this: we create casters and give them a shape in code (just points in an array). Then we create a trianglestrip primitive to draw the shadow. (Below is just what I put on my blog)
SETUP
First off let’s create some objects. Create one called oParCaster and oParLight. These will be the parent object for our effects. Also create an object called oLightMap. This object will store our surface for our light map.
Lightmap scripts
Next we will create some scripts for the light map.
scrLightMapInit:
We will call this script in the create event of our lightmap object. Like this:
The next script will update the light map (put it in the step event of the light map object). If we didn’t update the light map then we would just have static shadows (ones that don’t move). This can be what you want if you want to “bake” in the shadows. In that case you would only call this script once.
scrLightMapUpdate:
I’m gonna explain how the shadows are drawn in a minute (there are some bugs still that I’m working out). Don’t run anything yet we still have a ways to go. Call this next script in the draw event of the light map object. It simply draws our surface will blending.
scrLightMapDraw:
Lights:
Put this next script in the create event of you light object (YOUR light object not the parent light object). Make sure to set the parent as the parent light object!
scrLightInit:
Pretty simple. The original light engine by flokkienathur had support for other things such as sprite lights and other fancy stuff. Since we are making a shadow engine though our lights aren’t actually doing anything other than telling the game where we want shadows to be.
Casters:
Casters are going to be our “light blockers” so to speak. We will set up point in an array that store their shape. We then use this shape to cast shadows. Like with the lights, simply add these scripts to your caster objects (walls, player, etc.) and make the caster parent the parent.
scrCasterInitPolygon:
scrCasterAddPoint:
These next scripts are for specifying what shape. You could always make your own shapes by using the previous two scripts. These are just easy presets.
scrCasterInitCircle:
scrCasterInitRectangle:
scrCasterInitSprite
Great the caster scripts are all setup! Put one of the shape scripts in the create event of your caster object and set the parent as the caster parent object and there you go!
Next we will actually set up the drawing script. This one requires more explanation so I left it for last.
scrLightMapDrawShadow:
The main glitch here is that one shadow can be drawn over another shadow which messes up our nice effect. I have yet to figure out how to fix this. My idea would be to test that the end point’s of the shadow don’t run into another caster. This should prevent shadows from drawing on top of each other. I will post an update when I fix this.
Target Platform: ALL
Download: N/A
Links: My Blog Post on this
Old forum post
Summary:
Tutorial on how to make dynamic shadow. Based on code by flokkienathur from the archived forums.
Tutorial:
(BTW this is my first tutorial post. I hope it helps)
Hey there. On the old GM forums there was a post on dynamic light. It's pretty cool, however I wanted to make just dynamic shadows to give a different look. Some of the code is based on flokkienathur's post (linked above). I also did a blog post for this tutorial (also linked above).
The basic idea is this: we create casters and give them a shape in code (just points in an array). Then we create a trianglestrip primitive to draw the shadow. (Below is just what I put on my blog)
SETUP
First off let’s create some objects. Create one called oParCaster and oParLight. These will be the parent object for our effects. Also create an object called oLightMap. This object will store our surface for our light map.
Lightmap scripts
Next we will create some scripts for the light map.
scrLightMapInit:
Code:
///scrLightMapInit(width, height, parCaster, parLight)
//Use width and height of room as long as the room is somewhat small
lm_width = argument0; //width of lightmap (usually width of room)
lm_height = argument1; //height of lightmap (usually height of room)
lm_surface_id = surface_create(lm_width, lm_height); //Store the id of the surface so we can access it
//Parents of casters and lights
lm_parent_caster = argument2;
lm_parent_light = argument3;
Code:
scrLightMapInit(room_width, room_height, oParCaster, oParLight);
scrLightMapUpdate:
Code:
///scrLightMapUpdate()
//check if the lightmap surface exists
if(!surface_exists(lm_surface_id)){
//if it does not exist, recreate it
lm_surface_id = surface_create(lm_width,lm_height);
}
surface_set_target(lm_surface_id); //do all the next events to our light map
draw_clear(make_colour_rgb(0,0,0)); //clear the surface
var lmid = id; //lightmap object id
if (lm_parent_light != -1) { //check that there is a parent object for the light
with (lm_parent_light) { //do the next events for all lights that exist
var lx = x; //light x pos
var ly = y; //light y pos
var lr = light_radius; //light radius (set in the light object)
var lid = id; //light object id
with(lmid.lm_parent_caster) { //get the caster parent (from the lightmap)
if point_in_circle(x,y,lx,ly,lr) { //check if the caster is in the light’s radius
//start drawing shadow
draw_primitive_begin(pr_trianglestrip);
draw_set_color(c_white);
//draw shadow primitive from each point on caster
for(i = 0; i < caster_point_count-1; i++){
scrLightMapDrawShadow(id, lid, lr, caster_point_x[i], caster_point_y[i], caster_point_x[i+1], caster_point_y[i+1]);
}
//close the shape from the last point of the caster to the first point
scrLightMapDrawShadow(id, lid, lr, caster_point_x[caster_point_count-1],caster_point_y[caster_point_count-1], caster_point_x[0], caster_point_y[0]);
draw_primitive_end();
//stop drawing shadow
}
}
}
}
//reset this light surface
surface_reset_target();
scrLightMapDraw:
Code:
///scrLightMapDraw()
draw_set_blend_mode(bm_subtract);
draw_surface(lm_surface_id,0,0);
draw_set_blend_mode(bm_normal);
Put this next script in the create event of you light object (YOUR light object not the parent light object). Make sure to set the parent as the parent light object!
scrLightInit:
Code:
///scrLightInit(radius)
light_radius = argument0;
Casters:
Casters are going to be our “light blockers” so to speak. We will set up point in an array that store their shape. We then use this shape to cast shadows. Like with the lights, simply add these scripts to your caster objects (walls, player, etc.) and make the caster parent the parent.
scrCasterInitPolygon:
Code:
///scrCasterInitPolygon()
caster_point_count = 0;
Code:
///scrCasterAddPoint(x, y)
caster_point_x[caster_point_count] = argument0;
caster_point_y[caster_point_count] = argument1;
caster_point_count += 1;
scrCasterInitCircle:
Code:
///scrCasterInitCircle(radius, fractions)
scrCasterInitPolygon();
var radius = argument0;
var fractions = argument1; //how accurate you want the circle to be
var i;
var m;
m = 2 * 3.141592654 / fractions;
for(i = 0; i < fractions; i++){
scrCasterAddPoint(cos(m * i) * radius, sin(m * i) * radius);
}
Code:
//scrCasterInitRectangle(left, top, bottom, right)
scrCasterInitPolygon();
var left = argument0;
var top = argument1;
var right = argument2;
var bottom = argument3;
scrCasterAddPoint(left,top);
scrCasterAddPoint(right,top);
scrCasterAddPoint (right,bottom);
scrCasterAddPoint (left,bottom);
Code:
///scrCasterInitSprite()
scrCasterInitPolygon();
scrCasterAddPoint (-sprite_xoffset,-sprite_yoffset);
scrCasterAddPoint (-sprite_xoffset + sprite_width,-sprite_yoffset);
scrCasterAddPoint (-sprite_xoffset + sprite_width,-sprite_yoffset + sprite_height);
scrCasterAddPoint (-sprite_xoffset,-sprite_yoffset + sprite_height);
Next we will actually set up the drawing script. This one requires more explanation so I left it for last.
scrLightMapDrawShadow:
Code:
///scrLightMapDrawShadow(caster, light, lightRadius, x1, y1, x2, y2)
var caster = argument0; //caster id
var light = argument1; //light id
var lightRadius = argument2; //radius
//caster point are relative to the caster, thus we must add the caster’s position to compensate and get the actual position in the world
var Ax = argument3 + caster.x; //startx
var Ay = argument4 + caster.y; //starty
var Bx = argument5 + caster.x; //endx
var By = argument6 + caster.y; //endy
var Lx = light.x; //light x
var Ly = light.y; //light y
//angles to calculate end points (these are just from the light to the caster points)
var shadowAngleA = point_direction(Lx, Ly, Ax, Ay);
var shadowAngleB = point_direction(Lx, Ly, Bx, By);
//Next we calculate the end points of the shadow
//The shadow will be drawn from our caster points to the end of the light’s radius
var E1x, E1y, E2x, E2y;
E1x = Lx+lengthdir_x(lightRadius, shadowAngleA);
E1y = Ly+lengthdir_y(lightRadius, shadowAngleA);
E2x = Lx+lengthdir_x(lightRadius, shadowAngleB);
E2y = Ly+lengthdir_y(lightRadius, shadowAngleB);
//We will fade the shadow out as it gets to the end of the light radius so that it will look better
var c = c_white;
var alphaEnd = 0;
var dist = point_distance(caster.x, caster.y, Lx, Ly);
if dist == 0
dist = 1;
var lightToCaster = point_direction(Lx,Ly, caster.x, caster.y);
var totalDist = point_distance(Lx,Ly, Lx+lengthdir_x(lightRadius,lightToCaster), Ly+lengthdir_y(lightRadius,lightToCaster));
//This is our start alpha, it will be darker if the caster is closer to the light
var alphaStart = 0.25*(1-(dist/totalDist));
//We now add vertexes to our shadow primitive
draw_vertex_colour(Ax, Ay,c,alphaStart); //start of point a
draw_vertex_colour(E1x, E1y,c,alphaEnd); //end of point a
draw_vertex_colour(Bx, By, c, alphaStart); //start of point b
draw_vertex_colour(E2x, E2y, c, alphaEnd); //end of point b
Last edited by a moderator: