Jump to content

Hm.. more trigonometry. Yay!


robhol

Recommended Posts

Okay, I want to make a system for admins in my server, that will list all players that are approximately in front of the user's facing direction. I've made you guys a super paint illustration (>.>) to hopefully make it a bit clearer.

trigct0.gif

I will of course have to loop through all the players and check something, I just have no idea about the math in this one. Help please? :P

Link to comment

This is a function I've made some while ago. It is not perfect because it only takes 2 dimensions into account but it may give you an idea how to proceed. What it does is return true if target is within a certain angle of the source' line of sight.

The parameters are as follows:

  
source = any element representing the LOS source 
target = any element to test the angle against 
traceAngle = maximum angle to take into account (for example 20 degree) 
minDepth = minimal depth to take into account from source to target 
maxDepth = maximum depth to take into account from source to target 
minOffsetZ = minimal height offset to take into account from source to target 
minOffsetZ = maximum height offset to take into account from source to target 
  

  
function testTargetAngleAgainstLoS(source, target, traceAngle, minDepth, maxDepth, minOffsetZ, maxOffsetZ) 
  local oX, oY, oZ = getElementPosition(source) 
  local rotX, rotY, rotZ = getObjectRotation(source) 
  local tX, tY, tZ = getElementPosition(target) 
  local targetDist2D = getDistanceBetweenPoints2D(oX, oY, tX, tY) 
  local losX, losY, losZ = oX, oY, oZ 
  local btX, btY, btZ = oX, oY, oZ 
  local btDist2D = 0 
  local pX2D, pY2D = 0, 0 
  local pDist2D = 0 
  local ptotDist2D = 0 
  local angle = 0 
  if (targetDist2D < minDepth or targetDist2D > maxDepth or tZ < (oZ - minOffsetZ) or tZ > (oZ + maxOffsetZ)) then 
    return 
  end 
  losX = losX - math.sin(math.rad(rotZ)) * targetDist2D 
  losY = losY + math.cos(math.rad(rotZ)) * targetDist2D 
  btX = btX - math.sin(math.rad(rotZ + 180)) * targetDist2D 
  btY = btY + math.cos(math.rad(rotZ + 180)) * targetDist2D 
  btDist2D = getDistanceBetweenPoints2D(btX, btY, tX, tY) 
  if (tX > losX) then 
    pX2D = tX - (math.abs(tX - losX) / 2) 
  else 
    pX2D = losX - (math.abs(losX - tX) / 2) 
  end 
  if (tY > losY) then 
    pY2D = tY - (math.abs(tY - losY) / 2) 
  else 
    pY2D = losY - (math.abs(losY - tY) / 2) 
  end 
  pDist2D = getDistanceBetweenPoints2D(oX, oY, pX2D, pY2D) 
  ptotDist2D = getDistanceBetweenPoints2D(pX2D, pY2D, tX, tY) 
  angle = math.deg(math.tan(ptotDist2D / pDist2D)) * 2 
  return (angle > 0 and angle < traceAngle and targetDist2D < btDist2D) 
end 
  

Edited by Guest
Link to comment

A dot product might be a bit easier and faster perhaps, as it only requires a few lines of code.

Here is an example for finding the angle between two players relative to source rotation. This is 2d only as requested by opening poster.

  
function findAngle(sourcePlayer, targetPlayer) 
  
  local sourceX, sourceY, sourceZ = getElementPosition(sourcePlayer) 
  local sourceRotX, sourceRotY, sourceRotZ = getPedRotation(sourcePlayer) 
  local targetX, targetY, targetZ = getElementPosition(targetPlayer) 
  
  -- get relative position 
  local relativePosX = targetX - sourceX 
  local relativePosY = targetY - sourceY 
  
  -- get distance 
  local distance = math.sqrt(relativePosX*relativePosX + relativePosY*relativePosY) 
  
  -- normalize 
  relativePosX = relativePosX / distance 
  relativePosY = relativePosY / distance 
  
  -- get source angle vector. should already be normalized. I might have sin and cos the wrong way around.. 
  local sourceVecX = math.sin(math.rad(360-sourceRotZ)) 
  local sourceVecY = math.cos(math.rad(360-sourceRotZ)) 
  
  -- use dot product to find cos of angle between vectors 
  local dotProduct = (sourceVecX * relativePosX) + (sourceVecY * relativePosY) 
  
  -- return acos of dot product. convert angle to degrees before returning. 
  return math.deg(math.acos(dotProduct)) 
  
end 
   

This function will return a value between 0 and 180 representing angle away from the direction the admins player model is facing.

Of course it might be more convenient for the admin to use camera angle instead of ped angle.

Link to comment
A dot product might be a bit easier and faster perhaps, as it only requires a few lines of code.

Here is an example for finding the angle between two players relative to source rotation. This is 2d only as requested by opening poster.

  
function findAngle(sourcePlayer, targetPlayer) 
  
  local sourceX, sourceY, sourceZ = getElementPosition(sourcePlayer) 
  local sourceRotX, sourceRotY, sourceRotZ = getPedRotation(sourcePlayer) 
  local targetX, targetY, targetZ = getElementPosition(targetPlayer) 
  
  -- get relative position 
  local relativePosX = targetX - sourceX 
  local relativePosY = targetY - sourceY 
  
  -- get distance 
  local distance = math.sqrt(relativePosX*relativePosX + relativePosY*relativePosY) 
  
  -- normalize 
  relativePosX = relativePosX / distance 
  relativePosY = relativePosY / distance 
  
  -- get source angle vector. should already be normalized. I might have sin and cos the wrong way around.. 
  local sourceVecX = math.sin(math.rad(360-sourceRotZ)) 
  local sourceVecY = math.cos(math.rad(360-sourceRotZ)) 
  
  -- use dot product to find cos of angle between vectors 
  local dotProduct = (sourceVecX * relativePosX) + (sourceVecY * relativePosY) 
  
  -- return acos of dot product. convert angle to degrees before returning. 
  return math.deg(math.acos(dotProduct)) 
  
end 
   

This function will return a value between 0 and 180 representing angle away from the direction the admins player model is facing.

Of course it might be more convenient for the admin to use camera angle instead of ped angle.

Good piece of code.

However, I fear it's not what he's looking for... He's looking for something that shows ALL players in an angle just a bit bigger than his sight. And the looking depth as far away as you want. :P

Link to comment
However, I fear it's not what he's looking for... He's looking for something that shows ALL players in an angle just a bit bigger than his sight. And the looking depth as far away as you want. :P

Well yes, obviously. I just assumed he was competent enough to apply that function to all players on the server, and compare the returned angle with some predetermined limiting factor, say a 45 degree angle.

  
  
function getAllPlayersWithinAngleRangeOfSource(sourcePlayer, angleLimit) 
  
  local returnPlayerList = {} 
  
  for i,player in pairs(g_Players) do 
    if findAngle(sourcePlayer, player) <= angleLimit then 
      table.insert(returnPlayerList, player) 
    end 
  end 
  
  return returnPlayerList 
end 
  
  

Can you return tables in this manner? I forget. This should cover all users regardless of distance.

Anyway something like this is kind of basic, if a scripter can't even figure that out what chance does he really stand making something good anyway? ;) Oh well everything learned is progress on the way I suppose

Link to comment
However, I fear it's not what he's looking for... He's looking for something that shows ALL players in an angle just a bit bigger than his sight. And the looking depth as far away as you want. :P

Well yes, obviously. I just assumed he was competent enough to apply that function to all players on the server, and compare the returned angle with some predetermined limiting factor, say a 45 degree angle.

  
  
function getAllPlayersWithinAngleRangeOfSource(sourcePlayer, angleLimit) 
  
  local returnPlayerList = {} 
  
  for i,player in pairs(g_Players) do 
    if findAngle(sourcePlayer, player) <= angleLimit then 
      table.insert(returnPlayerList, player) 
    end 
  end 
  
  return returnPlayerList 
end 
  
  

Can you return tables in this manner? I forget. This should cover all users regardless of distance.

Anyway something like this is kind of basic, if a scripter can't even figure that out what chance does he really stand making something good anyway? ;) Oh well everything learned is progress on the way I suppose

You can return them this way, but you can't output them this way.

If you want to output them to the chatbox, you need table.concat. Which is simply something like:

foodTable={"apples","bananas","kiwis"} --Example table... LOL 
  
outputChatBox("Foods: " .. table.concat(foodTable,", ") .. ".") 

That SHOULD return:

Foods: apples, bananas, kiwis

Might be that you already knew that, but just to be sure. :P

Link to comment
You can return them this way, but you can't output them this way.

If you want to output them to the chatbox, you need table.concat. Which is simply something like:

foodTable={"apples","bananas","kiwis"} --Example table... LOL 
 outputChatBox("Foods: " .. table.concat(foodTable,", ") .. ".") 

That SHOULD return:

Foods: apples, bananas, kiwis

Might be that you already knew that, but just to be sure. :P

Yeah, I decided to use a return type that is more generic. As in, it is easy to use a list of player pointers to do various tasks, like fetch and display names or perform other operations, but a list of names is pretty limiting in what you can do without additional work. I sure am not silly enough to try outputting an element pointer directly to a display function requiring string input :P

Anyway, by now robhol should have enough information to set up his player scan just about any way he wants..

Sorry if my lingo is a bit off, I am accustomed to C/C++. I still think in terms of pointers / references.. I don't know how to 'talk lua'

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...