How would I get a random position on a surface on a part with offset from the sides?

You can write your topic however you want, but you need to answer these questions:

I need to get a position that’s on a surface of a part with a distance from the sides of the part for a system I’m working on which adds random spikes to the surface of a part. The part that needs this done to it is created via scripts and cannot have any modifications made to make it easier to get a position.

image
Yellow: The area I would want a random CFrame of that’s positioned on the surface and points away from the surface.
Red: Area that I do not want a random position from.
Attachments: The data I want from this.

I’ve tried doing this with a bunch of if statements for individual logic for each side. Is there some CFrame/Vector3 math I could do to avoid needing to make a ton of if statements for individual logic for each surface?

1 Like

You could always simply weld and duplicate small parts onto that actual part. Then you’d just make the little parts massless and switch CanCollide to false for them. At that point you’d just use math.random, pick a small part, and get the CFrame position of it.

That’s too hacky of a solution for what I need to accomplish.

Hmm okay, well it’s something to fall back on if it’s a struggle to get a mathematical solution at least.

So you want a script that will pick any point on any of the sides where it’s yellow? So not just one side at a time, correct?

I need to know the math to get a position on the surface of any side of a part in the space that is yellow, not in the space that is red. Preferably without a ton of if statements for each individual side if there’s a way to avoid that.

Ah okay. So is the red area a seperate part from the yellow area? And, do you know the dimensions of the part, as well as the red area/yellow seperately? It’d make it hard to do something like this without a clue as to how to recognize where this red area and yellow area lies on the part with numbers.

There is only one part, the yellow and red part is just to show the area of where I want a part from. The red represents an offset to the sides of a surface.

Do you know what that offset is in numbers? How would someone put this into a mathematical equation and without knowing what the dimensions of the yellow and red sections are - or the part for that matter?

Offset would be a Vector2, in the example the offset would be Vector2.new(1,1). Part size is known and is a variable.

Well I’m not too sure how to approach this, so I’ll give what I think could work as a starting point at the very least through a 2D example of a similar problem.

Without Borders

So imagine you had a scenario where you wanted to know any random point in 2D space on a 2D rectangle with the dimensions (5, 7), with the Vector2 point in space being (160, 50) - assuming that point is the midpoint of the rectangle, as well as the rectangle is vertical. So here’s how I’d do it:

  1. Get the furthest left and right X coordinate points of the rectangle in 2D space. For the left side, you’d want to take the rectangle’s X coordinate in space (160) - the X length of the triangle (7) - the midpoint of the rectangle (3.5), expressed as 160 - (7 - 3.5). From this, we’d get the answer of 156.5, which is the furthest left our random points should be able to go. Then we’d just do the same thing for the right side, but instead of subtracting 160 by 7 - 3.5 like above, we’d do 160 + (7 - 3.5).
  2. Also get the furthest bottom and top Y coordinate points of the rectangle in 2D space. This is pretty much the same as step 1, but for the Y coordinate. For the lowest point do 50 - (5 - 2.5), and for the top do 50 + (5 - 2.5).
  3. Now, finally, to get a random point on the 2D rectangle do this:
local Xmin = 156.5
local Xmax = 163.5
local Ymin = 47.5
local Ymax = 52.5

local randomPointX = math.random(Xmin, Xmax) --> Random number between 156.5 and 163.5
local randomPointY = math.random(Ymin, Ymax) --> Random number between 52.5 and 52.5

randomPointX and randomPointY now represent your new randomly-selected point. And I didn’t put any metric denotations in this, just to make it more general.

With Borders

If you did the same scenario as the above, but with borders on all sides that are 1 units thick, here’s the solution (I believe).

  1. To calculate the Xmin and Xmax values now you’d have to take into account the 1 unit of the border. So for Xmin, instead of doing the earlier equation, you’d now do (160 - (7 - 3.5)) + 1, and for Xmax do (160 + (7 - 3.5)) - 1.
  2. And following in the lead of the revised step 1, for Ymin you’d do (50 - (5 - 2.5)) + 1, and for Xmax do (50 + (5 - 2.5)) - 1
  3. Then the final result for finding a random point would be:
local Xmin = 157.5
local Xmax = 162.5
local Ymin = 48.5
local Ymax = 51.5

local randomPointX = math.random(Xmin, Xmax) --> Random number between 157.5 and 162.5
local randomPointX = math.random(Ymin, Ymax) --> Random number between 48.5 and 51.5

So with borders, you’re just adding or subtracting the thickness of the border to the original solutions for Xmin, Xmax, Ymin, and Ymax.

Let me know if there’s any errors in the above, or any questions. And sorry, I know this isn’t a fix to your problem, but I hope it helps with the logic you can use when incorporating 3D vectors and the offsets you set up.

Something like this should work:

local function RandomCFOnSurface(part,Surface,Padding)
	local Size = part.size * .5
	local n = Vector3.FromNormalId(Surface)
	local Cf = part.CFrame
	
	local Point = Vector3.new(
		n.x==0 and gen:NextInteger(-Size.x, Size.x) or 0,
		n.y==0 and gen:NextInteger(-Size.y, Size.y) or 0,
		n.z==0 and gen:NextInteger(-Size.z, Size.z) or 0
	)
	
	local normal = (Cf * n - Cf.Position) 
	local Position = Cf * ((Point * Padding) + Size * n) 
	
	return CFrame.lookAt(Position, Position + normal)
end

Example:

local gen = Random.new()
...
local Part = game.Workspace.Part
local CF = RandomCFOnSurface(Part, Enum.NormalId.Top, .7)

As a brief explanation, in the function above Point represents a random point on a surface with one axis nullified (the axis that makes it “3d”), so that we can eventually add its non-random min/max axis size (Size * n) . When we add (Point + Size * n) together we get the full localized vector representing our random position. We then can multiply this position by the part’s CFrame to bring it into world space, forming our final position . After that, all that is needed is to create a cframe using our calcutled position that is facing away from the normal of the given surface.(CFrame.lookAt(Position, Position + normal)).

4 Likes