Category Archives: Gamemaker Studio

Tutorials on how to build cool stuff in Gamemaker studio.

Lighting with Gamemaker Studio 2

After I started working on my new platformer, I made some tweaks to it to try to separate it from the game that served as the original inspiration. The easiest change, that I thought of, was to make my game endless. Another idea was to have some sort of mechanic that would make my game more difficult the further you went along.

Keeping in mind that this game is about the player falling further and further down a hole in the ground, I thought that it would make sense that the visibility of the player would get progressively more restrictive the further down the player fell.

After some quick googling I found a simple little tutorial done by Zach Bell Games that looked perfect (Also if you don’t follow his work, you really should). The Zach Bell Games tutorial requires that you know a thing or two about surfaces in Gamemaker Studio 2, and even if you don’t it’s pretty easy to follow along. The only issue with the Zach Bell Games tutorial is that this is designed for a platformer with a fixed room size, which you can tell from this little section of code.


// Create a surface the size of the current room
// You could also create a surface the size of the current view, but I won't get into that
surf = surface_create(room_width, room_height);

They are making a surface the size of the entire room to create the darkness effect. This method doesn’t work with my endless platformer, because if a surface gets too big it will cause my game to crash. The tutorial, at Zach Bell Games, didn’t get into making a lighting system that only covers the view (as seen in that code block), so I needed to do it myself.

To start with this process I made a create event that is very similar to the one in the Zach Bell Games tutorial. The only major difference is that instead of making a surface that is the size of the room, it will only be the size of the view / camera. You are able to find the size of your view by going into the room editor and looking at the section “Viewport and Cameras”. My viewport is only 352 by 512 so I created a surface that size inside of my create event.


// Create Event
// Create the surface for lighting
surface = surface_create(352, 512);

// Set the surface target to the new surface instead of the application surface
surface_set_target(surface);

// Clear the surface
draw_clear_alpha(c_black, 0);

// Reset target surface back to the application surface
surface_reset_target();

Now that this surface is exactly the size of the view I can fill the surface with a translucent black layer black inside of the step event to give the effect of darkness. You should also keep in mind that surfaces are volatile and can be deleted randomly, so always check to make sure the surface still exists.


// Step Event

// Check that surface exists
if (surface_exists(surface))
{
// Switch surface to draw to
surface_set_target(surface);

// Select the colour to draw
draw_set_color(c_black);

// Set the alpha value to draw at
draw_set_alpha(0.8);

// Fill View with black
draw_rectangle(0,0, 352, 512, 0);

// Reset the alpha
draw_set_alpha(1);

// Reset the target
surface_reset_target();
}
else
{
// Run the create again if the surface isn't detected
surface = surface_create(352, 512);
surface_set_target(surface);
draw_clear_alpha(c_black, 0);
surface_reset_target();
}

Just filling the surface with black, in the step event ,doesn’t draw anything to the screen. To be able to draw the surface to the screen you need to run some code in the “draw end” event; however, this is where things get a little bit tricky. We now need the surface to follow the view because it isn’t going to fill the whole room. The tutorial by Zach Bell Games drew the surface at the position 0,0 and that isn’t going to work for this system because the surface would stay at the top left of the play area.

The first thing that you would probably think of is to draw the surface at the player’s current position. This might make sense because more than likely your view is set to follow the player. So if I go to the “draw end” event and run the code draw_surface(surface, obj_player.x, obj_player.y); this is what our view will look like.

The draw_surface command starts to draw the surface at the top left hand corner, when we provide it the x and y position of the player Gamemaker will start drawing the surface at the player object’s pivot point. Clearly, that will not do, I need to draw the surface starting at the top left of the view. Thankfully Gamemaker studio provides a method to be able to get top left X and Y position of whatever view you want. By making 2 local variables for the x and y position you can run the commands camera_get_view_x(camera) and camera_get_view_y(camera) to get the X and Y coordinates of the top left corner of the view (This command is different if you are using Gamemaker Studio 1.4). We can update our draw end event and see what we get.


// Draw End Event
if (surface_exists(surface))
{
// Get the X position of the view
var _x = camera_get_view_x(camera);

// Get the Y position of the view
var _y = camera_get_view_y(camera);


// Check what view we are on and draw the surface at _x and _y
if (view_current == 0)
draw_surface(surface, _x, _y);
}
else
{
// Run the create again if the surface isn't detected
surface = surface_create(352, 512);
surface_set_target(surface);
draw_clear_alpha(c_black, 0);
surface_reset_target();
}
}

Now I are getting somewhere. The black surface is now being drawn at the top left hand side of the camera, and it will always stay in front of the view no matter where the player moves because we are getting its position each time the draw_end event is run.

Now I just need to cut out the little circle of light around the player. I can go back to the step event and add in a couple more functions to take care of this. I need to set the blend mode to subtract, set the colour to white, and then I am ready to draw our circle (but don’t forget to set the blend mode back to normal after you draw the circle around the player). So now inside of the step event I check if the player object exists, and run the draw_circle function and feed it the x and y position of the player and see what happens.


// Draw Circle in Step Event

// Set blend mode to subtract
gpu_set_blendmode(bm_subtract);

// Set colour to draw
draw_set_color(c_white);

// Check if Player object exists
if (instance_exists(obj_player))
{
with (obj_player)
{
// Draw the light circle
draw_circle(x + random_range(-1, 1), y + random_range(-1, 1), 50 + random_range(-1, 1), false);
}
}
gpu_set_blendmode(bm_normal);
draw_set_alpha(1);
surface_reset_target();

What is going on here? Well, when we draw the circle we are providing it the X and Y position of the player in the room, and because the surface is following the view our little light circle will run away from the player. What I need to do is draw the circle at the X and Y position of the player inside of the view. To fix this I wrote a simple little script to be able to figure out the X and Y position of any object inside of the view.


// x_in_view(x)

// X position of Object
var _x = argument0;

// Check X position of object in view
_x = _x - camera_get_view_x(global.camera1);

// Return position of object in view
return _x;

That code block was just for the X position, I created a separate script for the Y only I changed the function to camera_get_view_y. With these two scripts I can figure out the X and Y position of the player object inside of the view and I should be able to cut out a circle of light around just the player. This is what the finished step event looks like.


// Finished Step Event
if (surface_exists(surface))
{
// Switch surface to draw to
surface_set_target(surface);

// Select the colour to draw
draw_set_color(c_black);

// Set the alpha value to draw at
draw_set_alpha(0.8);

// Fill View with black
draw_rectangle(0,0, 352, 512, 0);

// Set blend mode to subtract
gpu_set_blendmode(bm_subtract);

// Select the colour to draw
draw_set_color(c_white);

// Check if player object exists
if (instance_exists(obj_player))
{
with (obj_player)
{
// Get X position of player in view
var _x = x_in_view(x);

// Get Y position of player in view
var _y = y_in_view(y);

// Draw the light circle
draw_circle(_x + random_range(-1, 1), _y + random_range(-1, 1), 50 + random_range(-1, 1), false);
}
}

// reset blend mode
gpu_set_blendmode(bm_normal);

// Reset alpha
draw_set_alpha(1);

// Reset target
surface_reset_target();
}
else
{
// Run the create again if the surface isn't detected
surface = surface_create(352, 512);
surface_set_target(surface);
draw_clear_alpha(c_black, 0);
surface_reset_target();
}

Now that is looking a lot better. The scripts x_in_view and y_in_view are also able to return the view position of any other object I want. So because my game has enemies that also generate light I just copied the code from the player and provided the x and y positions of my enemies.

There it is, a lighting system that only covers the view and will work in an endless runner.

Until Next Time
– Steven

Making a Downwell

Ludum Dare has come and gone and I, being the busy person that I am, did not participate. But not to let a good idea go to waste I thought that maybe I could make a game from scratch really quickly. After all, I now have some experience working in Gamemaker Studio 2.

I have been on a little bit of a Roguelike kick recently playing games in this genre. Rogue Legacy and Spelunky are 2 of the games that I like to play most but recently I have started to play a little gem called Downwell. Downwell is a quaint little rogue like available for just about every platform under the sun. I have been playing it on my Playstation Vita and on my smartphone and I started thinking about what it would take to make something similar. After all, imitation is the sincerest form of flattery.

If we break Downwell into its 2 most basic parts there are vertical procedural levels and platforming. I have the platforming part down thanks to my work on Cursed Squire, and the platforming in this game would be even simpler than that, because the player will be constantly falling, I will not need to have the player be able to jump through platforms like in Cursed Squire, so I will “borrow” a lot of my platforming code from that game.

The second part is procedural levels. This part is going to be the hardest to do, mainly because of tweaking with variables so that levels are not incredibly easy nor completely impossible. Working on the newest game with the team over at Elektri has given me an idea of how to start with these levels using a ds_grid.

The first thing I will need to do is create a new script and make some macros for constant variables that I will need to refer back to several times.

The cells of the grid are going to be 32 pixels by 32 pixels each, and the values of the cells are either going to have a platform on them or be empty space that the player can fall through. To be able to get a visual representation of the level I created a 32*32 sprite and just made it solid red and a matching game object just called obj_solid. This game object is what is going to be put into the room to build our level.


#macro CELL_WIDTH 32
#macro CELL_HEIGHT 32
#macro PLATFORM -5
#macro VOID -6

In order to actually build the levels I created a level manager game object and put it into an empty room in Gamemaker. Downwell has tall and narrow levels, to build something similar my rooms are going to be 9 cells across by 100 cells in height. Inside the create event of my level manager I adjust the size of the room, and create local variables to reference the width and height of my level grid. Finally I will create the ds_grid that I will use to create the levels.


// Resize the room
room_width = CELL_WIDTH * 9;
room_height = CELL_HEIGHT * 100;

// Set the dimensions of the grid
var width = room_width div CELL_WIDTH;
var height = room_height div CELL_HEIGHT;

// Create the grid
grid = ds_grid_create(width, height);

Now that the grid has been created I can go about this in 2 different ways. I can fill the room with platforms and then carve out the empty space, or I can leave the grid empty and go through and place the platforms. With this example I am going to try to leave the room empty and add platforms instead of filling the room with platforms and carving out the empty space. So the next thing is to flag every single tile of our grid as void.


// Fill the grid with void
ds_grid_set_region(grid, 0, 0, width, height, VOID);

To place platforms inside of the empty grid I decided to loop through the entire grid and then I will choose if I should place a platform at a tile. I gave my script a 25% chance to place down a floor tile or leave it as empty space.


// The odds for creating a platform
var odds = 3;

// Flag the platforms
for (var cx = 0; cx < width; cx ++)
{
for (var cy = 0; cy < height; cy++)
{
if (irandom(odds) == odds)
{
grid[# cx, cy] = PLATFORM;
}
}
}

After filling the grid with the flags to see if the space is a platform or empty, I need to loop through the grid a second time and spawn in the ground platforms.


for (var xx = 0; xx <= width; xx++)
{
for (var yy = 0; yy < height; yy++)
{
if (grid[# xx, yy] == PLATFORM)
instance_create_layer (xx * CELL_WIDTH, yy * CELL_HEIGHT, "Instances", obj_platform);
}
}

All that is left is to test the game and see how it looks (I should point out that if you do not turn on views and give your camera a 16*9 vertical aspect ratio, your room will look a little bit weird).

Looking at this, apparently a 25% chance to spawn in a platform is a little bit too much. There is not enough space to be able to jump or free-fall. I want a fewer platforms than that a more negative space. Playing around with the numbers will be able to get you the amount of platforms that you would want for your own roguelike platformer. I switched mine over about 16% and I am pretty happy with the results.

After adding in the player object and my platforming code, ripped right out of Cursed Squire, I have a game object that I am able to navigate through a procedural generated well. All that is needed to turn this into something that people would want to play is some enemy types and combat mechanics.

Until Next Time
– Steven