Legacy GM Simplifying light casting, fixing bad code

A

Alek214

Guest
I've been trying (on and off for probably a few months now) to create an extremely simple "light casting" function in my game in order to tell the player what enemies can and can't see. The game is essentially a top-down zelda dungeon puzzle games with "stealth" elements using a C64-esque graphics style.

Right now it's almost working, but there are a few major issues that i cant really get past. The light casting system is supossed to be literally black and white, so a long of tutorials are giving me a lot of information that i dont really need. This tutorial helped a lot, but the main problem is that it's drawing the dark parts instead of the light parts, so i cant have more than one enemy on screen without either the last enemy that loads prioritizing it's "light" or it just showing a black screen altogether

The other major problem is that i have no idea what i'm doing code wise and i'm pretty sure having a for statment calling a script in the draw event is probably a terrible idea.

This is what my code looks like right now

Draw event of ob_ShowSight:

Code:
if room!=rm_TitleScreen
    {
    var n=0;
    for(n=0; n != array_length_1d(global.eye) ; n+=1)
        {
        var status=global.eye[n].is_alive;
        scr_ShowSight(global.eye[n])
        }
    }
The global.eye array is what i use to link specific eyes-type-enemy instances (which are the focal point of the game) to specific trap instances. it is also used to save and load the life/death status of an eye instance after exiting the game. there are multiple different types of "eye" enemies, so this also makes it so i dont have to type scr_ShowSight(obj_SmallEye) and scr_ShowSight(obj_PanicEye) and so on.

scr_ShowSight code:

Code:
with(argument0)
{
if scr_InRoomWith(obj_Player) and is_alive and keyboard_check(vk_numpad0)
    {
    draw_set_colour(c_white)
    draw_rectangle(r_x1,r_y1,r_x2,r_y2,false) //draw white recangle the size of current room
    with(obj_BlockSight)
        {
        depth=-100000
        var shadowsize=1000
        draw_set_colour(c_black)
        draw_rectangle(bbox_left,bbox_top,bbox_right,bbox_bottom,false) //draw black squares over the walls
        draw_primitive_begin(pr_trianglestrip)
       
            draw_vertex(bbox_left, bbox_top);
            var dir = point_direction(argument0.x,argument0.y,bbox_left,bbox_top)
            draw_vertex(x+lengthdir_x(shadowsize,dir), y+lengthdir_y(shadowsize,dir));
           
            draw_vertex(bbox_left, bbox_bottom);
            var dir = point_direction(argument0.x,argument0.y,bbox_left,bbox_bottom)
            draw_vertex(x+lengthdir_x(shadowsize,dir), y+lengthdir_y(shadowsize,dir));
           
            draw_vertex(bbox_right, bbox_top);
            var dir = point_direction(argument0.x,argument0.y,bbox_right,bbox_top)
            draw_vertex(x+lengthdir_x(shadowsize,dir), y+lengthdir_y(shadowsize,dir));
           
            draw_vertex(bbox_right, bbox_bottom);
            var dir = point_direction(argument0.x,argument0.y,bbox_right,bbox_bottom)
            draw_vertex(x+lengthdir_x(shadowsize,dir), y+lengthdir_y(shadowsize,dir));
       
        draw_primitive_end()
        }
    draw_set_colour(c_gray)
    with (obj_Player) draw_rectangle(bbox_left,bbox_top,bbox_right,bbox_bottom,false) //draw a grey rectangle over the player
    }
}
this code draws a black and white representation of each eye's FOV, in the full game the player will have to find and use an item to get this effect. right now i can just press 0 on the number pad. one obvious thing that im noticing now is that drawing the white rectangle will overshadow previously drawn FoV's, but changing this just bring back the initial problem of the screen just going completely black.

Here are a few gifs of the game and examples of this problem.





Another problem is that light will seep between tiles, if anyone knows a quick fix for that then that would be great, but it's not the main problem.

I've been trying to figure out how to change the light system to draw the "light" instead of drawing "dark" but it's a lot harder than i thought, plus i only barely understand what's going on in my script right now.

does anyone know 1: a better way to run this code that's not overexerting the draw event and 2: a way to easily change the scr_ShowSight code to draw triangles where the eye can see, rather than where they cant?

Thank you!
 
Q

Quackertree

Guest
That code from that tutorial seems really weird an unpractical. It works for some really basic implementations, but when you get to more complicated stuff, it'll start to mess up and not work properly. Heck, there's even bugs visible in the actual demo itself, which, although can be fixed "easily", it's not the best practice.

My gut is calling: "Use a shader! Use a shader!", but I'll refrain from that for now.

In most games, the way these things work is made a lot easier since they have a cone of vision. They don't literally see everything, but they look in some sort of triangle, and they then rotate the camera's around a bit. The easiest way to implement this, is to simply set a blend mode (bm_add, perhaps) and draw a triangle on top of the eye into the direction it's looking.

Would be something like this (without blend mode):

Code:
draw_triangle(x, y, x + dcos(direction - fov/2)*range, y + dsin(direction - fov/2)*range, x + dcos(direction + fov/2)*range, y + dsin(direction + fov/2)*range);
Where 'range' represents the view distance, 'direction' represents the direction the eye is looking in (with 0 being to the right, 90 being upwards, etc.) and 'fov' being the degrees of vision (mostly somewhere between 45 and 60).

The check for whether an the enemy actually sees you is then done by comparing the view angle to the angle between the player and the enemy (using angle_difference(...)) and if it's smaller than the enemy's field of view (fov) it'll detect the player.
 
A

Alek214

Guest
That code from that tutorial seems really weird an unpractical. It works for some really basic implementations, but when you get to more complicated stuff, it'll start to mess up and not work properly. Heck, there's even bugs visible in the actual demo itself, which, although can be fixed "easily", it's not the best practice.

My gut is calling: "Use a shader! Use a shader!", but I'll refrain from that for now.

In most games, the way these things work is made a lot easier since they have a cone of vision. They don't literally see everything, but they look in some sort of triangle, and they then rotate the camera's around a bit. The easiest way to implement this, is to simply set a blend mode (bm_add, perhaps) and draw a triangle on top of the eye into the direction it's looking.

Would be something like this (without blend mode):

Code:
draw_triangle(x, y, x + dcos(direction - fov/2)*range, y + dsin(direction - fov/2)*range, x + dcos(direction + fov/2)*range, y + dsin(direction + fov/2)*range);
Where 'range' represents the view distance, 'direction' represents the direction the eye is looking in (with 0 being to the right, 90 being upwards, etc.) and 'fov' being the degrees of vision (mostly somewhere between 45 and 60).

The check for whether an the enemy actually sees you is then done by comparing the view angle to the angle between the player and the enemy (using angle_difference(...)) and if it's smaller than the enemy's field of view (fov) it'll detect the player.
I'm open to using shaders, i just need to learn how to use them first.

I do want the eyes to be able to see everything around them though, the way the game works revolves around the eyes being stationary objects that can see everything around them as long as it's not behind a wall. plus, i already have a system in place for how the eyes see you (just a collision line from the eye to the player that checks for walls)

i just want a way to visualize the FoV of the eye so the player can avoid being spotted. It's important to the game's mechanics that it shows everything the eye can/cant see rather than a cone of vision

(also, i plomped your code into my script just to see what would happen, it had some pretty interesting effects after i changed a few things and made it show the outline rather than the filled triangle)

 
A

Alek214

Guest
This does what you want. Pretty sure you can set it up so the shadow is above to hide what's behind the walls
https://marketplace.yoyogames.com/assets/721/aura-surface-based-lighting
I kind of wanted to avoid buying an engine for something as simple as what i'm doing. plus i'd rather create something from scratch than strip away all the unnecessary shading and colored lighting from an existing engine. I dont have anything against buying an engine but it seems unnecessary and i'd rather figure out how to do it myself, im just having trouble doing that. maybe later i'll give in and buy it, but i dont feel like im at that point yet.
 
A

Alek214

Guest
alright, i caved and bought the engine, but is there a tutorial or readme somewhere? google hasn't been helpful since "aura lighting engine" is pretty vague. the notes in the code are good but im always missing something and it feels like it's a lot harder than it's supposed to be.
 
S

Shariku Onikage

Guest
Would something like this be what you're looking for (the blocks here are masked so they can be hidden if you don't want them)?

Pressing Control is what turns it on and off.
test.gif
If so, my code is below (i use triangles instead of vertexes on account of being an idiot):

Object: oWall (presumes sprite size is 32x32, change all references to 16 if not)
Visible
Solid
Depth:0
Create:
Code:
point_lt=0;
point_rt=0;
point_ld=0;
point_rd=0;

instance_create(x,y,oWallMask);  //remove this if you want the blocks to go invisible as well
Step:
Code:
if keyboard_check_pressed(vk_control){
    instance_create(0,0,oLight);
    point_lt = point_direction(oPlayer.x,oPlayer.y,x-16,y-16);
    point_rt = point_direction(oPlayer.x,oPlayer.y,x+16,y-16);
    point_ld = point_direction(oPlayer.x,oPlayer.y,x-16,y+16);
    point_rd = point_direction(oPlayer.x,oPlayer.y,x+16,y+16);
}
Draw:
Code:
if(keyboard_check(vk_control)){
draw_set_colour(c_black);
draw_triangle(x-16,y-16,x-16+lengthdir_x(1000,point_lt),y-16+lengthdir_y(1000,point_lt),x+16+lengthdir_x(1000,point_rt),y-16+lengthdir_y(1000,point_rt),0)
draw_triangle(x-16,y-16,x+16+lengthdir_x(1000,point_rt),y-16+lengthdir_y(1000,point_rt),x+16,y-16,0)
draw_triangle(x-16,y+16,x-16+lengthdir_x(1000,point_ld),y+16+lengthdir_y(1000,point_ld),x+16+lengthdir_x(1000,point_rd),y+16+lengthdir_y(1000,point_rd),0)
draw_triangle(x-16,y+16,x+16,y+16,x+16+lengthdir_x(1000,point_rd),y+16+lengthdir_y(1000,point_rd),0)
draw_triangle(x-16,y+16,x-16,y-16,x-16+lengthdir_x(1000,point_lt),y-16+lengthdir_y(1000,point_lt),0)
draw_triangle(x-16,y+16,x-16+lengthdir_x(1000,point_ld),y+16+lengthdir_y(1000,point_ld),x-16+lengthdir_x(1000,point_lt),y-16+lengthdir_y(1000,point_lt),0)
draw_triangle(x+16,y+16,x+16,y-16,x+16+lengthdir_x(1000,point_rd),y+16+lengthdir_y(1000,point_rd),0)
draw_triangle(x+16,y-16,x+16+lengthdir_x(1000,point_rt),y-16+lengthdir_y(1000,point_rt),x+16+lengthdir_x(1000,point_rd),y+16+lengthdir_y(1000,point_rd),0)

draw_rectangle(x-16,y-16,x+16,y+16,0)
}
Object: oLight -Floods the room with darkness when created - don't create this yourself. Let oWall do it for you.
Visible
Depth:1
Step:
Code:
if keyboard_check_released(vk_control){
    instance_destroy();
    }
Draw:
Code:
draw_set_colour(c_white);
draw_rectangle(0,0,room_width,room_height,0);
Object: oWallMask - only needed if you still want to see the blocks
Uses the same Sprite as oWall
Depth: -1 - keeps it above everything
 
A

Alek214

Guest
Would something like this be what you're looking for (the blocks here are masked so they can be hidden if you don't want them)?

Pressing Control is what turns it on and off.
View attachment 928
If so, my code is below (i use triangles instead of vertexes on account of being an idiot):

Object: oWall (presumes sprite size is 32x32, change all references to 16 if not)
Visible
Solid
Depth:0
Create:
Code:
point_lt=0;
point_rt=0;
point_ld=0;
point_rd=0;

instance_create(x,y,oWallMask);  //remove this if you want the blocks to go invisible as well
Step:
Code:
if keyboard_check_pressed(vk_control){
    instance_create(0,0,oLight);
    point_lt = point_direction(oPlayer.x,oPlayer.y,x-16,y-16);
    point_rt = point_direction(oPlayer.x,oPlayer.y,x+16,y-16);
    point_ld = point_direction(oPlayer.x,oPlayer.y,x-16,y+16);
    point_rd = point_direction(oPlayer.x,oPlayer.y,x+16,y+16);
}
Draw:
Code:
if(keyboard_check(vk_control)){
draw_set_colour(c_black);
draw_triangle(x-16,y-16,x-16+lengthdir_x(1000,point_lt),y-16+lengthdir_y(1000,point_lt),x+16+lengthdir_x(1000,point_rt),y-16+lengthdir_y(1000,point_rt),0)
draw_triangle(x-16,y-16,x+16+lengthdir_x(1000,point_rt),y-16+lengthdir_y(1000,point_rt),x+16,y-16,0)
draw_triangle(x-16,y+16,x-16+lengthdir_x(1000,point_ld),y+16+lengthdir_y(1000,point_ld),x+16+lengthdir_x(1000,point_rd),y+16+lengthdir_y(1000,point_rd),0)
draw_triangle(x-16,y+16,x+16,y+16,x+16+lengthdir_x(1000,point_rd),y+16+lengthdir_y(1000,point_rd),0)
draw_triangle(x-16,y+16,x-16,y-16,x-16+lengthdir_x(1000,point_lt),y-16+lengthdir_y(1000,point_lt),0)
draw_triangle(x-16,y+16,x-16+lengthdir_x(1000,point_ld),y+16+lengthdir_y(1000,point_ld),x-16+lengthdir_x(1000,point_lt),y-16+lengthdir_y(1000,point_lt),0)
draw_triangle(x+16,y+16,x+16,y-16,x+16+lengthdir_x(1000,point_rd),y+16+lengthdir_y(1000,point_rd),0)
draw_triangle(x+16,y-16,x+16+lengthdir_x(1000,point_rt),y-16+lengthdir_y(1000,point_rt),x+16+lengthdir_x(1000,point_rd),y+16+lengthdir_y(1000,point_rd),0)

draw_rectangle(x-16,y-16,x+16,y+16,0)
}
Object: oLight -Floods the room with darkness when created - don't create this yourself. Let oWall do it for you.
Visible
Depth:1
Step:
Code:
if keyboard_check_released(vk_control){
    instance_destroy();
    }
Draw:
Code:
draw_set_colour(c_white);
draw_rectangle(0,0,room_width,room_height,0);
Object: oWallMask - only needed if you still want to see the blocks
Uses the same Sprite as oWall
Depth: -1 - keeps it above everything
This is almost exactly what i need, but inverted. i need it to draw the triangles where the light is, not where it isn't. I have to visualize multiple light sources and having it draw the darkness just makes it only show the first light source that spawns. this is way better than what i was doing though, so thank you!

also sorry i didn't respond to this for like 3 months, i tried using Aura then gave up for a long time.
 
S

Shariku Onikage

Guest
No worries. Hope it helps.

It's been a while since i played with this bit of code but you should be able to tweak it appropriately depending on where you put it (i.e. have it draw from the light sources object you want rather than oWall and then change the colour of the triangles).
 
A

Alek214

Guest
Ideally, i want my system to work something like this http://ncase.me/sight-and-light/
but I'm not sure how i would even do this. I did make a system that creates a point at each corner so the system doesn't have to look at the four corners of each and every block in the game, but i dont have a good enough understanding of GML to actually put that to good use. I'm trying to figure it out and i'll get there eventually, but if you guys have any ideas it would be greatly appreciated.
deburg.gif
 
A

Alek214

Guest
There's also this one, which again, i dont understand GML well enough to know how to emulate it. http://www.redblobgames.com/articles/visibility/
if i could understand raycasting more i think i could figure it out but i really dont know how to draw a line and find out the specific point at which it collides with an object.
 

Hyomoto

Member
I've screwed around with this in the past but it's a case of nothing simple. Essentially, and don't let my description frighten you, you are looking for some sort of edge detection algorithm. I don't think I have any code I can dig out on this one, not on this computer at least, and definitely nothing you can just plop in. Instead I can explain to you what you are accomplishing.

First off, your game probably already knows what spaces are occupied and which aren't. So your next goal is really just to figure out what tiles are touching each other. So let's look at a simple horizontal line of blocks, and for this example we'll assume that we're using a two-dimensional array, walls, to hold the scenery geometry. I use a _ to denote a local variable, or one that will be freed at the end of this execution, it's a style thing, and helps with readability.
Code:
var _start = true, _next = 0;
horizontalWall = 0;

for ( var _h = 0; _h < array_length_2d( walls, 0 ); _h++ ) {
    if walls[ 0, _h ] > 0 && !_start { _start = true; _next++; continue }
    if _start {
        _horizontalWall[ _next, 0 ] = tileHeight;
        _horizontalWall[ _next, 1 ] = _h * tileWidth;
        _horizontalWall[ _next, 2 ] = ( _h + 1 ) * tileWidth;
        _start = false;
  
    } else {
        _horizontalWall[ _next, 2 ] = ( _h + 1 ) * tileWidth;
  
    }

}
So let's start at the beginning. What I'm going to do is make a list of line segments that will represent the bottom of the top row of walls. The first thing I did was create two local variables: the first is _start which let's me know whether or not to begin a new line segment. I set this to true because when I start there are no line segments yet and I need to make one. The second is _next which I'm using to keep track of how many line segments I've made (and hence, which one I'm on).

I start with a for loop. I'm using array_length here because I don't know how large the room is, but you would obviously use your room size. This is how I'll scan along the very top row of blocks, each loop I'll check each block if it's solid or not and move on. So, the first thing to check is whether or not the block being examined is not a wall, and whether or not I've started a new line segment. The reason for this is if I've hit air, I know the line segment has ended. And if I haven't yet started a line segment there's no need to 'move on' to the next one. So, if those conditions are true, I set _start to true, move to the next line segment and use continue to skip further processing that cycle, otherwise I keep going.

The next check is whether or not a line segment needs to be started. If it does, and you'll notice this is why we set _start to true to begin with, I set the first index in my array to be the y value of this wall, the second index to the leftmost x of this wall, and the third is the rightmost x. If my wall were, say, 16x16 and the first tile found is a wall, the array would hold { 16, 0, 16 }. This line segment is usable by itself, but we want to optimize it into a single line segment along the entire row of walls. So if I didn't need to start a new line segment, specifically because I've encountered a wall next to another wall, I simply extend the line segment by changing the third index of the current line segment to be the rightmost x position of this wall. Let's say the first two tiles are walls, then the line segment would now be { 16, 0, 32 }. If I wanted to visualize that I could draw my line from x1,y to x2,y. Hopefully you see how as this process continues I'm building an array of horizontal line segments.

Please keep in mind this is a simplified example to try and demonstrate a way of accomplishing your goal, it's obviously not drag and drop code. What you have when you are done though is a bunch of horizontal line segments that you can use to calculate things blocking your sight. Hopefully this will help you on your way!

EDIT: I just checked out the source example you gave, so I apologize for using line segments but this method isn't so different from how you can gather polygons from your level for use with the example given. His method is pretty neat. I think I'd probably see if I could cull any vertices that are blocked from sight, but otherwise a pretty sound idea.
 
Last edited:
S

Salvakiya

Guest
well if you draw to a surface before you draw the shadows you can get light data from that surface. so say you draw shadows onto a white surface right? you can pull what areas of that surface are in light and which are not. there are 2 ways that come to mind. one you can use draw_get_pixel or the surface equivalent but this is inefficient. I would rather build a grid which will process once every time the light moves and mark cells as which are visible and which are not. you could make them half the grid size of your game and you would not know the difference when playing.


this would be the easiest way to do things in my opinion. especially if you already have the ability to draw a black and white surface which casts shadows.

Edit: I take that back.... I would not do that... I would use some sort of collision line setup to find if the player is in view. if you want to visualise the cone of sight you simply have to remove the black from the surface then draw it with the alpha value and blend color of your choosing
 
Last edited:
A

anomalous

Guest
Ideally, i want my system to work something like this http://ncase.me/sight-and-light/
but I'm not sure how i would even do this. I did make a system that creates a point at each corner so the system doesn't have to look at the four corners of each and every block in the game, but i dont have a good enough understanding of GML to actually put that to good use. I'm trying to figure it out and i'll get there eventually, but if you guys have any ideas it would be greatly appreciated.
View attachment 3526
Do you really want LOS or light/shadows? They are two different things. Typically shadows have a length, are soft, they are not infinite. Looks like you want a line of sight blackout.

I prefer tile based LOS personally for the look of your game. All rogue-likes use that, the early ultimas, etc. Example in game maker tutorial.
You can implement that in about an hour for testing purposes to see if you like it. If you like it, it can then be optimized.

If found raycasting to look bad in nearly every implementation, it really takes a certain game type for it to look OK, and then only if its part of the game mechanics in a big way (monaco)
I did it 3 times, each time better, faster, and in every case it was very time consuming, and was never blazing fast. It's also very complex compared to nearly everything else for your game.
Then to add soft edges, or heaven forbid a non-rectangle...

If you really want raycasting, how are you doing collisions/walls, are they objects or a grid?
If you already have the corners, that's good.

What you can do is this:
- add each of those points to a ds_grid, points in Y, x/y/angle in X.
where angle is point_direction from the PLAYER xy to the point.
when done, sort ascending, this gets them in counter clockwise order.

Loop through them from smallest to largest angle.
- prune points that are out of range or fail collision line from player to point and push to another data structure (list?) There are other ways to prune, this all depends on your design.

Now you have a list of points to draw from.
Draw a triangle primitive something like player x,y, to the first point, first point to second point, second point back to player xy. Forming a triangle. repeat until your last point is the first point.

Draw that as the subtraction area from a dark surface with the appropriate blend mode (and the normal surface light overlay stuff)

When you draw to specific vertices, there should be no way for light to go through gaps. Although you may want to not allow corners touching for walls, its probably bad for a lot of reasons having to do with collision.
 
Last edited by a moderator:
A

Alek214

Guest
Do you really want LOS or light/shadows? They are two different things. Typically shadows have a length, are soft, they are not infinite. Looks like you want a line of sight blackout.

I prefer tile based LOS personally for the look of your game. All rogue-likes use that, the early ultimas, etc. Example in game maker tutorial.
You can implement that in about an hour for testing purposes to see if you like it. If you like it, it can then be optimized.

If found raycasting to look bad in nearly every implementation, it really takes a certain game type for it to look OK, and then only if its part of the game mechanics in a big way (monaco)
I did it 3 times, each time better, faster, and in every case it was very time consuming, and was never blazing fast. It's also very complex compared to nearly everything else for your game.
Then to add soft edges, or heaven forbid a non-rectangle...

If you really want raycasting, how are you doing collisions/walls, are they objects or a grid?
If you already have the corners, that's good.

What you can do is this:
- add each of those points to a ds_grid, points in Y, x/y/angle in X.
where angle is point_direction from the PLAYER xy to the point.
when done, sort ascending, this gets them in counter clockwise order.

Loop through them from smallest to largest angle.
- prune points that are out of range or fail collision line from player to point and push to another data structure (list?) There are other ways to prune, this all depends on your design.

Now you have a list of points to draw from.
Draw a triangle primitive something like player x,y, to the first point, first point to second point, second point back to player xy. Forming a triangle. repeat until your last point is the first point.

Draw that as the subtraction area from a dark surface with the appropriate blend mode (and the normal surface light overlay stuff)

When you draw to specific vertices, there should be no way for light to go through gaps. Although you may want to not allow corners touching for walls, its probably bad for a lot of reasons having to do with collision.
The functionality of the lighting system is dependent on the ability to see very specific areas of lighting, this could theoretically work but i think the conveyance would be better if it was a light/shadows system.
the puzzles in the game are activated and deactivated by whether or not an eye can see you, and an eye "sees" you by creating a collision line between itself and you and checking if it's colliding with obj_blocksight
 

Attachments

A

Alek214

Guest
I've screwed around with this in the past but it's a case of nothing simple. Essentially, and don't let my description frighten you, you are looking for some sort of edge detection algorithm. I don't think I have any code I can dig out on this one, not on this computer at least, and definitely nothing you can just plop in. Instead I can explain to you what you are accomplishing.

First off, your game probably already knows what spaces are occupied and which aren't. So your next goal is really just to figure out what tiles are touching each other. So let's look at a simple horizontal line of blocks, and for this example we'll assume that we're using a two-dimensional array, walls, to hold the scenery geometry. I use a _ to denote a local variable, or one that will be freed at the end of this execution, it's a style thing, and helps with readability.
Code:
var _start = true, _next = 0;
horizontalWall = 0;

for ( var _h = 0; _h < array_length_2d( walls, 0 ); _h++ ) {
    if walls[ 0, _h ] > 0 && !_start { _start = true; _next++; continue }
    if _start {
        _horizontalWall[ _next, 0 ] = tileHeight;
        _horizontalWall[ _next, 1 ] = _h * tileWidth;
        _horizontalWall[ _next, 2 ] = ( _h + 1 ) * tileWidth;
        _start = false;
 
    } else {
        _horizontalWall[ _next, 2 ] = ( _h + 1 ) * tileWidth;
 
    }

}
So let's start at the beginning. What I'm going to do is make a list of line segments that will represent the bottom of the top row of walls. The first thing I did was create two local variables: the first is _start which let's me know whether or not to begin a new line segment. I set this to true because when I start there are no line segments yet and I need to make one. The second is _next which I'm using to keep track of how many line segments I've made (and hence, which one I'm on).

I start with a for loop. I'm using array_length here because I don't know how large the room is, but you would obviously use your room size. This is how I'll scan along the very top row of blocks, each loop I'll check each block if it's solid or not and move on. So, the first thing to check is whether or not the block being examined is not a wall, and whether or not I've started a new line segment. The reason for this is if I've hit air, I know the line segment has ended. And if I haven't yet started a line segment there's no need to 'move on' to the next one. So, if those conditions are true, I set _start to true, move to the next line segment and use continue to skip further processing that cycle, otherwise I keep going.

The next check is whether or not a line segment needs to be started. If it does, and you'll notice this is why we set _start to true to begin with, I set the first index in my array to be the y value of this wall, the second index to the leftmost x of this wall, and the third is the rightmost x. If my wall were, say, 16x16 and the first tile found is a wall, the array would hold { 16, 0, 16 }. This line segment is usable by itself, but we want to optimize it into a single line segment along the entire row of walls. So if I didn't need to start a new line segment, specifically because I've encountered a wall next to another wall, I simply extend the line segment by changing the third index of the current line segment to be the rightmost x position of this wall. Let's say the first two tiles are walls, then the line segment would now be { 16, 0, 32 }. If I wanted to visualize that I could draw my line from x1,y to x2,y. Hopefully you see how as this process continues I'm building an array of horizontal line segments.

Please keep in mind this is a simplified example to try and demonstrate a way of accomplishing your goal, it's obviously not drag and drop code. What you have when you are done though is a bunch of horizontal line segments that you can use to calculate things blocking your sight. Hopefully this will help you on your way!

EDIT: I just checked out the source example you gave, so I apologize for using line segments but this method isn't so different from how you can gather polygons from your level for use with the example given. His method is pretty neat. I think I'd probably see if I could cull any vertices that are blocked from sight, but otherwise a pretty sound idea.
I'm gonna need like. a while to process all this information but i will get back to you on this once i do.
 
A

Alek214

Guest
Also, note that the gifs in the initial post are what the full game is going to look like, just not broken. i want it to be just hard white-on-black. this is about functionality, not aesthetic.
 

Hyomoto

Member
Here's an old video of my method, I wish I had the code lying around for you to look at, but maybe this will help visualize it? I'm using this exact method where I decide whether or not a tile is occupied to build line segments. First it shows just shows the final effect, but thirty seconds or so in I turn on the linedef display and the wireframe for the lights. Maybe it will help in processing seeing what the end result is meant to look like.
 
A

anomalous

Guest
Sounds good Alek214, having done that stuff for a month it will feel good to help in any way I can, it will make me feel like it was worth it :p (I scrapped it all in favor of simple lighting and no LOS).

I'm still curious how you're doing collisions, it will be a big determinant in how best to approach it.
I feel like your implementation given your "tile size" relative to the screen, will result in acceptable performance, at least on windows.
 
A

Alek214

Guest
Sounds good Alek214, having done that stuff for a month it will feel good to help in any way I can, it will make me feel like it was worth it :p (I scrapped it all in favor of simple lighting and no LOS).

I'm still curious how you're doing collisions, it will be a big determinant in how best to approach it.
I feel like your implementation given your "tile size" relative to the screen, will result in acceptable performance, at least on windows.
upload_2016-10-21_18-23-11.png
Collision is determined by 16 by 16 tiles, i've always have bad luck with just taking a square and stretching it out. Anything that blocks sight spawns a separate object called "obj_blocksight" so that i can create solid objects that are "see through" and objects you can walk through that aren't.

i have an script made to determine what "room" an object is in, the entire floor is one room and it all exists simultaneously, but some stuff "turns off" when the player isn't in the room with it. ideally i want there to be a rectangle of "obj_blocksight"s around the room the player is in to prevent eyes outside of your room from seeing you, and i want the lighting script to ignore everything that isn't in the room with the player.

Code:
///scr_SetRoomCoord
r_x1 = (304*floor(x/304))
r_y1 = (144*floor(y/144))
r_x2 = (304*floor(x/304))+304
r_y2 = (144*floor(y/144))+144
I just plop that into the create event of every object, then use this
Code:
///scr_InRoomWith
if !instance_exists(argument0)
or r_x1 > argument0.x
or r_y1 > argument0.y
or r_x2 < argument0.x
or r_y2 < argument0.y
    return false
else
    return true
it's pretty sloppy, but it works! usually! but yeah, because of this it should make it a little less intensive since it will only be acting on the stuff within that space.
 
Top