[SOLVED] Custom Line-Of-Sight Issue

This forum is currently in read-only mode.
From the Asset Store
Supports keyboard, mouse and gamepads. Supports several gamepads at once.
  • I'm trying to make line-of-sight work with multiple enemies. This is proving to be a tremendous pain in the ass. Where to begin?

    This is my goal: I need the player character to see enemies within a cone of vision at indefinite range, blocked by solid obstacles. I need the enemies to see the player character likewise.

    Okay, so, originally, I set LOS to the player sprite, and quickly realized it could only handle one target at a time. I then scrapped the LOS behavior and programmed a sprite, 'Beam', to set its position and angle to the player sprite and increase its length constantly, until it collided with a solid or enemy, at which point it would set its length to the distance between the player and the object.

    In this manner, the Beam object acted as an impromptu LOS that could target multiple enemies. However, it was not a cone, so I could only see enemies immediately in front of my character. Even if I made the Beam object wider, it would still limit its size to the first object it collided with, essentially decreasing its accuracy (increasing the smallest gap it could see through). I tried to procedurally generate a conic spread of these objects, which worked...but killed the framerate.

    I scrapped the Beam object and tried something different. I removed the player's LOS behavior and gave the enemies LOS behavior instead. I created a 'Compass' object, a long, thin sprite that was created when an enemy established LOS with the player. Compass set its position to the player but set its angle toward the enemy. The idea was to make the enemy visible to the player when the player sprite's angle matched the Compass' angle, give or take X degrees (X being the player's cone of vision, in degrees). This caused some thoroughly crazy issues, as the Compass objects refused to sync up with their proper enemies, even when identical private variables were assigned to help them identify each other. It was a mess, and ended up butchering the framerate as badly as the previous attempt. Back to the drawing board.

    I scrapped the Compass system and went back to the LOS behavior. I created a C-shaped 'Cloak' sprite and made it solid. I set its position and angle to the player sprite, with the 'open' end of the C facing forward. Now the LOS for all enemies was blocked by the Cloak except when the player was facing the enemy, so that it could be seen through the open end. This effectively established the player's cone of vision. Now I just had to find a way to allow the enemies to target the player, regardless of which way the player was facing. I created a second object for each enemy, a 'Radar' sprite, which I also gave LOS behavior. I changed the LOS setting from 'solid' to 'custom' and told it to acknowledge only walls (but not the Cloak) as obstacles. This worked...but, again, killed the framerate after running for a few minutes. I'm not sure why custom LOS settings would do this, but they did.

    But I was not about to give up so easily. I decided to create a ring-shaped sprite, 'Bubble', which would likewise follow the player sprite, but sit outside of the Cloak. This should allow the Radar objects to detect it without custom settings, right? Right? WRONG. The presence of the Cloak messes with their ability to detect the Bubble, even though it's sitting outside of the Cloak. I thought it might be an issue of LOS accuracy, so I increased the accuracy to 1 pixel. This only helped marginally; whereas before, the enemies would ignore the Bubble until the player sprite turned to face them, they now track the Bubble in very abrupt, choppy movements, unless the player sprite is facing them (in which case, they track smoothly).

    In desperation, I scrapped the Cloak and Bubble objects and made a gigantic triangular sprite, 'Vision', large enough to cover the entire screen (the game does not scroll, so it only had to be large enough to cover 640x480). I set Vision's position and angle to the player sprite, allowing enemies to target the player with their regular LOS behavior and allowing the player to see them when they had LOS and Vision was overlapping them. This likewise worked...and, of course, kicked the sh*t out of the framerate.

    These are the three final products of this God-forsaken endeavor. I can only hope there is something fundamental I'm overlooking that will solve this problem. I tried putting the Cloak on a different layer than the Radar sprites, but this didn't seem to work. I've never worked with multiple layers extensively, so there may be something I'm missing there. That's really the only direction I can see this going successfully, but I don't know enough to say.

    Your thoughts?

    Custom

    mediafire.com

    Bubble

    mediafire.com

    Vision

    mediafire.com

  • Try using "for each" above the LOS condition. That will allow it to work with multiple objects.

    + System: For each badguy

    + hero: Has LOS to badguy

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • Or, you could always do your own "cone of sight" routine.

    Have a look at the image.

    o = object with cone of sight

    a = angle, to which o faces

    c = the cone of sight

    r = the radius of the cone

    p = sample points within the cone

    d = distance between points on r

    g = distance between points on cone

    All there is to do is creating a virtual cone, and sample only a small subsets of the possible points within the cone (for speed reasons).

    c exists angular, centered around a. The radius is needed and should probably be the border of the visible screen of your game.

    What you need:

    • The height (r) of the cone in pixel => cH
    • The width of the cone in degrees => cA
    • The sample density along r => pH
    • The sample density along the circumference of the cone => pV

    The position of a sample on the virtual grid is

    x = cos( (a - cA / 2) + loop_through_pV * (cA / pV) ) * loop_through_pH * (cH / pH)

    y = sin( (a - cA / 2) + loop_through_pV * (cA / pV) ) * loop_through_pH * (cH / pH)

    Then do whatever suits your needs at that sample point. In my example I did a collision-at-offset check.

    Download coneofview.cap

  • Try using "for each" above the LOS condition. That will allow it to work with multiple objects.

    + System: For each badguy

    + hero: Has LOS to badguy

    Thanks! All this time, I've been giving objects private variables to replicate this. Functionally, is this any different from using private variables? For example, is

    IF

    Bullet on collision with Gunman

    THEN

    set Gunman.Value('Health') to 0

    IF

    Gunman.Value('Health') equal to 0

    THEN

    destroy Gunman

    different from

    IF

    For each Gunman

    Bullet on collision with Gunman

    THEN

    destroy Gunman

    ?

  • Or, you could always do your own "cone of sight" routine.

    Have a look at the image.

    o = object with cone of sight

    a = angle, to which o faces

    c = the cone of sight

    r = the radius of the cone

    p = sample points within the cone

    d = distance between points on r

    g = distance between points on cone

    All there is to do is creating a virtual cone, and sample only a small subsets of the possible points within the cone (for speed reasons).

    c exists angular, centered around a. The radius is needed and should probably be the border of the visible screen of your game.

    What you need:

    - The height (r) of the cone in pixel => cH

    - The width of the cone in degrees => cA

    - The sample density along r => pH

    - The sample density along the circumference of the cone => pV

    The position of a sample on the virtual grid is

    x = cos( (a - cA / 2) + loop_through_pV * (cA / pV) ) * loop_through_pH * (cH / pH)

    y = sin( (a - cA / 2) + loop_through_pV * (cA / pV) ) * loop_through_pH * (cH / pH)

    Then do whatever suits your needs at that sample point. In my example I did a collision-at-offset check.

    Download coneofview.cap

    Wow, this works really well, actually. I've been trying to replicate it from scratch, however, and can't. I'm just not math-savvy enough to do it intuitively. As such, I don't feel right including it in my programming, since this is meant for me to be able to alter at will. I'll definitely hold on to the example and do my best to familiarize myself with it, though. I appreciate you taking the time to put it together.

  • tulamide, I don't understand why you did what you did when you set the 'angle' variable to:

    ".Angle < 'cone' / 2 ? .Angle + 360 : .Angle"

    Isn't ".Angle + 360" and ".Angle" the same angle anyway?

  • tulamide, I don't understand why you did what you did when you set the 'angle' variable to:

    ".Angle < 'cone' / 2 ? .Angle + 360 : .Angle"

    Isn't ".Angle + 360" and ".Angle" the same angle anyway?

    Technically they are the same, but for Construct it would be a problem, if an angle becomes negative. Construct is designed for positive angles only (that's why there are extra expressions for testing anticlockwise and rotating anticlockwise)

    This conditional expression is used to prevent that 'angleLeft' ever gets negative.

    See this example:

    The sprite's current angle is 5?. Without that expression 'angleLeft' would become 'angle' - ('cone' / 2) = 5 - 30 = -25?

    Now with that expression, any angle below the threshold is added 360, which makes 5? become 365?, and 'angleLeft' is now calculated as 365 - 30 = 335?, which is the positive expression of -25?.

  • Thanks!

  • I implemented this algorithm and it's actually quite good. Just to share, if your LOS detection is more spatially critical than temporally critical (e.g. if you care more about detecting tiny things inside the cone as opposed to detecting them if they enter the cone for tiny time intervals) you can do something like use a very small density of points while adding +/- noise proportional to the spacing between points. So instead of lined up points as in the picture, imagine a cone with garbled up uniformly distributed points that change frame by frame. Basically what you're doing is trading off temporal accuracy for spatial accuracy, instead of trading framerate performance (e.g. increasing the number of points checked) for spatial accuracy. Neato.

    I guess the reverse of that would be to increase the number of points but decrease the time interval at which you're checking (so every 5 frames).

    With some combination fo both you can get some really great performance for tons and tons of LOS objects.

  • tulamide , I like your LOS example and tried downloading the .cap file to investigate further but C2 doesn't want to open it. Do I need to change the .cap to .capx in order for C2 to recognize it?

  • tulamide , I like your LOS example and tried downloading the .cap file to investigate further but C2 doesn't want to open it. Do I need to change the .cap to .capx in order for C2 to recognize it?

    This is for the - now long retired - Construct Classic software.

    The .cap produced by CC is in no way at all compatible with C2 or C3.

    The only way to open it would be to install CC. It might then be possible to convert the code from CC to C2/C3 as they use a similar Event system.

  • > tulamide , I like your LOS example and tried downloading the .cap file to investigate further but C2 doesn't want to open it. Do I need to change the .cap to .capx in order for C2 to recognize it?

    >

    This is for the - now long retired - Construct Classic software.

    The .cap produced by CC is in no way at all compatible with C2 or C3.

    The only way to open it would be to install CC. It might then be possible to convert the code from CC to C2/C3 as they use a similar Event system.

    Ah, thanks zenox98

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)