Log In  

Line Of Sight Function

I`ve created a Line-Of-Sight function that should work for most projects. It checks for map-collisions(using flag0 as the collision layer). Please use it and let me know what you do with it!

I was inspired by this post: https://www.lexaloffle.com/bbs/?tid=48889
But i wanted a simpler starting point for my own approach. A Line Of Sight check is a good starting point for you own approach but also involves some math. So to bypass that step use my function:

UPDATE 16/9/22
added a optional length-variable. Use it to define a max "range" of the that line. Keep in mind that the function already neglects far away points.
Also added a new break point for perfomance.

function can_see(x1,y1,x2,y2,length)
--x1 and y1 are the point of view,length is the max distance of the two points
    local max_length=length or 1000
    local xvec=0
    local yvec=0
    local len=0
    local ret=true  
    local tx=x1
    local ty=y1
    xvec=x2-x1
    yvec=y2-y1
    len=sqrt(xvec^2+yvec^2)
    if len==0 or len>max_length then
     ret=false
    end
    xvec=(xvec/len)
    yvec=(yvec/len)
    if ret then
        for i=1,len do
        tx+=xvec
        ty+=yvec
            if fget(mget(flr(tx/8),flr(ty/8)),0) then
                ret=false
                break
            end
        end
    end
    return ret
end

Features (if you're intrested)

  • handling LOOONG Distances with a big "Nay" from the function
  • always checking from x1/y1 point of view
  • stopping when no more calculations are necessary

How it works

  • set standard return to a yes (true)
  • create a directional vector from 1 to 2
  • calculate the distance of that vector
  • check wether that distance is too big
  • normalize it(max it out at 1) with that distance(so your steps don't get bigger than 8(which would skip a whole sprite, thus negating the whole point of this function))
  • loop through incremental steps with that normalized vector
  • check for collisions on each point
  • if a collision occurs change the output to no and exit the loop to save power

How you might use it(and how i use it)

  • Use it for a stealth game and combine it with the general direction the enemys are facing
  • Use it for shooting stuff to see how far things will go
  • Use it as a starting point for a raycast by returning the distance the ray traveled (I'd like to do that too)
  • I'm making my first roguelike to learn about Mazecreations and get better at making fights fun (Which i'm terrible at)
  • in this game the enemys, similar to that post linked above, remember the last point where they saw you and approach that point(the white dot). They also have a timer, so they only try to spot you every now and then.

You can find a minimal setup for the function right here:

Cart #lineofsight-1 | 2022-09-02 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
7

P#116768 2022-09-02 10:18 ( Edited 2022-09-16 06:44)

Wow this is amazing! I'm saving this one to use on future games. Makes me glad to know you were inspired by my bullet trail approach :D yours is way more elegant! 10/10

P#116782 2022-09-02 16:02
1

Thank you! But yours had all the bells and whistles, mine is really just one part of your great system!

P#116795 2022-09-02 21:12
1

This should be possible to do without using SQRT() or exponent, @taxicomics.

Also does it register bricks not just below but above ?

Hmm ... This stands to write code.

Cart #kebuyusejo-0 | 2022-09-02 | Code ▽ | Embed ▽ | No License
1

Here now, your routine works perfectly !

Use arrow keys to control both sprites. Swap between which with ❎.

P#116798 2022-09-02 22:07 ( Edited 2022-09-02 22:50)
1

What do you mean by below or above?
If you`re talking about the width of the line-yes, i have been thinking about that. Maybe ill add another line so the FOV gets a little less tight, but that'll cost more power. But in some situations it might be able to see through a wall if the two tiles have just one joined corner.

And yes, i do remember that there was a neat way to avoid sqrt, but as i figured that my application only deals with numbers smaller than 128 i did not utilize it.

P#116800 2022-09-02 22:25
1

Actually no problems, @taxicomics. It works perfectly. Try the demo above to really see it works in all situations.

Well done, gold star work !

P#116802 2022-09-02 22:51
2

Thought I would try to recode that without the exponent or SQRT. Here is what I have:

Cart #nipuwuwoda-0 | 2022-09-02 | Code ▽ | Embed ▽ | No License

function can_see2(a,b,c,d)
local e,f=abs(a-c),abs(b-d)
  if (e<f) e=f
  e=max(1,e)
  repeat
    if (fget(mget(c\8,d\8),0)) return
    c+=(a-c)/e
    d+=(b-d)/e
  until a==(c+.5)\1 and b==(d+.5)\1
  return true
end
P#116804 2022-09-02 23:21

Cart #geyatoyazu-0 | 2022-09-03 | Code ▽ | Embed ▽ | No License

Here is a further variation. This one checks only grid boxes assuming that all sprites to move in an 8x8 format.

Use arrow keys to navigate. Press ❎ to swap between control of sprite. If one can see the other, the background will tint dark blue, otherwise black.

The dots you see are the line of sight and optionally drawn in the function.

P#116827 2022-09-03 15:37

Hey, thanks for the rewrite! Intresting approches too, now this is the perfect compilation for everybody to choose a version depending on their needs. Good collab!

P#116883 2022-09-04 17:10

Yup ! Quite a few choices, @taxicomics. Now if I could just write AStar or MyStar in code as small or smaller than I've done above we'd be all set. :)

And yes, that would be a great internal function to add to future Pico-8.

_can_see(x1,y1,x2,y2,v,{sx,sy,ex,ey,{a[]})

x1 + y1 = coordinates of player.
x2 + y2 = coordinates of target.
      v = value looked for.
sx + sy = start of area to look (default 0,0)
ex + ey = end of area to look (default 63,63)
      a = array, if not nil use array[sizex][sizey],
          otherwise if nil then use map() as normal.

wallid=1
if _can_see(playerx,playery,enemyx,enemyy,wallid) then ...

scrn={}
for i=0,15 do
  scrn[i]={}
  for j=0,15 do
    scrn[i][j]=0
  end
end
b=_can_see(playerx,playery,enemyx,enemyy,wallid,0,0,15,15,scrn)
P#116884 2022-09-04 17:25

Those two bundled would be great! I haven't done any A-star-stuff yet, i used the native pathfinding in Godot but i never wrote my own.
But for A-Star you need a little more code around the function, so i think it is not as straightforward to implement as can_see(). But maybe if it is bundled with the movement and maybe a speed or time() variable, then it might be easier to use for beginners.
Like a Move_To(x1,y1,x2,y2,Speed_in_pixels,collision_flag) that returns the progress or proximity.

P#116926 2022-09-05 12:43 ( Edited 2022-09-05 12:44)

I added a new optional variable to the function that i needed in a new project. length, the optional fifth variable, now specifies the max length of that line of sight.

function can_see(x1,y1,x2,y2,length)
--x1 and y1 are the point of view
    local max_length=length or 1000
    local xvec=0
    local yvec=0
    local len=0
    local ret=true  
    local tx=x1
    local ty=y1
    xvec=x2-x1
    yvec=y2-y1
    len=sqrt(xvec^2+yvec^2)
    if len==0 or len>max_length then
     ret=false
    end
    xvec=(xvec/len)
    yvec=(yvec/len)
    if ret then
        for i=1,len do
        tx+=xvec
        ty+=yvec
            if fget(mget(flr(tx/8),flr(ty/8)),0) then
                ret=false
                break
            end
        end
    end
    return ret
end
P#117503 2022-09-16 06:45

If I wanted to use this sort of thing in a platformer, such as to have an archer enemy check for line of site whenever player.y is within 10 pixels of archer.y, then if true, fire an arrow in the direction of the player, would that work?

P#123545 2023-01-02 23:04

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2023-05-30 08:30:33 | 0.035s | Q:39