I’m trying to generate a random position somewhere near the player. The position has to be within a certain radius of the player. However, I want the position to be generated outside the player’s FOV. This position must be generated on the server. I already have the player’s camera FOV and CFrame.
This is my current code:
local angle = math.random() * 2 * math.pi
local random_distance = math.random(MIN_DIST, MAX_DIST)
local cframe = CFrame.new(center) * CFrame.Angles(0, angle, 0) * CFrame.new(0,0, -rndDist)
This generates a random position around the player, but it doesn’t disregard the possibility of the position being generated within the player’s FOV.
What I would do is take the code you have, then use WorldToViewportPoint to check if the point is in their FOV. If so, find a new point, check it, and so on and so forth.
local function generate_point()
local angle = math.random() * 2 * math.pi
local random_distance = math.random(MIN_DIST, MAX_DIST)
local cframe = CFrame.new(center) * CFrame.Angles(0, angle, 0) * CFrame.new(0,0, -rndDist)
local vector, onScreen = camera:WorldToViewportPoint(cframe.Position)--if you are doing this on the server, you will need to call a remotefunction to do this
if onScreen then
task.wait() --you may get unlucky and keep getting points in the FOV. If this happens, having a wait will ensure that the script doesn't time out since this *technically* is a loop.
generate_point()
else
return cframe
end
end
local random_point = generate_point()
I don’t think that this would be good because then I would have to send requests to the client every time the generated position is within the player’s FOV, but it might work if nothing else does
Why are you doing it on the server to begin with? No matter what you do, you are going to have to do some form of math on the client if you want an accurate check of if a part is in their FOV.
The problem with using the characters CFrame is that if the game isn’t locked in first person, then the camera could be pointed somewhere different than where the character is facing.
Is your game single-player or multi-player? If it is single-player, you can just do the teleport on the client. If it is multi-player, you can do the calculations on the client and then fire a RemoteEvent to actually set the NPC’s position on the server.
If that’s the case you need to network the rotation of the camera to the server and then do the same program except using the camera’s CFrame or sentCFrame. The rootPart is the HumanoidRootPart of the character, but it can be any reference position.
Right. I just feel like they should just do the calculations on the client and fire a RemoteEvent to the server to actually set the NPC’s position, rather than getting the camera CFrame from the client and then doing the calculations on the server. This way, they can make use of camera:WorldToViewportPoint.
Still seems like a hacky method. A RemoteFunction that checks if a part is on the screen using camera:WorldToViewportPoint would still (in my opinion) be the best, despite the creator shooting down that idea originally.
worldtoscreenpoint calculates if something is in the frustum which is unnessescarily specific for npcs that just need to spawn behind their back
spamming worldtoscreenpoint to check randomly generated positions is a waste of resources as its significantly more expensive to do frustum checks than a dot product for example
the positions can be exploited because they can only be checked on the client
The targeted player occasionally sends some information to the server script through a remote event. The information contains the camera FOV and CFrame. When the NPC wants to teleport, it’ll wait for the remote event to fire. Once fired it will try to generate a random position outside the targeted player’s FOV. This code sort of works, but it’s not very reliable.
local function AngleBetween(vectorA, vectorB)
return math.acos(math.clamp(vectorA:Dot(vectorB), -1, 1))
end
function GeneratePosition(FOV, cameraCFrame)
local randomAngle = math.random() * 2 * math.pi
local randomDistance = math.random(50,100)
local cframe = CFrame.new(character.HumanoidRootPart.Position) * CFrame.Angles(0, rndAngle, 0) * CFrame.new(0,0, -dist)
local lookForward = camera.CFrame.LookVector
local lookToPoint = (cframe.Position - cameraCFrame.Position).Unit
local angle = AngleBetween(lookForward, lookToPoint)
local fovCheck = math.rad(FOV)/2
if math.abs(angle) <= fovCheck then
task.wait()
return GeneratePosition(FOV, cameraCFrame)
else
return cframe.Position
end
end
I would prefer not to make the function loop until it finds a position outside the FOV, but rather limit where the random positions can be generated.
This only works if the player is in first person. I need the position to always be generated around the player while also being generated behind the camera. Anyways, thank you for your help, I appreciate it