I want to make a custom movement engine, and to make a good jumping system, I want to get the closest vector to the player that is also on the “top-est” face within a part. You can see what I mean on the screenshots below. Red is the vector that should ideally be returned
Ignoring my last attempt… maybe try…
Local Distance = (Player.HumanoidRootPart.Position - Part.Position)
Distance = (math.clamp(Distance.X, Part.Position.X-(Part.Size.X/2), Part.Position.X+(Part.Size.X/2), THEN Y AND Z in same pattern yk)
Then red dot.position = Distance … I think that could work.
My math ig:
PlrRootPos = vector3.new(0,0,0)
Part.Position = vector3.new(-1, 0, 1)
Part.Size = vector3.new(2,2,2)
Distance = vector3.new(1, 0, -1)
Then multiply that distance by the part’s CFrame like this:
Idk = part.CFrame * CFrame.new(Distance)
Here’s a function (getClosestPointOnTopFace) for this and the code I used to test the function. It converts the HumanoidRootPart’s position to the part’s local space, clamps it and converts the result back to world space. I used vector:Dot(Vector3.FromAxis(axis)) instead of vector[axis.Name] because strict type checking doesn’t like dynamic property accesses.
--!strict
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local function getNormalIdForTopFace(part: Part): Enum.NormalId
local greatestY: number = 0
local normalIdWithGreatestY: Enum.NormalId
for _, normalId: Enum.NormalId in Enum.NormalId:GetEnumItems() do
local y: number = part.CFrame:VectorToWorldSpace(Vector3.FromNormalId(normalId)).Y
if y > greatestY then
greatestY = y
normalIdWithGreatestY = normalId
end
end
return normalIdWithGreatestY
end
local function getClosestPointOnTopFace(pos: Vector3, part: Part): Vector3
local topFaceNormalId: Enum.NormalId = getNormalIdForTopFace(part)
local normalAxisValue = topFaceNormalId.Value % 3
local axisEnumItems: {Enum.Axis} = Enum.Axis:GetEnumItems()
local otherAxis1: Enum.Axis = axisEnumItems[(normalAxisValue + 1) % 3 + 1]
local otherAxis2: Enum.Axis = axisEnumItems[(normalAxisValue + 2) % 3 + 1]
local normalVector: Vector3 = Vector3.FromNormalId(topFaceNormalId)
local otherAxis1Vector: Vector3 = Vector3.FromAxis(otherAxis1)
local otherAxis2Vector: Vector3 = Vector3.FromAxis(otherAxis2)
local relativePos: Vector3 = part.CFrame:PointToObjectSpace(pos)
local closestPointInPartSpace: Vector3 =
.5 * math.abs(part.Size:Dot(normalVector)) * normalVector
+ math.clamp(relativePos:Dot(otherAxis1Vector), -.5 * part.Size:Dot(otherAxis1Vector), .5 * part.Size:Dot(otherAxis1Vector)) * otherAxis1Vector
+ math.clamp(relativePos:Dot(otherAxis2Vector), -.5 * part.Size:Dot(otherAxis2Vector), .5 * part.Size:Dot(otherAxis2Vector)) * otherAxis2Vector
return part.CFrame:PointToWorldSpace(closestPointInPartSpace)
end
local char: Model = Players.PlayerAdded:Wait().CharacterAdded:Wait()
local part: Part = Instance.new("Part")
part.Name = "TopFaceClosestPointTestPart"
part.BottomSurface = Enum.SurfaceType.Smooth
part.Anchored = true
part.Orientation = 360 * Vector3.new(math.random(), math.random(), math.random())
part.Size = Vector3.new(16, 4, 8)
part.Position = Vector3.new(-10, 8, -20)
part.Parent = workspace
local closestPointAttachment: Attachment = Instance.new("Attachment")
closestPointAttachment.Name = "ClosestPointAttachment"
closestPointAttachment.Visible = true
closestPointAttachment.Parent = part
RunService.PostSimulation:Connect(function()
closestPointAttachment.Position = part.CFrame:PointToObjectSpace(getClosestPointOnTopFace((char:FindFirstChild("HumanoidRootPart") :: Part).Position, part))
end)