Finding distance to a wall regardless of where you are

I am trying to make a system which finds the closest player to a wall. I have used magnitude, however, this is giving me bad results:

By this logic, the middle NPC would be closest just because the other two lines are diagonal (the magnitude is from the centre of the wall to the players)

Ideally, I want each player’s distance to the wall to be counted in a straight line from their forward vector:

Any ideas?

7 Likes

Can’t you just cast a ray in that direction, then subtract the starting position with the point where the ray intersects with the wall?


For example:

local RayResult = workspace:RayCast(StartPos, Direction, Params) -- Params should have whitelist and have the wall in the whitelist
if RayResult then
    local Magnitude = (StartPos - RayResult.Position).Magnitude -- Gets the magnitude of the starting position and the position the ray intersected with the wall
end
2 Likes

Is this solution beneficial to you?

1 Like

What about if the player is facing the other direction? I still want the distance

5 Likes

Just calculate if the player is in the other distance

Else, hmm, this is a hard problem. Ok, i will try to help u. Why not try to cast a ray like @VegetationBush said? But you are using the WorldSpace so that it will not be affected from the player rotation. First try with a wall:

--Variables
local Player = YourPlayer
local Character = Player.Character
local HRP:BasePart = Character.HumanoidRootPart

--While Player is unrotated, at spawn if u want to say it
local VectorToWall:Vector3 =  (TargetWall.Position - MiddlePart.Position).Unit --A unit vector direction

--Now using @VegetationBush tip

local RayResult = workspace:RayCast(HRP.Position, (HRP.Position+VectorToWall)*300, Params) -- Params should have whitelist and have the wall in the whitelist
if RayResult then
    local Magnitude = (RayResult.Position - HRP.Position).Magnitude -- Gets the Magnitude between Raycasted point (the wall) and the Player
end

If the player is in the other direction just use the dot product
(

)
Or, if you dont want, then make the same calculations but the other way and pick the lowest magnitude (to check if at what wall the Player is):


--Variables
local Player = YourPlayer
local Character = Player.Character
local HRP:BasePart = Character.HumanoidRootPart

--While Player is unrotated, at spawn if u want to say it
local VectorToWallA:Vector3 = (TargetWallA.Position - MiddlePart.Position).Unit --A unit vector direction
local VectorToWallB:Vector3 = (TargetWallB.Position - MiddlePart.Position).Unit

--Now using @VegetationBush tip
local MagnitudeA:number
local MagnitudeB:number
local MagnitudeToUse:number

local RayResultA = workspace:RayCast(HRP.Position, (HRP.Position+VectorToWallA)*300, Params) -- Params should have whitelist and have the walls in the whitelist
local RayResultB = workspace:RayCast(HRP.Position, (HRP.Position+VectorToWallB)*300, Params) -- Params should have whitelist and have the walls in the whitelist

if RayResultA then
    MagnitudeA = (RayResultA.Position - HRP.Position).Magnitude -- Gets the Magnitude between Raycasted point (the wall) and the Player
end

if RayResultB then
    MagnitudeB = (RayResultB.Position - HRP.Position).Magnitude
end

if MagnitudeB < MagnitudeA then --If Magnitude B is lower as MagnitudeA, use MagnitudeA
   MagnitudeToUse = MagnitudeB

elseif MagnitudeA < MagnitudeB then --Else, Magnitude A ist the lowest
    MagnitudeToUse = MagnitudeA
end

I don’t have experience with anything of this type, but couldn’t you simply create multiple different walls, and determine the distance from each wall? Therefore not having as much of a difference.

I’d Raycast from each player and get the magnitude to the intersection rather than to the wall’s position.

You know which direction to raycast in if the wall is the entire pitch width, players are restricted to the pitch, and you know which end you’re checking against.

local direction = ( WallPos - PitchCenterPos ) * Vector3.new( 1, 0, 1 )

The multiplier removes any vertical component that may appear as a result of differences in height between the pitch centre and the wall centre.

Make the Ray length around the length of the pitch, and use a collision group just for those walls to increase efficiency and avoid other things being picked up by the Ray. You’ll need a collision group that is set only to collide with itself and not with any other group incl. Default, I’ve assumed the name Walls.

You can then raycast from each HRP in the newly found direction - multiply the direction vector by at least 2 to ensure full pitch coverage.

local params = RaycastParams.new()
params.CollisionGroup = 'Walls'
local raycastResult = workspace:Raycast( HRP.Position, direction * 2, params )

The result can then be used to get the distance for each player, and then you’d simply go with the smallest.

local distanceToWall = ( raycastResult.Position - HRP.Position ).Magnitude
5 Likes

I suggest you use Magnitude, but instead of using the Position of the wall;
Only use the Z or the X of the wall

(This is an easy way out, but it won’t work if the wall is rotated slightly, as we’re assuming the wall is either on the Z or X axes.)

The result:

  • Position 1: HumRP.Position
  • Position 2: Vector3.new(Wall.Position.X, HumRP.Position.Y, HumRP.Position.Z)
    or Vector3.new(HumRP.Position.Z, HumRP.Position.Y, Wall.Position.X)

Then use those 2 positions for the magnitude.

If this is just 1 wall you are using, then that makes it much easier. I’m assuming that’s the case and that the object is rotated exactly as a multiple of 90(0, 90, 180, 270, etc). If this is the case, then you can use the Pythagorean Theorem formula to calculate the magnitude:


We’re trying to calculate the blue side of the triangle, the pink dot is the position of the wall, the wall lines up with the x axis.

local function FindAngle(X, Z) -- Finds the angle of dotted line and orange line
	return math.atan2(X, Z) * 180 / math.pi
end
local PlayerPosition = Vector2.new(math.abs(Player.Position.X), math.abs(Player.Position.Z))
local XLength = math.abs(Wall.Position.X) - math.abs(Player.Position.X) -- Finds the length of dotted lines
local ZDiagonalLength = (PlayerPosition - Vector2.new(math.abs(Wall.Position.X), math.abs(Wall.Position.Z))).Magnitude -- Finds the length of orange line
local Length = ZDiagonalLength * math.sin(FindAngle(XLength, ZDiagonalLength)) -- Finds the angle of the blue line
print(Length)

Edit: @Alvin_Blox, I made a mistake in my code, did an extra unnecessary step the broke the code, now it works.


If you are curious, here are the resources I used:

  1. Given angle and hypotenuse

Apply the law of sines or trigonometry to find the right triangle side lengths:

  • a = c * sin(α) or a = c * cos(β)
  • b = c * sin(β) or b = c * cos(α)

JavaScript: Find the angle between two points · GitHub

var p1 = {
	x: 20,
	y: 20
};

var p2 = {
	x: 40,
	y: 40
};

// angle in radians
var angleRadians = Math.atan2(p2.y - p1.y, p2.x - p1.x);

// angle in degrees
var angleDeg = Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
1 Like

player can be rotated at any angle

3 Likes

The player can we rotated at any angle, the wall doesn’t.

2 Likes

No need to raycast to solve this problem - you can treat this as a problem of finding the shortest distance between a point (player position) and a plane (the wall). This means you can just use vector projection / the dot product. I made a diagram which should hopefully help:

diagram

offset • normal means the dot product of offset and normal
distance is what you are solving for: the distance from a player/point to the wall

7 Likes

I wouldn’t necessarily say that it’s giving you bad results, just that it’s giving you the euclidean distance from the centre points of each object.

For your case, since there’s only max 2 static walls orientated to world axis, you could take the worldspace position without orientation and check the delta along the axis of the pitch to the wall.i.e.

local a, b = 5, -15 -- wall.Position.x/z, player.Position.x/z
a, b = math.max(a, b), math.min(a, b)
local d = math.abs(a - b)

print(d) --> 20

The distance will (essentially) be the same regardless of player orientation since it’ll once again be calc’d from the centre point