Navigation Compass - Markers

Hi ! I stumbled on a post regarding RPG Compass like in Skyrim (Roblox Studio Compass Quest) a while ago. Sadly the post was closed, yet I decided to implement it in Roblox.

So far, I have the compass with world directions that works like this:

while true do
	local delta = wait(1/60)
	
	local look = camera.CoordinateFrame.lookVector
	local look = Vector3.new(look.x, 0, look.z).unit
	local lookY = math.atan2(look.z, look.x)
	
	local difY = restrictAngle(lookY - lastY)
	lookY = restrictAngle(lastY + difY*delta*smoothness)
	lastY = lookY

	PositionIcons(look)
	
	for unit, rot in pairs(units) do
		rot = restrictAngle(lookY - rot)
		if math.sin(rot) > 0 then
			local cosRot = math.cos(rot)
			unit.Visible = true
			unit.Position = UDim2.new(0.5 + cosRot*0.6, unit.Position.X.Offset, 0, 3)
		else
			unit.Visible = false
		end
	end
end

The problem I have is about the function PositionIcons, aka markers on that compass. The function is as below:

for WaypointIcon, WaypointPosition in pairs(WaypointPositions) do
		local difWaypoint = math.acos(WaypointPosition:Dot(Root.Position)/(WaypointPosition.Magnitude * Root.Position.Magnitude))
		WaypointIcon.Visible = true
		WaypointIcon.Position = UDim2.new(difWaypoint, 0, 0, 0)
	end

The result looks like this, which isn’t correct (the markers aren’t show in proper way, the Store is on the left, while here we see it’s on the right, school isn’t on the left). Does anyone know how to make it work properly? I didn’t find any solution for a couple of days.

Perhaps someone from the previous topic will know how to solve it:
@kala6nik168
@ThanksRoBama

What I will say is approach this from a logical standpoint - by diagnosing the problem we can look at solving the problem effectively:
By following a logical thought process such as breaking down the problem into its composite parts we can seek to work out what is actually wrong.

STEP ONE: Check the maths behind the icons is correct.
If you display the angle the icon is supposed to be at, is this number correct?

  • If the number is correct, it implies their is a problem with how its displayed.
  • If the number is incorrect, it implies their is a problem with the maths

SCENARIO ONE: The maths was incorrect, oh no!
If your maths is incorrect, you need to look at why your maths is incorrect.

  • To start with, whats important is we work out if theirs any deriving logic behind it. Because code is logical, the result is always logical → therefore if the result is incorrect, it is because our logic is incorrect. I am not going to do the error handling for you at this moment in time (especially as I can see you understand the maths in part) but instead would implore you to test.

SCENARIO TWO (less likely): The maths was correct, so what is wrong???
If your maths is correct, it likely suggests the Icon is not being placed correctly.

This can be for many reasons including:

  • The icon anchor point is not correct, so whilst it should be displaying correct instead you forgot to account for a specific property.
  • The general position in the bounding is incorrect, usually implies the maths is wrong.

I can already tell you the maths is likely wrong though, as the map logo should not be be visible if your arc is 90 degrees just from visual inspection of where the icon is.

If you are struggling in a few days time, I’m happy to run through the code line by line and try and help.

1 Like

Thank you for you reply, @6Clu !

After hours of debugging and attempts, it’s really likely that math is incorrect in my solution. Right now the rotation to Gas Station from the point where I stand should me math.pi * 2/4 (in the direction of W). This value is around 1.57, meanwhile the value I get from:

local difWaypoint = math.acos(WaypointPosition:Dot(look)/(WaypointPosition.Magnitude * look.Magnitude))

is 0.4. Strangely enough the icon for Gas Station in that moment is shown as it should be, close to the W marker.

I have tried repeating process of thinking from other game engines, including right vector and forward vector. I feel there is something I’m missing or I understand wrongly, which affects the logic of the compass.

I can tell you for a fact, the compass markers are correct. However, if the value produced is 0.4 then it suggests your maths is correct in this instance and that the market is being incorrectly placed on the line.

This could simply be verified by taking the UI, adding the marker, and then position such marker at 0.4 in - if it shows correctly then it means the whole maths is just wrong to begin with - but seeming as 0.4 is a plausible number it is likely the marker isn’t sitting properly on the line.

A bit of advice in regards to the maths of a compass however:

  • A lot of people on Roblox, and in games in general, get way too caught up with the 3D element of a game. A compass like such is a 2 dimensional component, and as such the maths behind it should be treated as a 2 dimensional component.
  • If your starting point is (0,0), and your new point is r units away at an angle of θ, you can find the coordinates of that point using the equations x = r cosθ and y = r sinθ. Additionally, you can find the angle by reverse engineering these equations such that x/r = cosθ. You will need to do additional handling as in these instances they are bound to a set 180 degrees of values, but its a lot simpler then trying to do the 3d math equivelant.

If I wanted to create this myself:

  • I would collect the angle of all my markers around me, get the angle of such markers using 2D maths (using the x and z components of part.Position) relative to me. Resolve this into degrees based on North.
  • Seeming as I know my camera orientation, set an upper and lower bound of co-ordinates I’m willing to accept, again based on North. This is because I know my camera is facing a certain way based on North - so all my angles have a shared property
  • Iterate through, if they are within the bounding of lets say 235-315 then I would do:
    θ - 235 / (235 - 315) → this results in a number from 0-1 which is then the place it needs to be on the line.

I would not get too hooked up in 3d maths unless your making a 3d compass.

1 Like

It’s how I was thinking about it yet I still can’t manage to implement it properly. Right now I tried to change the strategy and do it as you mentioned, so the function looks like this:

for WaypointIcon, WaypointPosition in pairs(WaypointPositions) do
		local difWaypoint = math.deg(math.atan2(WaypointPosition.X * look.Z - WaypointPosition.Z * look.X, WaypointPosition.X * WaypointPosition.Z + look.X * look.Z))
		if math.sin(difWaypoint) >= 0 then
			local cosRot = math.cos(difWaypoint)
			WaypointIcon.Visible = true
			WaypointIcon.Position = UDim2.new(rotationToGuiPosition(difWaypoint), 0, 0, 0)
		else
			WaypointIcon.Visible = false
		end

The function restrictAngle looks like this; and it checks if the difWaypoint is in the view.

function restrictAngle(angle)
	if angle < -math.pi then
		return angle + math.pi*2
	elseif angle > math.pi then
		return angle - math.pi*2
	else
		return angle
	end
end

Finally in rotationtoGuiPosition I tried to implement what you mentioned:

θ - 235 / (235 - 315) → this results in a number from 0-1 which is then the place it needs to be on the line.

I don’t think it works though, for θ = 50, then we have the result of 2.3125.

With all those changes, the icons stay on the right side of the compass and don’t move.

Before I go into trying to explain the whole theory again in detail to see if it helps:

θ = Angle
x = Lower Bound of Camera Angle
y = Upper Bound of Camera Angle

Formula:

θ-x / (x-y)
Where: x-y is equal to the max shown angle in the compass.
Range: x <= θ <= y

If the value is outside of the range, the symbol should not be shown on the compass.

1 Like

Not entirely. When I chose -180 to 180 cause it matched my situation, then indeed the things on the left show as intended, but those that are visible on the start (for example School is at angle 3 from me at the start) while turning around they become at angles 2-0 so they still stay in the in x - y range and remain visible in the compass.

local difWaypoint = math.deg(math.atan2(WaypointPosition.X * look.Z - WaypointPosition.Z * look.X, 
			WaypointPosition.X * WaypointPosition.Z + look.X * look.Z))
local x = -180
local y = 180
	
local shown_angle = difWaypoint-x/(90)
print(WaypointIcon, shown_angle)
		
if shown_angle >= x and shown_angle <= y then
	WaypointIcon.Visible = true
	WaypointIcon.Position = UDim2.new(0.5, 0, 0, 0)
else
	WaypointIcon.Visible = false
end

If you choose your range as -180 < x < 180 then of course everything stays in the compass - you’ve basically said “I want everything to show on my compass”. The range you should look at should be relative to the current camera orientation - for example -45 degrees → +45 degrees from the camera orientation.

I’ve included a place here (open source) which handles locations based on relative angle - I hope this helps.
Compass Basic

If you wish to add additional locations, all you will need to do is constantly redo the maths for additional locations so that they are represented relevant to the player.

1 Like

I started with -45 and 45 yet the result was exactly the same, so I tried to find the good range.

Thank you for sharing the example, I checked it and my solution is similar to what you have implemented. The problem is that the NESW works perfectly fine for me and only markers seem to be problematic.

Here it is and how it works right now: Linear Compass - Roblox

I used the different method to hide the markers (math.sin with restricted angle) yet it seems to work as it pleases for some reason and the markers still disappear too soon or appear too late. Not to mention that their position is also wrong, cause the formula in rotationtoGui doesn’t always give 0-1 results.

Hey, i’ve created a simple function that will get the distance alpha relative from the center of the ui bar . I believe this can be integrated with your ui. The clamp function is used to ensure that it’s only accounting for the Y rotation.

local function clamp(v3)
    return Vector3.new(v3.X,0,v3.Z).Unit
end

local fov = 100
local function get_alpha(origin,point,cam)
    local dif = clamp(point-origin)
    local dot_p = dif:Cross(Vector3.new(0,1,0)):Dot(clamp(cam.CFrame.LookVector))
    local direction 
    if dot_p > 0 then 
        direction = -1
    else
        direction = 1
    end

    local dot_p2 = dif:Dot(clamp(cam.CFrame.LookVector))

    local distance_alpha = (math.deg(math.acos(dot_p2)))/(fov/2)*direction
    return distance_alpha
end

When the alpha is above 1 or below -1, then the marker should be masked as it is out of the FOV.

2 Likes

Thank you, I’ll test it out !

Hopefully it will work as I’ve been thinking about it for few days now and no luck —

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.