The Maths
The math needed for this is basic knowledge of trigonometric functions and of dot products, which you can find here and here respectively.
with that said, lets dive in!
The circle
above image represents the unit circle
the text indicates the rotation in radians at each point, for those of you unfamiliar with radians, pi radians is 180 degrees.
This shows the rotations counterclockwise, you should also know that rotations that are clockwise are considered negative and can be expressed as 2π + θ (or 2π - |θ|), due to the nature of circles(θ being the variable most commonly used for unknown angles).
Also important (marked with red line) is the length associated with the cosine value for each rotation. You can see from the diagram that cosine is an even function, meaning for any given θ, cos(-θ) = -cos(θ)
, this will be important for later.
Since the equation for the dot product is:
We can use this, and inverse trig functions to solve for theta:
it is useful to note that arccosine has a domain of [-1,1], due to the periodic nature of cosine
Due to this, and that from a bird’s eye view, a 0 degree rotation would face towards the top, rather than the right, a diagram of rotations from the front , instead of a traditional circle, would look something like this:
Application
With the principle said, lets get onto applying this in game.
Constants
In order to achieve the desired goals, we must first establish some constant values, namely
- Field / Range / Limit (max rotation on either side)
- Maximum Distance (optional) (maximum distance where a target can be seen)
- origin (what the object is “looking” from)
I will be going with 45 degrees for the first, 45 studs for the 2nd , and the head of the objects for the 3rd
Getting the relative vector
getting the relative vector from point A to point B is the vector from the origin to point B minus the vector from the origin to point A, or A -> B = B-A
, which can be seen from the following:
we can then get the directional component of this vector by using [vector].Unit
to find our forward facing vector, we can just use the lookvector of the object we are using
note that since both of these vectors are unit vector, we don’t need to explicitly divide by their magnitudes, allowing us to simply take the arccosine of the value given by v1:Dot(v2)
Putting it all together
finally, we are at the stage where we can assemble everything, but first, the steps
- define constants
- go through all targets
- establish forward vector,other vector, and angle between vectors
- check if they and in the field of view and within maximum distance
- do whatever you want
which would look something like this:
hidden code, for if you wish to try yourself
--step 1
local head = [Origin].Head
local defaultColor = BrickColor.new("Medium stone grey")
local maxDist = 45
local range = 45
local targets = [Target]
--step 2
for _,target in ipairs(targets:GetChildren()) do
target.Changed:Connect(function()
local relative = (target.Position-head.Position)
--step 3
local forward = head.CFrame.LookVector
local side = relative.Unit
local theta = math.deg(math.acos(forward:Dot(side)))
--step 4
if (relative.Magnitude < maxDist) and (theta <= range) then
--step 5
target.BrickColor = BrickColor.new("Lime green")
else
target.BrickColor = defaultColor
end
end)
end
Result
With that done , the finished result should look something like this:
https://gyazo.com/0db81873bf09fb6ef7b289af4f3d1d39
with the added effect of having a conical field:
https://gyazo.com/c8425a41a2a49edb5a3d5b467f6f7bc1
This method also is able to be used for many different angles, as seen here
https://gyazo.com/5f11fd6b595d973bfe43fb5a73708eea