Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

How do I find parts in a rotated part?

This works to find a part named "Hitbox" in item.Hitbox, but when item.Hitbox is rotated it doesnt work...

local item = game.ServerStorage.ItemStorage:FindFirstChild(itemname):Clone()
item:SetPrimaryPartCFrame(obby.NextConnectionPart.Value.CFrame)
for i,v in pairs (game.Workspace:FindPartsInRegion3(Region3.new(Vector3.new(item.Hitbox.Position.X - item.Hitbox.Size.X/2, item.Hitbox.Position.Y - item.Hitbox.Size.Y/2, item.Hitbox.Position.Z - item.Hitbox.Size.Z/2), Vector3.new(item.Hitbox.Position.X + item.Hitbox.Size.X/2, item.Hitbox.Position.Y + item.Hitbox.Size.Y/2, item.Hitbox.Position.Z + item.Hitbox.Size.Z/2)), nil, 1000)) do
	if v.Name == "Hitbox" and v ~= item.Hitbox then
		placementerror = true
	end
end

1 comment

1

I can answer this question but it's a bit mathy. Do you know the dot product?

1 answer

✓ Accepted answer

Okay, prepare for a bit of a math lesson here because that's what this is going to be :P

For one, a few things you'll need to know to do a basic rotated region 3:

Alright, once you've read up on those we can now start to answer your question regarding creating a simple rotated region 3 call.

The basic premise behind this method is to essentially get the centre of our region, treat is as the (0, 0, 0) point, make everything else relevant to it, and then finally use 6 infinite planes to create a box and then check if a point is inside it. ~~ Wow! That's a mouthful!

First, let's start by running over how we can tell if a point is "under" or "over" a plane. I put these terms in quotes because everything is relative, as the ender's game quote goes "the enemy's gate is down". For our purposes we define above the plane as any position in which you'd have to intersect the plane to draw a line from that point to the origin, and we define below the plane as any point you could draw a line between it and the origin without intersecting the plane.

To do this we'll start by defining our plane. Normally all we need to define a plane is simply the surface normal of the plane and any point on the plane. However, for our purposes we're instead going to define the plane with another property, the scalar we'd need to multiply the surface normal by to reach a point on the plane. Let's not worry about how we get that value quite yet and instead just assume we have it for the sake of argument. We're also going to define an origin for our plane. In order for this math to work all our values need to be relevant to (0, 0, 0). For our region 3 we're working relevant to an arbitrary point in space. If however we subtract everything that's relevant to our region 3 origin we now have vectors that are relevant to (0, 0, 0) that we can do the math on and then add back to our region 3 origin when we're done.

We can define our plane as such:

local plane = {};

function plane.new(origin, normal, scalar)
	local self = setmetatable({}, {__index = plane});
	
	self.origin = origin; -- cframe value, but it could be vector3
	self.normal = normal;
	self.scalar = scalar;
	
	return self;
end;

Now that we have that information we can do a simple check on any point to see if it's above or below the plane. We can do this by projecting the relative point onto the normal which will give us a scalar that essentially tells us how much the point we just projected is travelling in the same direction as the normal. We can then take this value and subtract the scalar value of the plane (which if you recall is how long the normal would need to be from the origin to reach a point on the plane).

function plane:isAbove(point)
	local h = ((point - self.origin.p):Dot(self.normal)) - self.scalar;
	return h;
end;

From this result we have 3 possible outcomes:

  1. if h > 0 then the point is above the plane.
  2. if h < 0 then the point is below the plane.
  3. if h = 0 then the point is on the plane.

If we use 6 infinite planes we can create a box shape (all planes relative to the box's centre) to represent our region 3. We know if our point lies "below" all 6 of those plane it's inside of the region!

All that's left is to create 6 planes out of a CFrame and a size. This is really is to do. Simply use the cframe as the centre of the box, use vector to world space to get the normals (rotation), and then use the size divided by two in the proper direction to get the plane's scalar. Last but not least run a loop to check the six planes and return a boolean or something like that.

local region = {};

function region.new(cframe, size)
	local self = setmetatable({}, {__index = region});

	self.planes = {
		plane.new(cframe, cframe:vectorToWorldSpace(Vector3.FromNormalId(Enum.NormalId.Top)), size.y/2);
		plane.new(cframe, cframe:vectorToWorldSpace(Vector3.FromNormalId(Enum.NormalId.Bottom)), size.y/2);
		plane.new(cframe, cframe:vectorToWorldSpace(Vector3.FromNormalId(Enum.NormalId.Left)), size.x/2);
		plane.new(cframe, cframe:vectorToWorldSpace(Vector3.FromNormalId(Enum.NormalId.Right)), size.x/2);
		plane.new(cframe, cframe:vectorToWorldSpace(Vector3.FromNormalId(Enum.NormalId.Front)), size.z/2);
		plane.new(cframe, cframe:vectorToWorldSpace(Vector3.FromNormalId(Enum.NormalId.Back)), size.z/2);
	};
	
	return self;
end;

function region:CastPart(part)
	for _, plane in ipairs(self.planes) do
		print(plane.normal)
		if plane:isAbove(part.CFrame.p) >= 0 then
			return false;
		end;
	end;
	return true;
end;

That's pretty much it! Keep in mind in it's current form this will only detect the centre of the part.

This discussion is preserved as it was. New answers, comments, and votes are closed.