Jump to content


Photo
* * * * * 3 votes

Silhouette Objects Behind Walls


  • Please log in to reply
21 replies to this topic

#1 seanMombo

seanMombo

    GMC Member

  • GMC Member
  • 1790 posts
  • Version:GM:Studio

Posted 07 September 2015 - 09:40 PM

Silhouette objects behind walls!

 

  • Description: How to show and object's silhouette when behind tiles/objects
  • GM Version: GM:S
  • Registered: Don't think so
  • File Type: .gmz
  • File Size: 1.78 MB
  • File Link: http://host-a.net/u/.../Silhouette.gmz
  • Tags: tutorial, silhouette, outline, behind, walls, hyper, light, drifter

 

Hey everyone, I have an awesome effect that I would like to share with you all. I struggled with this for a while and I finally came up with something decent!

 

Screenshot

 

kyZzA4q.png

 

 

 

How it works: ( You need to know how to use surfaces )

tile=tile_get_ids() //Get all the tiles in the room
 
surface_set_target(tileSurf)//draw to tileSurf
draw_clear_alpha(c_black,0)//clear tileSurf

for(i=0;i<array_length_1d(tile);i++)//loop through all the tiles and grab their data
{
    bk=tile_get_background(tile[i])
    xx=tile_get_x(tile[i])
    yy=tile_get_y(tile[i])
    
    left=tile_get_left(tile[i])
    top=tile_get_top(tile[i])
    
    width=tile_get_width(tile[i])
    height=tile_get_height(tile[i])
    
    //Only draw the tiles if they are below the player
    //This allows the player to be infront, and behind objects, and only draw the silhouette if player is behind them.
    if yy+height<objPlayer.y continue 
    
    //draw the tile to the surface
    draw_background_part(bk,left,top,width,height,xx-surfXPos,yy-surfYPos)
}
surface_reset_target()//reset draw target

On one surface you draw the tiles in your room (This is not the most efficient implementation of this so larger rooms will need optimization)

 

Then this line is important

    //Only draw the tiles if they are below the player
    //This allows the player to be infront, and behind objects, and only draw the silhouette if player is behind them.
    if yy+height<objPlayer.y continue 

We don't not draw tiles that are lower than the player. (note that objPlayer.y should be the lowest point on the player, ie his feet)

This allows the player to walk in front of walls without the silhouette appearing, but if he goes behind them then the silhouette will appear.

 

 

Then we use a special blend mode to only draw the part of the silhouette that is overlapping with the wall

 

 

  1. clear the surface and draw the tile surface

  2. set the blend mode to (bm_dest_alpha,bm_inv_dest_alpha)

  3. set the silhouette color

  4. draw the player object

  5. reset all the drawing functions

surface_set_target(silhouetteSurf)//draw to silhouetteSurf

    draw_clear_alpha(c_black,0) //clear surface
    draw_surface(tileSurf,0,0) //draw the tile surface to silhouetteSurf

    draw_set_blend_mode_ext(bm_dest_alpha,bm_inv_dest_alpha) //Important blend mode to draw the silhouette
      
    d3d_set_fog(1,color,0,0) //This set's the color of your silhouette
    
    //draw the player to the surface so a silhouette can be made from it
    //IMPORTANT- you must still draw you player sprite normally at a lower depth than this object 
    //so the silhouette can be drawn above the player sprite
    //The objPlayer's depth is -10 and objSilhouette's depth is -100
    with objPlayer 
        draw_sprite_ext(sprite_index,image_index,x-other.surfXPos,y-other.surfYPos,1,1,0,-1,.5) 
 
    d3d_set_fog(0,c_fuchsia,0,0) //Stop coloring the sprite
        
    draw_set_blend_mode(bm_normal) //reset blend mode
       
surface_reset_target()//reset draw target

Finally we draw the tile surface then the silhouette surface,

 

Drawing the tile surface covers the actual player sprite, then we draw the player's silhouette overtop of that.


Edited by seanMombo, 08 September 2015 - 06:06 PM.

  • 1

#2 chance

chance

    GMC Member

  • Global Moderators
  • 8762 posts
  • Version:GM:Studio

Posted 08 September 2015 - 07:15 PM

The effect is interesting.   And I think this could make a useful tutorial.   The overall formatting is fine, and there are plenty of comments in the code (along with your explanation in the post).  So that's good.  However, I'll ask you to do a bit more before we accept it. 
 
It lacks proper line termination (semi-colons), and that can lead to problems.  So that should be fixed.   Also, the template should be filled out more carefully.   I mean, "uses surfaces" is not a GM version.  :P  Just teasing...I'm sure that's just an oversight.
 
My other comment is to consider moving some of the DRAW event code (in objSilhouette) into the CREATE event.  It's not efficient to re-check the (x,y) positions of each tile, along with their dimensions, in every DRAW step.  They aren't moving, or changing size.  My approach would be to use arrays for those tile parameters, and define them once when the room starts.
 
Your approach still runs fast enough, but I try to avoid repetitive calculations in the STEP and DRAW events.   So that's something to consider.
 
EDIT:
OK, I see you replaced the old version with a new one.  And you addressed my concerns.   So I'll move this back to Tutorials.
 
I hope people find this useful.
  • 0

#3 nott

nott

    GMC Member

  • New Member
  • 2 posts
  • Version:GM:Studio

Posted 08 September 2015 - 11:27 PM

How would one apply this to objects in addition to tiles?


  • 0

#4 seanMombo

seanMombo

    GMC Member

  • GMC Member
  • 1790 posts
  • Version:GM:Studio

Posted 09 September 2015 - 12:16 PM

When you draw the tile surface you are gathering all of the tiles in the room and drawing them to the surface, as seen below.

tile=tile_get_ids() //Get all the tiles in the room
 
surface_set_target(tileSurf)//draw to tileSurf
draw_clear_alpha(c_black,0)//clear tileSurf

for(i=0;i<array_length_1d(tile);i++)//loop through all the tiles and grab their data
{
    bk=tile_get_background(tile[i])
    xx=tile_get_x(tile[i])
    yy=tile_get_y(tile[i])
    
    left=tile_get_left(tile[i])
    top=tile_get_top(tile[i])
    
    width=tile_get_width(tile[i])
    height=tile_get_height(tile[i])
    
    //Only draw the tiles if they are below the player
    //This allows the player to be infront, and behind objects, and only draw the silhouette if player is behind them.
    if yy+height<objPlayer.y continue 
    
    //draw the tile to the surface
    draw_background_part(bk,left,top,width,height,xx-surfXPos,yy-surfYPos)
}
surface_reset_target()//reset draw target

All you have to do to add objects is use a with statement to draw the objects

surface_set_target(tileSurf)//draw to tileSurf
draw_clear_alpha(c_black,0)//clear tileSurf

with objWall
   draw_self()

surface_reset_target()//reset draw target

Whatever you draw to the tile surface will be used as a mask which the silhouette can be drawn on.


  • 1

#5 jackhigh24

jackhigh24

    GMC Member

  • GMC Member
  • 1346 posts
  • Version:GM:Studio

Posted 14 September 2015 - 08:04 PM

that does look a good idea and good tut to sean nice job glad you just popped that on that other thread i just commented on, i don't usually come a look at these tuts but think i better as there seems to be some good ideas going around, again nice job man. 


  • 0

#6 xeroxaero

xeroxaero

    GMC Member

  • New Member
  • 17 posts
  • Version:GM:Studio

Posted 15 September 2015 - 03:57 PM

Works and looks great, thanks for sharing!


  • 0

#7 seanMombo

seanMombo

    GMC Member

  • GMC Member
  • 1790 posts
  • Version:GM:Studio

Posted 18 September 2015 - 09:35 PM

Thanks guys!

This was something that I wanted to figure out ever since I found out Hyperlight drifter was made in gamemaker. I'm super happy with the result and I just had to share it with the community :)


  • 1

#8 jackhigh24

jackhigh24

    GMC Member

  • GMC Member
  • 1346 posts
  • Version:GM:Studio

Posted 18 September 2015 - 09:53 PM

ye sean its great man, iv not actually needing to use it in any game yet but im sure i will at some stage and id of never of thought of this, greatly appreciated, a green one up from me.


  • 0

#9 ElllElllElll

ElllElllElll

    GMC Member

  • GMC Member
  • 112 posts
  • Version:GM:Studio

Posted 28 September 2015 - 07:56 PM

This is amazing..  Great work man


  • 0

#10 ElllElllElll

ElllElllElll

    GMC Member

  • GMC Member
  • 112 posts
  • Version:GM:Studio

Posted 28 September 2015 - 09:10 PM

i dont know why, but I get a sorta... i cant describe it, but i can see the surface following my player object, cause it distorts the pixels by, about 1... any idea why this is happening?  I can't get rid of it.

 

also, tiles beyond the player object are still being drawn above... does this method only work for isometric?


Edited by ElllElllElll, 28 September 2015 - 11:11 PM.

  • 0

#11 seanMombo

seanMombo

    GMC Member

  • GMC Member
  • 1790 posts
  • Version:GM:Studio

Posted 01 October 2015 - 12:32 AM

Hmm, I would guess if you aren't drawing everything, and moving everything with integer values, you will experience this; and it will be further exacerbated if you scale your game.

 

Try drawing the player and surface at floor()'d coordinates

 

The tiles behind the player are drawn overtop of the player so that:

  • the main player object is covered by the walls,
  • the silhouette can be transparent and show the walls behind it.

 

This should work for any game type.


Edited by seanMombo, 01 October 2015 - 12:33 AM.

  • 0

#12 ElllElllElll

ElllElllElll

    GMC Member

  • GMC Member
  • 112 posts
  • Version:GM:Studio

Posted 02 October 2015 - 02:40 PM

Hmm, I would guess if you aren't drawing everything, and moving everything with integer values, you will experience this; and it will be further exacerbated if you scale your game.

 

Try drawing the player and surface at floor()'d coordinates

 

The tiles behind the player are drawn overtop of the player so that:

  • the main player object is covered by the walls,
  • the silhouette can be transparent and show the walls behind it.

 

This should work for any game type.

 

Thanks for the response!  

 

I had tried using ceiling, but I will try floor as well.  The offset comes and goes..  I believe its because my view follows a camera object.  The camera object moves_to_point and probably isn't always at a whole number x,y.  Ive tried to make the camera lock to whole numbers when following... but to no avail...

 

If you have any tips Id love to hear them.

 

Thanks again


Edited by ElllElllElll, 02 October 2015 - 02:40 PM.

  • 0

#13 Murr_

Murr_

    GMC Member

  • GMC Member
  • 54 posts
  • Version:GM:Studio

Posted 16 December 2015 - 01:02 AM

Wait! Would this work with objects too? Or it only works with tiles for compatibility? Just to be sure.

 

And also THANK YOU for this and great job! I've been looking for this example and tutorial for ever. Also ive been too wondering the fact how the game Hyper Light Drifter did the silhouette effect behind tiles and walls.


Edited by Murr_, 16 December 2015 - 01:08 AM.

  • 0

#14 ben_crossman

ben_crossman

    GMC Member

  • GMC Member
  • 45 posts
  • Version:GM:Studio

Posted 16 December 2015 - 01:48 PM

Murr_
Check out seanMombo's reply to nott's post above, he tells exactly how to apply it to objects.
  • 0
"Heyooo!" -Steve from Borderlands 2

#15 Murr_

Murr_

    GMC Member

  • GMC Member
  • 54 posts
  • Version:GM:Studio

Posted 24 December 2015 - 08:05 PM

When you draw the tile surface you are gathering all of the tiles in the room and drawing them to the surface, as seen below.

tile=tile_get_ids() //Get all the tiles in the room
 
surface_set_target(tileSurf)//draw to tileSurf
draw_clear_alpha(c_black,0)//clear tileSurf

for(i=0;i<array_length_1d(tile);i++)//loop through all the tiles and grab their data
{
    bk=tile_get_background(tile[i])
    xx=tile_get_x(tile[i])
    yy=tile_get_y(tile[i])
    
    left=tile_get_left(tile[i])
    top=tile_get_top(tile[i])
    
    width=tile_get_width(tile[i])
    height=tile_get_height(tile[i])
    
    //Only draw the tiles if they are below the player
    //This allows the player to be infront, and behind objects, and only draw the silhouette if player is behind them.
    if yy+height<objPlayer.y continue 
    
    //draw the tile to the surface
    draw_background_part(bk,left,top,width,height,xx-surfXPos,yy-surfYPos)
}
surface_reset_target()//reset draw target

All you have to do to add objects is use a with statement to draw the objects

surface_set_target(tileSurf)//draw to tileSurf
draw_clear_alpha(c_black,0)//clear tileSurf

with objWall
   draw_self()

surface_reset_target()//reset draw target

Whatever you draw to the tile surface will be used as a mask which the silhouette can be drawn on.

For some reason this obstructs my player.

Capture.PNG

PTest.gif

//* This makes that weird effect happen to my player. It draw the wall object around it *//

//Obj wall
surface_set_target(tileSurf)//draw to tileSurf
draw_clear_alpha(c_black, 0)//clear tileSurf

with (obj_wall)
    draw_self();
surface_reset_target()//reset draw target


  • 0

#16 seanMombo

seanMombo

    GMC Member

  • GMC Member
  • 1790 posts
  • Version:GM:Studio

Posted 24 December 2015 - 09:38 PM

You can't just draw the walls, you have to offset them by the surface coords


draw_sprite(sprite_index,image_index,x-surfXPos,y-surfYPos)

  • 0

#17 Murr_

Murr_

    GMC Member

  • GMC Member
  • 54 posts
  • Version:GM:Studio

Posted 24 December 2015 - 10:09 PM

This is a error i get when i add draw_sprite(sprite_index, image_index, x=surfXPos,y-surfYPos) to with obj_wall.

 

############################################################################################
ERROR in
action number 1
of Draw Event
for object obj_silhoutte_effect:
 
Trying to draw non-existing sprite.
 at gml_Object_obj_silhoutte_effect_DrawEvent_1 (line 51) -     draw_sprite(sprite_index, image_index, x - other.surfXPos, y - other.surfYPos)
############################################################################################
 
 
It has a sprite. But the console is saying otherwise. It does not.

  • 0

#18 seanMombo

seanMombo

    GMC Member

  • GMC Member
  • 1790 posts
  • Version:GM:Studio

Posted 24 December 2015 - 11:06 PM

with (obj_wall)
    draw_sprite(sprite_index,image_index,x-surfXPos,y-surfYPos)

That throws an error?

 

 

Is there any way it is confused as to what obj_wall is, or any way the sprite_index could be reset to noone?


  • 0

#19 Murr_

Murr_

    GMC Member

  • GMC Member
  • 54 posts
  • Version:GM:Studio

Posted 25 December 2015 - 11:44 PM

No not in anyway. The sprite_index of the wall isn't reseted to noone.


  • 0

#20 seanMombo

seanMombo

    GMC Member

  • GMC Member
  • 1790 posts
  • Version:GM:Studio

Posted 26 December 2015 - 02:41 AM

Post your code


  • 0

#21 Murr_

Murr_

    GMC Member

  • GMC Member
  • 54 posts
  • Version:GM:Studio

Posted 26 December 2015 - 06:26 PM

Draw Event 

///Draw the silhouette surface

//Update player variables as create event
playerSprite = player.sprite_index;
spriteWidth = sprite_get_width(playerSprite);
spriteHeight = sprite_get_height(playerSprite);
spriteXOff = sprite_get_xoffset(playerSprite);
spriteYOff = sprite_get_yoffset(playerSprite);
surfXPos = obj_player.x - spriteXOff;
surfYPos = obj_player.y - spriteYOff;

if!surface_exists(tileSurf) //Create surface
{
    tileSurf = surface_create(spriteWidth, spriteHeight); //Dimensions = player sprite dimensions
}   

tile = tile_get_ids() //Get all the tiles in the room
 
surface_set_target(tileSurf);//draw to tileSurf
draw_clear_alpha(c_black,0);//clear tileSurf

for(i = 0; i < array_length_1d(bkList); i++)//loop through all the tiles and grab their data
{

    bk = bkList[i];
    xx = xxList[i];
    yy = yyList[i];
    left = leftList[i];
    top = topList[i];
    
    width = widthList[i];
    height = heightList[i];
    //Only draw the tiles if they are below the player
    //This allows the player to be infront, and behind objects, and only draw the silhouette if player is behind them.
    if yy + height < obj_player._y continue; 
    
    //draw the tile to the surface
    draw_background_part(bk, left, top, width, height, xx-surfXPos, yy-surfYPos);
}
surface_reset_target();//reset draw target

if !surface_exists(silhouetteSurf)//create silhouetteSurf
    silhouetteSurf = surface_create(spriteWidth, spriteHeight); //Dimensions = player sprite dimensions

/* Obj wall */
surface_set_target(tileSurf)//draw to tileSurf
draw_clear_alpha(c_black, 0)//clear tileSurf
 
with (obj_wall)
    draw_sprite(sprite_index,image_index,x-surfXPos,y-surfYPos)
    draw_self();
surface_reset_target();
    
surface_set_target(silhouetteSurf);//draw to silhouetteSurf

    draw_clear_alpha(c_black, 0); //clear surface
    draw_surface(tileSurf, 0, 0); //draw the tile surface to silhouetteSurf
    draw_set_blend_mode_ext(bm_dest_alpha, bm_inv_dest_alpha); //Important blend mode to draw the silhouette
      
    d3d_set_fog(1, color, 0, 0); //This set's the color of your silhouette
    
    //draw the player to the surface so a silhouette can be made from it
    //IMPORTANT- you must still draw you player sprite normally at a lower depth than this object 
    //so the silhouette can be drawn above the player sprite
    //The objPlayer's depth is -10 and objSilhouette's depth is -100
    with (obj_player) 
        draw_sprite_ext(sprite_index, image_index, x-other.surfXPos, y-other.surfYPos, 1, 1, 0, -1, .5);
        
    d3d_set_fog(0, c_fuchsia, 0, 0); //Stop coloring the sprite
        
    draw_set_blend_mode(bm_normal); //reset blend mode
    
surface_reset_target();//reset draw target

draw_surface(tileSurf, surfXPos, surfYPos); // Draw the tile surface
draw_surface_ext(silhouetteSurf, surfXPos, surfYPos, 1, 1, 0, -1, 1); //the the silhouette surface


  • 0

#22 seanMombo

seanMombo

    GMC Member

  • GMC Member
  • 1790 posts
  • Version:GM:Studio

Posted 26 December 2015 - 09:48 PM

If I had to guess I would say its this

with (obj_wall)
    draw_sprite(sprite_index,image_index,x-surfXPos,y-surfYPos)
    draw_self(); <------RIGHT HERE
surface_reset_target();

draw_self is being run outside of the with(obj_wall) call. So your controller object is trying to draw its sprite, and I am assuming it had no sprite.


Edited by seanMombo, 26 December 2015 - 09:49 PM.

  • 0