Feypath Cinematic Trailer
▲ 27 r/gamemaker+1 crossposts

Feypath Cinematic Trailer

This has been a wild journey, but the release of Feypath is just over a month away!

I spent this weekend learning a lot of new things as I created the "cinematic" trailer for the game. Among those were dabbling with animation in Clip Studio Paint (I use that to draw a lot), going more in deep with Da Vinci Resolve and doing voice acting for the first time in my life.

Especially the voice part was tricky. Producing a girl sounding voice as a grown up male was challenging, but I spent the last couple of weeks watching tutorials on male to female transitioning voice coaching. That and some minor pitch shifting in Audacity got me a hopefully not too disastrous result :D (feel free to roast me, though)

The one thing that I will bring with me as I move to other projects is that I need to start much earlier with posting things like devlogs and progress videos. I only started a few weeks ago and given that I have no following at all on Youtube and very seldom use social media it is quite hard at this point to reach out to people. This means that my expectations for the Steam release is very low (sitting at like 31 wishlists so far). It's okay though, as the purpose with the project was never really to sell a lot of games, but just to actually learn how Steam works.

With this said I hope that you like the trailer, and if you are interested in Feypath it would be super helpful if you wishlisted it (link in video description). Also, if you have any questions about the game or about the development process I'm eager to answer as best as I can.

youtu.be
u/StyrbjornA — 9 hours ago
▲ 2 r/platformer+2 crossposts

Feypath - Speedrunning Secrets Revealed!

My Feypath related videos have been focused on programming so far, so I felt it was time to make a gameplay related one.

Initially I never thought about having speed running techniques in my game, but one day I ran into a bug that I could not explain at first.

In short what happened was that when the player character (the slime) landed on the edge of tiles, there seemed to be a one pixel offset compared to when the player moved to the edge by "walking" (or sliding, or whatever slimes do).

After finding out that this had to do with how my state machine worked I realized that I wanted to capitalize on the behaviour by making it a feature. Instead of the initial one pixel offset I added two pixels and this was enough for the player to make jumps that were previously not possible.

The "Edge Jump" was born and it became a staple for creating shortcuts through the levels.

A similar story applies to what I call the "Slope Glide". I noticed that there was an irregularity regarding how the player character fell down from slopes and it turned out to have to do with my solution to alterable jump height. I do the "apply gravity twice when the player is not falling downwards and is not holding the jump button" method to allow for shorter jumps. As the player vertical speed equals zero (which means the player is not falling downwards, yet) when moving from "walking state" to "falling state" moving off a slope the player could alter the application of gravity on that single frame to decrease the fall speed slightly.

This is actually useful in a few places, and instead of declaring it a bug and removing it I kept it in the game.

I am curious about if any of you have similar stories to share regarding bugs turning into features? I feel that Gamemaker being so quick for development sometimes makes it prone to introduce things like this.

If you're interested in Feypath it will release later this summer, and is available on Steam for wishlisting. Link is in the description of the video.

youtu.be
u/StyrbjornA — 7 days ago

Feypath - Dynamic Lighting demo

(wishlist of Feypath available on Steam: Feypath on Steam)

A short video showcasing the light engine used in Feypath. It's a custom approach that worked pretty well in my tile based game. If you are interested in the code for my light object, here is it (please feel free to ask any questions):

debug_m = true
tile_size = 16
light_radius = 6
level_width = 16
level_height = 14 
tiles_processed = 0
rects_created = 0
verts_drawn = 0


shadow_surface = surface_create(256, 224)
function init_shadow_surface(){
if not surface_exists(shadow_surface) shadow_surface = surface_create(256, 224)
surface_set_target(shadow_surface)
draw_clear_alpha(c_black, 1)


}

global.map = layer_tilemap_get_id("Tiles")
//Declare level data array
tile_data = array_create(level_width)
for (var _column = 0; _column< level_width; _column++) {
    tile_data[_column] = array_create(level_height)
}

//Populate level data array
for (var _column = 0 ; _column < level_width ; _column ++){
for (var _row = 0 ; _row < level_height ; _row ++){
tile_data[_column][_row] = tilemap_get(global.map, _column, _row) != 0
}
}

function get_tile_at_pixel(_pixel_x,_pixel_y){
return tile_data[floor(_pixel_x / tile_size)][floor(_pixel_y / tile_size)]
}

function generate_skip_tiles_array(){
var _skip_tiles = array_create(level_width)
for (var _column = 0; _column< level_width; _column++) {
    _skip_tiles[_column] = array_create(level_height)
}
return _skip_tiles
}

//DRAWING STUFF

function draw_rectangles(){


verts_drawn = 0
tiles_processed = 0
rects_created = 0
var _start_column = max(0, floor(x / tile_size) - light_radius)
var _end_column = min(level_width, floor(x / tile_size) + light_radius)
var _start_row = max(0, floor(y / tile_size) - light_radius)
var _end_row = min(level_height, floor(y / tile_size) + light_radius)

var _skip_tiles = generate_skip_tiles_array()

init_shadow_surface()

draw_set_color(c_white)

draw_circle_colour(x, y , light_radius * tile_size, c_white, c_black, 0)
for (var _column = _start_column ; _column < _end_column; _column ++){
for (var _row = _start_row ; _row < _end_row; _row ++){

if tile_data[_column][_row] tiles_processed ++


//Check if we can start the rectangle, the rectangle can be started if the tile is solid and it does not exist in _skip_tiles
if tile_data[_column][_row] and not _skip_tiles[_column][_row]{

rects_created ++
var _rectangle_width = 1
var _rectangle_height = 1

//checking how wide our rectangle will be
var _right_border_reached = false
while not _right_border_reached{
var _next_column = _column + _rectangle_width
if _next_column >= _end_column _right_border_reached = true
else if (not tile_data[_next_column][_row]) or _skip_tiles[_next_column][_row] _right_border_reached = true
else {
_rectangle_width ++
_skip_tiles[_next_column][_row] = true
}

}
//using the info about rectangle width, we check how many rows we can add (if any)
var _failed_to_add_row = false
var _next_row = _row + 1
var _next_column = _column
while not _failed_to_add_row{


if _next_row >= _end_row  _failed_to_add_row = true
else if (not tile_data[_next_column][_next_row]) or _skip_tiles[_next_column][_next_row]{
_failed_to_add_row = true

}
else{

_next_column ++
if _next_column == _column + _rectangle_width{

for (var _skip_col = _column; _skip_col < _column + _rectangle_width; _skip_col ++){
_skip_tiles[_skip_col][_next_row] = true
}
_rectangle_height ++
_next_row ++
_next_column = _column
}
}

}
//draw the rectangle DEBUG ONLY


if debug_m {
draw_set_color(c_white)
draw_rectangle(_column * tile_size, _row * tile_size, (_column + _rectangle_width) * tile_size,(_row + _rectangle_height) * tile_size, 1)
}
//Find the two corners
var _left_x = _column * tile_size
var _upper_y = _row * tile_size 
var _right_x = (_column + _rectangle_width) * tile_size
var _bottom_y =(_row + _rectangle_height) * tile_size 
var _corner_pairs = [
[_left_x, _upper_y, _right_x, _upper_y],
[_left_x, _upper_y, _right_x, _bottom_y],
[_left_x, _upper_y, _left_x, _bottom_y],

[_left_x, _bottom_y, _right_x, _bottom_y],
[_right_x, _upper_y, _left_x, _bottom_y],
[_right_x, _upper_y, _right_x, _bottom_y]

]
var _largest_angle = 0
var _largest_pair = 0
for (var _corner_pair = 0; _corner_pair < 6; _corner_pair ++){
var _x1 = _corner_pairs[_corner_pair][0]
var _y1 = _corner_pairs[_corner_pair][1]
var _x2 = _corner_pairs[_corner_pair][2]
var _y2 = _corner_pairs[_corner_pair][3]

var _ang1 = point_direction(x, y, _x1, _y1);
var _ang2 = point_direction(x, y, _x2, _y2);

var _current_angle = abs(angle_difference(_ang1,_ang2))

if _current_angle > _largest_angle{
_largest_angle = _current_angle
_largest_pair = _corner_pair
}
}

//The "opposing pair" are the corners currently not present in the largest pair - we need to use this in a bit if the largest pair form a diagonal in the rectangle
var _opposing_pair = (_largest_pair + 3) mod 6

// make the corner points more readable
var _point_1_x = _corner_pairs[_largest_pair][0]
var _point_1_y = _corner_pairs[_largest_pair][1]
var _point_2_x = _corner_pairs[_largest_pair][2]
var _point_2_y = _corner_pairs[_largest_pair][3]


var _closest_corner_x = _corner_pairs[_opposing_pair][0]
var _closest_corner_y = _corner_pairs[_opposing_pair][1]

if point_distance(x,y,_corner_pairs[_opposing_pair][0],_corner_pairs[_opposing_pair][1]) > point_distance(x,y,_corner_pairs[_opposing_pair][2],_corner_pairs[_opposing_pair][3]){

_closest_corner_x = _corner_pairs[_opposing_pair][2]
_closest_corner_y = _corner_pairs[_opposing_pair][3]
}
//Here we determine if we need to account for the "closest corner". If the largest pair are on a diagonal we need to add the closest corner of the rectangle.
var _draw_closest_corner = false
if _corner_pairs[_opposing_pair][0] != _corner_pairs[_opposing_pair][2]{
if _corner_pairs[_opposing_pair][1] != _corner_pairs[_opposing_pair][3]{
_draw_closest_corner = true
}
}

//Find the extension points

var _angle_1 = point_direction(x, y, _point_1_x, _point_1_y)
var _angle_2 = point_direction(x, y, _point_2_x, _point_2_y)
var _middle_angle = _angle_1 + angle_difference(_angle_2, _angle_1) * 0.5

//nudge the outer angles outward to remove shadow gaps

var _nudge_amount = 2

//we only do the nudge if the angle is either very close to horizontal or vertical. It is in those cases we might run into gaps
if abs(cos(degtorad(_angle_1))) < 0.02 or abs(sin(degtorad(_angle_1))) < 0.02{
if _angle_1 > _middle_angle _angle_1 += _nudge_amount else _angle_1 -= _nudge_amount
}
if abs(cos(degtorad(_angle_2))) < 0.02 or abs(sin(degtorad(_angle_2))) < 0.02{
if _angle_2 > _middle_angle _angle_2 += _nudge_amount else _angle_2 -= _nudge_amount
}
//end of nudge

var _ext_point_1_x = x + cos(degtorad(_angle_1)) * light_radius * 2 * tile_size//We multiply by two to make sure our wedge properly covers the shadowed area
var _ext_point_1_y = y - sin(degtorad(_angle_1)) * light_radius * 2 * tile_size


var _ext_point_2_x = x + cos(degtorad(_angle_2)) * light_radius * 2 * tile_size 
var _ext_point_2_y = y - sin(degtorad(_angle_2)) * light_radius * 2 * tile_size

var _ext_point_3_x = x + cos(degtorad(_middle_angle)) * light_radius * 2 * tile_size
var _ext_point_3_y = y - sin(degtorad(_middle_angle)) * light_radius * 2 * tile_size


if debug_m {
draw_set_color(c_blue)

draw_circle(_corner_pairs[_largest_pair][0],_corner_pairs[_largest_pair][1], 3, 1)
draw_circle(_corner_pairs[_largest_pair][2],_corner_pairs[_largest_pair][3], 3, 1)

draw_set_color(c_green)

draw_circle(_ext_point_1_x,_ext_point_1_y, 3, 1)
draw_circle(_ext_point_2_x,_ext_point_2_y, 3, 1)
draw_circle(_ext_point_3_x,_ext_point_3_y, 3, 1)

draw_set_color(c_red)
if _draw_closest_corner draw_circle(_closest_corner_x, _closest_corner_y, 3, 1)


draw_set_color(c_yellow)
draw_line(_corner_pairs[_largest_pair][0],_corner_pairs[_largest_pair][1],_ext_point_1_x,_ext_point_1_y)
draw_line(_corner_pairs[_largest_pair][2],_corner_pairs[_largest_pair][3],_ext_point_2_x,_ext_point_2_y)
draw_line(_ext_point_1_x,_ext_point_1_y, _ext_point_3_x, _ext_point_3_y)
draw_line(_ext_point_3_x, _ext_point_3_y,_ext_point_2_x,_ext_point_2_y)


}


//Draw the triangles
draw_set_color(c_black)
if debug_m draw_set_alpha(0.2)
draw_primitive_begin(pr_trianglestrip)

if _draw_closest_corner{
draw_vertex(_closest_corner_x,_closest_corner_y )
verts_drawn ++
}
draw_vertex(_point_1_x,_point_1_y)

draw_vertex(_point_2_x,_point_2_y )
draw_vertex(_ext_point_1_x ,_ext_point_1_y)
draw_vertex(_ext_point_2_x ,_ext_point_2_y)
draw_vertex(_ext_point_3_x ,_ext_point_3_y)
verts_drawn += 5 

draw_primitive_end()
if debug_m draw_set_alpha(1)
}


}
}
surface_reset_target()

if not debug_m shader_set(sh_360_light)

draw_surface(shadow_surface,floor(x / 256) * 256,floor(y / 224) * 224)
if not debug_m shader_reset()

}
youtu.be
u/StyrbjornA — 9 days ago

Trace surfaces in Gamemaker Studio 2

https://youtu.be/9PhHd75mqIA

Here is a short video showing how you can create nice trace effects using surfaces in Gamemaker Studio 2.

When making my game I accidentally forgot to do draw_clear_alpha on one of my surfaces, and that led to some experimentation that eventually turned out useful.

I notice that some people here manage to post images alongside text, making nice guides, but I don't know how to do that. Therefore I posted the link to a youtube guide instead, showing the visuals of the effect.

In short what we do is draw to a surface like normal, but instead of clearing the surface each frame we draw a semi transparent black rectangle over it, to gradually fade it out. When drawing the actual surface we do it with gpu_set_blendmode(add) to make sure that the blackness of the rectangle is not drawn solid on our background.

Example object (a simple object drawing a white circle to screen):

Create Event

trace_surface = surface_create(room_width, room_height)
function set_trace_surface(){
  if not surface_exists(trace_surface){
    trace_surface = surface_create(room_width, room_height)
  }
  surface_set_target(trace_surface)
  draw_set_color(c_black)
  draw_set_alpha(0.05)
  draw_rectangle(0,0,room_width,room_height,0)
  draw_set_alpha(1)
}

Draw Event

set_trace_surface()
draw_set_color(c_white)
draw_circle(x, y, 32, 0)
surface_reset_target()
gpu_set_blendmode(bm_add)
draw_surface(trace_surface, 0, 0)
gpu_set_blendmode(bm_normal)

When we move the circle around we get a nice trail/trace.

I've used this to create rain particles and some of the creatures appearing in my game Feypath. If you want to check it out (and wishlist it, if you feel generous :) ), you can find it here:

Feypath on Steam

u/StyrbjornA — 11 days ago
▲ 2 r/GameMusicComposition+1 crossposts

Feypath - Dreadfall Thicket music theme

The Dreadfall Thicket marks the departure from the safety and shelter of The Cave.

About the game:

Feypath is a single button precision platformer that is suitable for those that like punishing but fair gameplay. Save the princess and uncover the secrets of the Demon Kingdom by jumping to the top of the world!

I made the theme in FL Studio.

If you are interested you can wishlist the game on Steam: Feypath on Steam

youtube.com
u/StyrbjornA — 14 days ago

Feypath Concept Art

This is a character from a game I am developing named Feypath. It started out as a pencil drawing but I scanned it and did the coloring in Clip Studio Paint.

I'm not sure if I'm allowed to post links here, but if you want to check the game out it should be searchable in Steam.

u/StyrbjornA — 15 days ago

Feypath - "The Cave" music theme

The Cave is where we start our journey in Feypath, accepting our task to return the soul to the Demon Princess.

About the game:

Feypath is a single button precision platformer that is suitable for those that like punishing but fair gameplay. Save the princess and uncover the secrets of the Demon Kingdom by jumping to the top of the world!

I made the theme in FL Studio.

If you are interested you can wishlist the game on Steam: Feypath on Steam

youtube.com
u/StyrbjornA — 16 days ago

Multiple versions of Gamemaker at once?

I've spent the last year developing a game that is currently going through the review process in Steam. I've used Gamemaker Studio 2 (bought many years back when it was a perpetual license) for the project but I am aiming to move on to the latest release of Gamemaker for my next game.

The question here is if I can somehow have both versions of Gamemaker installed at once, to make sure I do not break my already fragile Steam game project?

reddit.com
u/StyrbjornA — 18 days ago
▲ 18 r/platformer+2 crossposts

Feypath - coming to Steam in summer 2026

Feypath - Youtube Trailer

Feypath is a single button precision platformer that is suitable for those that like punishing but fair gameplay. Save the princess and uncover the secrets of the Demon Kingdom by jumping to the top of the world!

The game is available for wishlist on Steam now!
Feypath on Steam

u/StyrbjornA — 12 days ago