Rotated Region 3 Module

Recently I took another crack at an old module I wrote some time back. The goal of this module is to provide users with a way to create Rotatable Region 3’s which is useful for too many reasons to list.

Even though this module (older versions) has been floating around for some time I figured I’d post on the devforum with this latest, and hopefully last, release


So what does this module allow you to do?

Basically, you the dev provide a CFrame, Size, and shape and the module allows you to do all the fun stuff you’d normally do with Region3’s for finding parts, but allowing for rotation.

Now something I just alluded to above is that you get to pick the shape! That means with this module you are not limited to boxes. You can create Rotated Region 3’s with the following shapes:

  • Blocks
  • Wedges
  • CornerWedges
  • Cylinders
  • Balls

Here are some images of those different shapes being used for those interested:

Images





Aside from the standard FindPartsInRegion methods you’re familiar with the RotatedRegion3 objects also pack methods for checking if individual points and parts collide with the region0.

If you’re interested the documentation you can find that here, but note it’s also included in the module itself:

--[[
API:

Constructors:
	RotatedRegion3.new(CFrame cframe, Vector3 size)
		> Creates a region from a cframe which acts as the center of the region and size which extends to 
		> the corners like a block part.
	RotatedRegion3.Block(CFrame cframe, Vector3 size)
		> This is the exact same as the region.new constructor, but has a different name.
	RotatedRegion3.Wedge(CFrame cframe, Vector3 size)
		> Creates a region from a cframe which acts as the center of the region and size which extends to 
		> the corners like a wedge part.
	RotatedRegion3.CornerWedge(CFrame cframe, Vector3 size)
		> Creates a region from a cframe which acts as the center of the region and size which extends to 
		> the corners like a cornerWedge part.
	RotatedRegion3.Cylinder(CFrame cframe, Vector3 size)
		> Creates a region from a cframe which acts as the center of the region and size which extends to 
		> the corners like a cylinder part.
	RotatedRegion3.Ball(CFrame cframe, Vector3 size)
		> Creates a region from a cframe which acts as the center of the region and size which extends to 
		> the corners like a ball part.
	RotatedRegion3.FromPart(part)
		> Creates a region from a part in the game. It can be used on any base part, but the region 
		> will treat unknown shapes (meshes, unions, etc) as block shapes.

Methods:
	RotatedRegion3:CastPoint(Vector3 point)
		> returns true or false if the point is within the RotatedRegion3 object
	RotatedRegion3:CastPart(BasePart part)
		> returns true or false if the part is withing the RotatedRegion3 object
	RotatedRegion3:FindPartsInRegion3(Instance ignore, Integer maxParts)
		> returns array of parts in the RotatedRegion3 object
		> will return a maximum number of parts in array [maxParts] the default is 20
		> parts that either are descendants of or actually are the [ignore] instance will be ignored
	RotatedRegion3:FindPartsInRegion3WithIgnoreList(Instance Array ignore, Integer maxParts)
		> returns array of parts in the RotatedRegion3 object
		> will return a maximum number of parts in array [maxParts] the default is 20
		> parts that either are descendants of the [ignore array] or actually are the [ignore array] instances will be ignored
	RotatedRegion3:FindPartsInRegion3WithWhiteList(Instance Array whiteList, Integer maxParts)
		> returns array of parts in the RotatedRegion3 object
		> will return a maximum number of parts in array [maxParts] the default is 20
		> parts that either are descendants of the [whiteList array] or actually are the [whiteList array] instances are all that will be checked
	RotatedRegion3:Cast(Instance or Instance Array ignore, Integer maxParts)
		> Same as the `:FindPartsInRegion3WithIgnoreList` method, but will check if the ignore argument is an array or single instance

Properties:
	RotatedRegion3.CFrame
		> cframe that represents the center of the region
	RotatedRegion3.Size
		> vector3 that represents the size of the region
	RotatedRegion3.Shape
		> string that represents the shape type of the RotatedRegion3 object
	RotatedRegion3.Set
		> array of vector3 that are passed to the support function
	RotatedRegion3.Support
		> function that is used for support in the GJK algorithm
	RotatedRegion3.Centroid
		> vector3 that represents the center of the set, again used for the GJK algorithm
	RotatedRegion3.AlignedRegion3
		> standard region3 that represents the world bounding box of the RotatedRegion3 object
--]]

You can find the module here:

Hopefully there aren’t any bugs, but if there are please let me know so I can fix and update them :grin:

Enjoy!

139 Likes
Best approach to making Press E shops
What are good methods for hitbox detection?
Alternative for Touched event?
Combat methods (Damaging humanoid)
Writing a Region3 yourself?
Best Way to Make Hitboxes
.Touched Event so laggy when it is inside while loop?
Rotated Region3 Module issue
How do you detect parts around another part with a certain radius?
Region3 CFrame Help
workspace:FindPartsInRegion3 is ignoring parts
Best method for hitbox detection?
World Point Proximity Check
Making rotated Region3's
ZonePlus v2 Template
ZonePlus v2 | Construct dynamic zones and effectively determine players and parts within their boundaries
Zone Capture System
Region3 is not working
How would I make a thick beam that damages players?
Is there any way I can keep a part in workspace on the server from replicating to the client?
Best way to check magnitude from surface?
Best way to check magnitude from surface?
Functions in .Touched event firing 30 seconds later
Weapons only doing damage after reaching a keyframe
Explosion radius in a nuke
Perspective object dragging like Superliminal
Detecting Certain Parts in a Sphere
Detect if a player is inside a certain area
Shop Loop Efficiency?
Any good rotated region3 modules?
Detecting if a rectangle is fully in a 2D plain
MakeJoints with WeldConstraint
Is there such a thing as good square shaped hitbox detection for combat?
Region3 refuses to work
Better way of getting instances in a circle?
Can i make damage map?
Calculating Total Amount of Players in a Certain Area
Help making a tool giving zone system
How would I have this sound play once when you are in range, then stop when you are out of range?
How can I find multiple closest parts to a player?
How to detect ray hit at the end of the ray?
Best way to check if a player is in this zone?
Is there any way to make region3 size = Part size?
Is there a way to get all of the parts around a raycast with a radius? (region3 around ray?)
Dealing with .Touched events and their inevitable delay buildup in my game
Explosion-based hitboxes vs region3 hitboxes
Best way to detect a player in an hexagon shaped "storm"
Understanding Region3
ZonePlus v2 | Construct dynamic zones and effectively determine players and parts within their boundaries
Is there a way to give a ray width?
Table item added/removed
EZ Fabrik IK [Deprecated] - Inverse Kinematics Intended for any Motor6d Rig
Need help with collision detection for a model placement system
Help with Detecting Brick Collision
Creating a script for melee fighting
Nearest position on the surface of a collection of parts
How to do hit detection for a fast and large projectile
Region3 Oriented Weirdly
Help With Region3 Hitbox
Region3 Rotation
How would I make it so if you enter a room it changes your camera?
Best way to Handle Area of Effect?
Help with hitboxes
Wall crash system help
A question regarding raycasting hitbox by swordphin123
Decent Hit Detection Methods
How to rotate region3
Explosion effect help
GetTouchingParts() is too laggy, but I can't find an alternative
Best method for hit boxs?

This looks really great and I could see how this could be useful for many people! I plan on using it soon as well…

But it does raise an eyebrow, how is this in performance?

3 Likes

Great question!

The main problem in actually getting some kind of bench mark here is that there’s nothing to compare it to (except other modules that do the same thing) since we don’t have this functionality built in.

I suppose if you’re truly looking for best performance you’d be best using that :GetTouchingParts() trick, but that actually requires physical parts in the workspace and that may or may not be a viable option compared to this.

Is this something you can run every frame? In my experience, yes, no problem! Would I actually do that? No. However, I wouldn’t do that with a normal region3 either so I guess it’s all dependent on how to plan to use it.

8 Likes

Looks extremely useful and well made, as per your usual!

I’m unable to check the source code right now, but I’m curious about something.
If you create a sphere region using a part, does it bother checking rotation? Since rotation wouldn’t affect it, does the algorithm skip it and assume orientation of 0,0,0?

3 Likes

Yes and no…

The collision detection is done with the GJK algorithm. In essence it requires a function that takes an aribitary direction as an argument and spits out the furthest point on that shape in that direction.

So in the case of a ball whether it’s rotated or not changes basically nothing as far as GJK is concerned.

That said, technically you can input an arbitrary size into the ball constructor and that would give you an ellipsoid where rotation would matter.

5 Likes

It’s slower than the original GJK module as well as the GetTouchingParts module.

RotatedRegion3.rbxl (33.2 KB)

Probably should mention I ran this on the new VM, and my specs are a 7600k and a 1080ti.

Here’s a more raw benchmark, which gave me the results below.

-- Speedtest.lua

local GJK = require(workspace.RotatedRegion3GJK:Clone())
local GetTouchingParts = require(workspace.RotatedRegion3Touching:Clone())
local GJKV1 = require(workspace.RotatedRegion3GJK1:Clone())

local Functions = {
	function() -- GJK
		local Region = GJK.FromPart(workspace.Region)
		return Region:Cast(workspace.Region)
	end;

	function() -- GetTouchingParts
		local Region = GetTouchingParts.FromPart(workspace.Region)
		return Region:Cast(workspace.Region)
	end;

	function() -- GJKV1
		local Region = GJKV1.FromPart(workspace.Region)
		return Region:Cast(workspace.Region)
	end;
}
local Times = {}
local tick = tick
for a = 1, #Functions do
	local Function = Functions[a]
	local StartTime = tick()
	for a = 1, 1e4 do
		Function()
	end
	local Time = tick() - StartTime
	print(Time)
	Times[a] = Time
end

local ShortestTime = math.min(table.unpack(Times))
local ShortestFunc
for a = 1, #Times do
	if Times[a] == ShortestTime then
		ShortestFunc = a
	end
end
print("Shortest Function:", ShortestFunc)
print("Shortest Time is", ("%.2f%%"):format(100 - (100 * (ShortestTime / math.max(unpack(Times))))), "faster than the longest time")
2.0143620967865
1.2489848136902
1.560450553894
Shortest Function: 2
Shortest Time is 38.00% faster than the longest time
2 Likes

So running the exact same test I got totally different results?

Capture

This does seem more accurate though. GetTouchingParts should be immensely more performant compared to GJK.

In addition, although V1 and V2 share some differences in structure which is prob what leads to V2 being slower (I still recommend it b/c V1 freezes sometimes) they do use almost exactly the same code so the gap in your image seems very unlikely.

4 Likes

This is great! I loved :heart: your old version of RotatedRegion3 and it’s been incredibly useful in many projects. Any time that I want to do collision detection I reach for this tool!

My favorite use case was a C&C Renegade-esque RTS/FPS project I worked on a while back for another person. Players could place buildings like in any other RTS, but they were big enough that players could walk into them! I needed to disallow placing buildings such that they overlap other buildings or the terrain (made of parts), so some kind of collision detection was needed.

I wanted to make it obvious to the player where they could or could not place any given building, so I decided to have a “ghost” version of a building showing where it will be placed, and to color the “ghost” red or green to show if it’s overlapping something:

image image

This means running the collision detection every frame, between several parts. To make this performant with many buildings and terrain consisting of 1000s of parts I used octrees (or quadtrees, don’t remember) to filter out things that were too far away to collide anyway. I also enclosed every building in a single part describing it’s bounding box (not axis aligned), to further filter out buildings that weren’t even close to touching, despite being physically close. Finally, the actual collision detection only needed to be run between 1 to 5 parts per building in the vicinity (5 for the most complicated buildings).

The tower in the pictures has a pretty big overhang, and I wanted to allow players to place small buildings beneath towers, as well as on top of or inside certain other buildings. Instead of just enclosing each building in a single volume that excludes other buildings, the “collision volume” can consist of any number of parts for fairly fine control.

Using these optimizations, we got the game to run with no drop in frame rate on my laptop (Intel HD Graphics 4000 and i5-3320M CPU @ 2.60 GHz). IIRC we didn’t do any actual benchmarks other than “yep, it runs at 60 fps”. For reference, my laptop can’t run Phantom Forces without occasionally dipping below 30 fps.

Anyway, I just wrote this to show an example of how this module can be super useful, and to show that it’s fine to do a few collision checks every frame. In a different game I’m just doing a super simple check to see if the player’s feet are touching the ground, so it’s also useful in simpler cases.

5 Likes

Hey, the old Region module supported calculating intersection points. Is there any way to get those out of this module? I had a look over the source code (the module itself, and GJK too) and I don’t see any of that functionality built-in. Could you consider adding something like the old intersectionPoints method?

4 Likes

I want to know if characters are in a region.
Would this be the method? It’s a dynamic shape of rooms and anglular hallways.

It would mean

Running this every frame for characters.
60FPS, 15 boxes rotated and 30 wedges , how many parts, thus Players (scanning for their HRP) could I manage?

that’s a horrible idea, I suggest you find a better alternative. I’m guessing you’re doing this on the server. Just check every wait() if their HRP position is within the locations you mentioned.

local function checkBounds(hrp,boundSize,boundPos)
	local hrpPos = hrp.Position
	local x,y,z = hrpPos.X,hrpPos.Y,hrpPos.Z
	return (x < boundPos.X + (boundSize.X/2)) and (x > boundPos.X - (boundSize.X/2)) and (y < boundPos.Y + (boundSize.Y/2)) and (y > boundPos.Y - (boundSize.Y/2)) and (z < boundPos.Z + (boundSize.Z/2)) and (z > boundPos.Z - (boundSize.Z/2))	
end

@EgoMoose

Hey, I was super excited to try your module as I just encountered an issue with my collision detection system, and I’ve pinpointed this to be due to the fact that when my models rotate 90 degrees, the Region3 I’m making is still centered in the model, and doesn’t alter it’s bounding box with the model’s actual rotation.

However, it appears I got an internal error with the module (assuming I set it up correctly.)

Any ideas? :frowning:

Right you are!

A month ago or so I pushed a change removing something I thought I didn’t need. Turns out it was necessary and I now remeber why (had to do with the different shapes)!

I’ve gone and fixed that up. Sorry for the difficulty!

1 Like

woudlent it be easyer to just have some points with magnitude and simple logic
so like say, has to be within magnitude of this point and this point, and not in this point.

I don’t know if I’m setting this up incorrectly, but I just tried again and I got the same error at line 88, attempt to index a nil value. :confused:

Did you update the module?

Line 88 doesn’t have the same code that it used to.

1 Like

This is actually bizarre, even after deleting and retaking the model in your OP, the line of code at 88 still looks the same, indexing set.

:confused:

It is bizzare. Line 88 has nothing to do with indexing set anymore. It’s a completely different function. I reinserted from the OP again just to make sure:

Line 88 used to be where line 101 is currently. I’m just as confused as you are. Can you open the module and confirm the existence of the get corners function?

Bingo. Turns out I needed to restart Studio or open up a new place for it to update marketplace assets apparently.

May or may not be an engine bug, but it’s the updated version now.

2 Likes

Yeah, I’ve been having that same problem of updating a model, inserting it again, and studio inserting the old version. Studio will only insert the new version in a new studio window.

3 Likes