We would love you to play this but your browser doesn't support HTML5 canvas.
Just use a different browser and you can happily play this game.

How to do HTML5/WebGL Field of View (FoV) in GameMaker
Controls :

W,A,S,D – Move left player
Arrow Keys – Move right player

Objective :

For users using Chrome; the above example can be a little choppy in Chrome because it doesn’t like drawing lots of primitives. I could change it to not draw shadows behind the player etc, however the code is the most efficient way of doing it for every other platform, so I didn’t want to fundamentally move away from the best practices for a small group of people on a platform my readers are unlikely to be using. Exe and App versions work perfectly.

Above we can see an example of casting a cone in front of the player that shows the area that is not blocked by walls. This could be used for lighting (for example if the player was holding a torch) or for detecting what enemies are in front of the player.

If you are new to Masking in GameMaker I have a guide here.

It is programmed in GameMaker and was designed to work in browsers so this is fully compatible with the GameMaker HTML5 extension.

This goes hand in hand with this example of lighting in GameMaker I made. In my lighting demo I cast shadows from the walls, however for this I actually needed the opposite shape and wanted the lit area between the player and the wall. To achieve this I used surfaces.

Code :
    /// CREATE EVENT
    // FOV stuff
    fovsurface = surface_create(room_width, room_height);

    shadowsize = 1100 // how far the walls cast a shadow
    fovarc = 90 // how big the vof arc is 

    lookdir = point_direction(x,y,room_width/2,room_height/2)

    /// DRAW EVENT
    if (surface_exists(fovsurface)) {
	draw_surface(fovsurface, 0, 0);
    }

    /// STEP EVENT

    /// Make the FOV and put it on a surface 

    surface_set_target(fovsurface)

    draw_set_colour(c_white)
    draw_set_alpha(0.4)
    draw_rectangle(0,0,room_width,room_height,false)
    draw_set_alpha(1)

    draw_set_blend_mode(bm_subtract)

    /// ANYTHING I DRAW HERE WILL BE REMOVED FROM THE FOV

    with(obj_wall) { /// REMOVE EVERYTHING BEHIND THE WALLS


	draw_set_colour(c_black)

	draw_primitive_begin(pr_trianglestrip)

	    draw_vertex(bbox_left, bbox_top);
	    var dir = point_direction(other.x,other.y,bbox_left,bbox_top)
	    draw_vertex(x+lengthdir_x(other.shadowsize,dir), y+lengthdir_y(other.shadowsize,dir));

	    draw_vertex(bbox_left, bbox_bottom);
	    var dir = point_direction(other.x,other.y,bbox_left,bbox_bottom)
	    draw_vertex(x+lengthdir_x(other.shadowsize,dir), y+lengthdir_y(other.shadowsize,dir));

	    draw_vertex(bbox_right, bbox_top);
	    var dir = point_direction(other.x,other.y,bbox_right,bbox_top)
	    draw_vertex(x+lengthdir_x(other.shadowsize,dir), y+lengthdir_y(other.shadowsize,dir));

	    draw_vertex(bbox_right, bbox_bottom);
	    var dir = point_direction(other.x,other.y,bbox_right,bbox_bottom)
	    draw_vertex(x+lengthdir_x(other.shadowsize,dir), y+lengthdir_y(other.shadowsize,dir));

	draw_primitive_end()

    }

    // DRAW WHAT IS BEHIND THE PLAYER IN 

    draw_primitive_begin(pr_trianglefan);
    draw_vertex(x, y);

    var fovarcprocessed = fovarc/10
    for(i=0; i<=36-fovarcprocessed; i++) {
	xx = lengthdir_x(1000, (i*10)+lookdir+(fovarc/2))
	yy = lengthdir_y(1000, (i*10)+lookdir+(fovarc/2))
	draw_vertex(x+xx, y+yy);
    }

    draw_primitive_end();

    draw_set_blend_mode(bm_normal)
    surface_reset_target();

    
Code :

I’m still not sure if this is called Ray Tracing however basically in GameMaker I create a surface the same size as the screen (a surface is a blank image I can add to and draw to the screen when I want). With this surface I fill it in with a solid white colour.

I then loop around every single wall like I did in my lighting demo. For each corner of every wall I draw a line from the player through this corner and extend this line for 1100px. I then take this point and draw triangles from each of these new points to the corner of the wall. Normally I would fill in all these triangles black to cast a shadow however for this I am removing the white from the surface where these triangles would be using draw_set_blend_mode(bm_subtract). This gives me a negative of the shadows that I can now draw to the screen.

Now this is great for some people, however it draws light being cast in every direction and I wanted to limit the field of view to only be an arc in front of the player that was about 90 degrees. Because we now have this surface we can keep drawing on it all the places we DON’T want the player to be able to see.

Below is the code I use to draw on the surface the area behind the player (remembering that by drawing on the surface I remove the lighting for it). This leaves just the cone in front of the player left on the surface.

It works by drawing a huge pie chart around the player made of 36 segments. Each segment is 10 degrees arc making a whole circle, any of these segments not drawn in makes the arc in front of the player, I then rotate this huge pie chart to point in the direction the player is, magic this gives the illusion the player is holding a torch.

If you want to make the arc bigger or smaller you can change fovarc=90 in the create event. I have set it to 90 only because I think it looks nice. It you wanted something to be more like a torch 30 degrees would look good, if you wanted to display the field of view of a human it is more like 120 degrees.

    draw_primitive_begin(pr_trianglefan);
    draw_vertex(x, y);

    var fovarcprocessed = fovarc/10
    for(i=0; i<=36-fovarcprocessed; i++) {
	xx = lengthdir_x(1000, (i*10)+lookdir+(fovarc/2))
	yy = lengthdir_y(1000, (i*10)+lookdir+(fovarc/2))
	draw_vertex(x+xx, y+yy);
    }

    draw_primitive_end();
    

Below is an image that might make it more obvious what is happening. It shows the lines being extended from every wall corner to the edge of the screen. I can then fill those in to cast shadows.

The advantage of using surfaces is the ability to have many light sources on the page at the same time. In the demo at the top I only have two, however this could easily be adapted to run a whole lighting engine with multiple different coloured lights overlapping.

When doing complex lighting the major worry is always performance. This is doubly important because of how bad browsers are at performing graphical tasks. However the underlining technology here is actually very efficient. The exe version of this runs on my computer at about 600fps so you can see there is plenty of room to add many more light sources and walls before it even starts to be an issue.

Notes :

Just like with the original lighting example it does require WebGL support, while basically all browsers do support WebGL some people might be running old versions. I think you could adapt this to run without WebGL however im not sure if it would be worth writing less efficient code for a very small minority of people.

If you want to test if WebGL is working on your browser you can use this page.

About the Article:
Medium Difficulty
GameMaker
By David Strachan
Get an E-mail when I post something new: Signup