Your browser doesn't support HTML5 canvas.

Casting Light and Shadows with Penumbra on a Hex Based Grid.

This is just a tech demo of using a hex grid to display lighting effects suitable for small games. This is the perfect example of very simple of fog of war.

It features the ability to turn on a penumbra, this is the effect you get from having the light source as an area rather than coming from a single infinitely small point. It creates a graduation between the light and dark area.

Controls:

Just the mouse

Clicking the green button allows you to turn on a penumbra.

Notes:

I found that if you use GameMaker and you have WebGL set to disabled you can’t use draw_primitive at all and if you have WebGL enabled draw_primitive performs exceedingly poorly, because of this I fell back to drawing an image of a hexagon and using that. I don’t know if other have a solution to this but it wasn’t worth my time to investigate it any further as the performance is satisfactory however from a programming point of view this irks me

Things I would change:

I would love to find an elegant solution to stop the penumbra from coming through the walls.

I would like to cache the results so you don’t need to do the collision detection, however the collision detection seems to perform better than the caching, I think this is because it actually moves large amounts of data around when updating the arrays so I just stuck with crunching the collision detection on every frame.

When the system loads I have to stagger the creation of the hex grid, not for performance reasons but because I get each tile to create the tiles that surround it and the computer thinks my program has gone recursive and errors. So to prevent this I put a 2 frame gap between the creation of the tile and when it spawns the ones around it.

The Code:
Create Event:
gridaamount = 6 //6 and 4 tessellate but you can use grids that dont tessellate
createangle = 360/gridaamount;
diameter = 15; //15

distancetosee = 300;

distanceoverwalls = diameter; // how far past the walls can it go?

// Save the points that make up this hex
pointsx[gridaamount-1] = '';
pointsy[gridaamount-1] = '';

for (i = 0; i < gridaamount; i ++) {

    pointsx[i] = x + lengthdir_x(diameter-4, createangle*i)
    pointsy[i] = y + lengthdir_y(diameter-4, createangle*i)

}
    
Draw Event:
draw_primitive_begin(pr_trianglelist);

repeat(gridaamount) {
    draw_vertex(x,y)
    draw_vertex(pointsx[i], pointsy[i]);
    if (i+1 >= gridaamount) {ti=0} else {ti=i+1} 
    draw_vertex(pointsx[ti], pointsy[ti]);

    i++ 
}
draw_primitive_end();
    
Step Event:
var dist = distance_to_point(mouse_x,mouse_y)
var checkdis = diameter

if (dist > distancetosee) { 
    visible = true
} else {

    if (collision_line(x+checkdis,y+checkdis,mouse_x,mouse_y,obj_wall,0,1) and 
        collision_line(x-checkdis,y-checkdis,mouse_x,mouse_y,obj_wall,0,1) and
        collision_line(x-checkdis,y+checkdis,mouse_x,mouse_y,obj_wall,0,1) and 
        collision_line(x+checkdis,y-checkdis,mouse_x,mouse_y,obj_wall,0,1)) {
        
        visible = true
    } else {
        visible = false
    }

}
Step Event With Penumbra:
if (obj_global.penumbra > 0) {
    // Casts penumbra shadows with transparency
    if (dist > distancetosee) { 
        transparency = 1
    } else {
        transparency = 0
        if (collision_line(x+obj_global.penumbra,y+obj_global.penumbra,mouse_x,mouse_y,obj_wall,0,1)) {transparency += 0.25} 
        if (collision_line(x-obj_global.penumbra,y-obj_global.penumbra,mouse_x,mouse_y,obj_wall,0,1)) {transparency += 0.25}
        if (collision_line(x-obj_global.penumbra,y+obj_global.penumbra,mouse_x,mouse_y,obj_wall,0,1)) {transparency += 0.25}
        if (collision_line(x+obj_global.penumbra,y-obj_global.penumbra,mouse_x,mouse_y,obj_wall,0,1)) {transparency += 0.25}
    }
} else {
    // Simple collisions with on/off transparency
    if (dist > distancetosee) { 
        transparency = 1
    } else {
        transparency = 0
        if (collision_line(x+checkdis,y+checkdis,mouse_x,mouse_y,obj_wall,0,1) and 
            collision_line(x-checkdis,y-checkdis,mouse_x,mouse_y,obj_wall,0,1) and 
            collision_line(x-checkdis,y+checkdis,mouse_x,mouse_y,obj_wall,0,1) and
            collision_line(x+checkdis,y-checkdis,mouse_x,mouse_y,obj_wall,0,1)) {
            transparency = 1
        }
    }
}