with that said, lets dive in!
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:
With the principle said, lets get onto applying this in game.
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 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
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)
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
With that done , the finished result should look something like this:
with the added effect of having a conical field:
This method also is able to be used for many different angles, as seen here