When making a game, I realized that creating 90 or more ray-casts for the vision of a bad guy seems a little bit silly. So I realized that some engines use a funnel which it like a ray-cast but more spread out. I don't now how to do this in ROBLOX, so help will be appreciated.
Another way to do this is to make your funnel around your target with raycasts.
Take in the number of raycasts you want, and the size of the radius for the circle being drawn around the target.
--Your start and finish positions local start,finish = workspace.Part1,workspace.Part2 --Number of raycasts, radius size local castRate,castRadius = 12,4
Now, since there are 360° in a circle, dividing 360 by the 'castRate' variable will get the size of each rotation increment in the circle. If you multiply this by the current iteration in a for loop, you can begin to draw the ray.
for i = 1,castRate do --'i' is the current iteration, 360/castRate is the rotation increment local step = i * (360/castRate); end
Next step? Get the desired endpoint for the raycast. This is where the circle gets drawn. Multiply the 'finish' variable's CFrame by CFrame.Angles(0,math.rad(step),0)
to rotate the CFrame. Here is a visual of what this does to the CFrame: before and after. Note: In real world space the '_end' CFrames would be inside the 'finish' CFrame.
for i = 1,castRate do local step = i * (360/castRate); --Rotate the CFrame outwards local _end = (finish.CFrame * CFrame.Angles(0,math.rad(step),0) end
As to why rotating the CFrame outwards is necessary.. this makes us able to "pull" the CFrame out, creating a circle. Multiply the rotated CFrame by CFrame.new(0,0,-castRadius)
to move it out. Here is a visual of what that does.
So the finished product would look like this;
local start,finish = workspace.Part1,workspace.Part2 local castRate,castRadius = 12,4 for i = 1,castRate do local step = i * (360/castRate) local _end = ( finish.CFrame * --Origin of circle CFrame.Angles(math.rad(step),0,0) * --Current rotation CFrame.new(0,0,-castRadius) --Move it out ); local ray = Ray.new( --Draw the ray start.CFrame.p, --Start point (_end.p - start.CFrame.p).unit * 300 --Direction of '_end' point ) local part = workspace:FindPartOnRay(ray) --Find the part if part ~= nil then --Check if it's real print(part.Name) --Print the name end end
You can set the 'castRate' variable to any amount you want. If you think 12 raycasts is still too much then lower it to whatever number you want. But the more you lower it the less accurate the NPC's 'sight' will be.
Use trigonometry.
Take the position of anything that you want to check if it is in the bad guy's field of vision, convert it to object space of the bad guy, and take math.acos(badguy.Position.Z/badguy.Position.Magnitude), and check if the angle is within a certain amount.
badGuy = game.Workspace.BadGuy --This function takes inputs of the object you want to check, the badGuy, and the angle range of the bad guy's field of vision function checkObject(object,badGuy,angle) local pos1 = badGuy.Head.Position --Convert the point to object space local pointToCheck = badGuy.Head:pointToObjectSpace(object.Position) --In order to only look at the X and Z values (don't take height into account) pointToCheck = Vector2.new(pointToCheck.X,pointToCheck.Z) --Get the angle from the badguy local angleGot = math.acos(pointToCheck.Y/pointToCheck.Magnitude) --Check to see if the position is in the field of view if angleGot < angle and angleGot > -1 * angle then return true else return false end end
One small thing to note in my code is that the angle is the deviation from the lookVector, so if you want a 90 degree total field of vision, your angle input has to be 45 degrees.