2D Damage Direction Indicator (Unit Vector math)

So I’m trying to make effectively a compass that always points to Point A on your screen. I’m doing this by finding the unit vector of A - Player and then mapping that onto a UDim2

One problem I’m aware of is that right now it uses Camera.p, meaning if I’m looking west by my camera position is east of the object, it’ll reflect the position of the camera (and not the Look Vector as it should).

Another problem I’m aware of is it just doesn’t accurate map the position. Any help (and explanation of what I did wrong) is very welcome!

local Radius = 50

local Players = game:GetService("Players")

wait(1)

local Player = Players.LocalPlayer
local PlayerGui = Player.PlayerGui

local Camera = workspace.CurrentCamera

local A = workspace:WaitForChild("A")

function Vec3ToVec2(Vec3)
	return Vector2.new(Vec3.x, Vec3.z - (Vec3.z * 2)).unit
end

function FindUnitVector(A, B)
	return (B - A).unit
end

local ScreenGui = Instance.new("ScreenGui", PlayerGui)
local CentrePoint = Instance.new("Frame", ScreenGui)
local Image = Instance.new("ImageLabel", CentrePoint)

CentrePoint.Size = UDim2.new(0, 0, 0, 0)
CentrePoint.Position = UDim2.new(.5, 0, .5, 0)

Image.Size = UDim2.new(0, 20, 0, 20)

while wait()do
	local aVec = Vec3ToVec2(A.Position)
	local bVec = Vec3ToVec2(Camera.CFrame.p)
	
	local unitVec = FindUnitVector(aVec, bVec)
	Image.Position = UDim2.new(0, unitVec.X * Radius, 0, unitVec.Y * Radius) 
end

The easiest way to do this is to figure out where A is relative to the camera’s orientation and position then only use the X an Z axes of that position, converting them to a unit vector.

Finding a position relative to another position and orientation is called moving into object space. Normally, you’re in world space because everything is relative to the “world” – starting at 0, 0, 0 with no rotation.

When you move into object space, everything becomes relative to a certain position and certain rotation. For example, normally (0, 0, 1) would mean “move 1 unit on the global Z axis”, but in object space it means “move forward one unit”. When you convert back to world space, you will be one unit in front of the object, not one unit on the Z axis.

A good way to visualize this is the Position and Rotation properties of attachments. Those move the attachment relative to their parent part. Those properties are in object space.


Anyways, this will move the point around properly:

while wait() do
	-- step 0: get the camera cframe not tilted up or down
	local flatCameraCFrame = CFrame.new(Camera.CFrame.p, Camera.CFrame.p + Camera.CFrame.lookVector*Vector3.new(1, 0, 1))
	-- step 1: convert A's position from world space to object space
	local pointRelativeToCamera = flatCameraCFrame:pointToObjectSpace(A.Position)
	-- step 2: remove the Y axis and make it a unit vector
	local unitRelativeVector = (pointRelativeToCamera*Vector3.new(1, 0, 1)).unit
	
	Image.Position = UDim2.new(0, unitRelativeVector.X * Radius, 0, unitRelativeVector.Z * Radius) 
end

You can also get the rotation for this point easily using atan2:

while wait() do
	-- step 0: get the camera cframe not tilted up or down
	local flatCameraCFrame = CFrame.new(Camera.CFrame.p, Camera.CFrame.p + Camera.CFrame.lookVector*Vector3.new(1, 0, 1))
	-- step 1: convert A's position from world space to object space
	local pointRelativeToCamera = flatCameraCFrame:pointToObjectSpace(A.Position)
	-- step 2: remove the Y axis and make it a unit vector
	local unitRelativeVector = (pointRelativeToCamera*Vector3.new(1, 0, 1)).unit
	
	Image.Position = UDim2.new(0, unitRelativeVector.X * Radius, 0, unitRelativeVector.Z * Radius) 
	
	-- use atan2 to convert from Y and X positions to rotation
	-- we're using Z for our 'Y' in this case
	-- atan2 is (y, x)
	-- it's based on inverse tan, which uses y/x
	local rotation = math.atan2(unitRelativeVector.Z, unitRelativeVector.X)
	-- atan2 gives us our rotation in radians, but gui rotations use degrees
	Image.Rotation = math.deg(rotation)
end
13 Likes