Region - Promise-Based Region API

region

A simple promise-based roblox-ts API that detects when parts enter defined regions.
GitHub
NPM

But Why?

This package was adapted from the codebase of a few games I’d worked on. I had originally created this out of a need to heavily optimize region-based events across the client and server. Although it never got to realize its full potential, the improvements over previous methods was significant enough that I decided to spin this off into a separate API.

Most region APIs rely on events, which generally involve a lot of busy waiting in the background. This API relies on promises- and only runs when you need it to. Additionally, promises make it easy to deal with multiple method calls, even without provided wrappers such as RegionUnion or RegionNegation. All that combined with a highly efficient codebase makes for code that is both performant and extensible.

Usage

Region objects all inherit the same abstract class, making it easy to negate and union them.

Region

Region is an abstract class, but it’s inherited by all classes in the API.

Region.enteredRegion


enteredRegion(part: BasePart, timeout?: number, step?: () => Promise<unknown>): Promise<void>

Returns a promise that resolves when the provided part enters the region.

step is an optional argument (that normally defaults to task.wait(0.1) for most Region classes) which can be used to provide a function that yields for a certain amount of time. This can be useful for tying checks to a specific event.

Region.leftRegion


leftRegion(part: BasePart, timeout?: number, step?: () => Promise<unknown>): Promise<void>

Returns a promise that resolves when the provided part leaves the region.

step is an optional argument (that normally defaults to task.wait(0.1) for most Region classes) which can be used to provide a function that yields for a certain amount of time. This can be useful for tying checks to a specific event.

Region.isInRegion


isInRegion(point: Vector3): boolean

Returns true if the point is in the Region, false if not.

BasePartRegion

The most basic of these is the BasePartRegion which can either be created using the properties or an existing BasePart.


const regionPart = new Instance("Part");

regionPart.Position = new Vector3(0, 0, 0);

regionPart.Size = new Vector3(10, 20, 10);

regionPart.Shape = Enum.PartType.Cylinder;

const region = Region.fromPart(regionPart)

Alternatively, BasePartRegions can be created using the position CFrame, size, and shape of the desired region.


const region = new Region(new CFrame(0, 0, 0), new Vector3(10, 10, 10), Enum.PartType.Cylinder);

constructor


constructor(

location: CFrame,

size: Vector3,

shape: Enum.PartType,

)

BasePartRegion.fromPart


fromPart(part: BasePart): BasePartRegion

An alternative to the constructor, this allows for the creation of BasePartRegions using BaseParts.

RegionUnion

RegionUnions are a great way to combine regions, though you can technically do it yourself with a little Promise.race. The main reason for using RegionUnions though, is that they’re compible with both other RegionUnions and RegionNegations.

RegionUnion has all the same methods as BasePartRegion, it just combines the functionality of multiple BasePartRegions.


const regionPart0 = new Instance("Part");

regionPart0.Position = new Vector3(0, 0, 0);

regionPart0.Size = new Vector3(10, 20, 10);

regionPart0.Color = new Color3(0, 1, 0);

regionPart0.Shape = Enum.PartType.Cylinder;

const regionPart1 = new Instance("Part");

regionPart1.Position = new Vector3(0, 0, 0);

regionPart1.Size = new Vector3(10, 10, 10);

regionPart1.Color = new Color3(1, 0, 0);

regionPart1.Shape = Enum.PartType.Ball;

const region = new RegionUnion([BasePartRegion.fromPart(regionPart0), BasePartRegion.fromPart(regionPart1)]);

constructor


constructor(regions: Region[])

RegionUnion.getRegions


getRegions(point: Vector3): Region[]

Returns an array of regions (if any) a point is in.

RegionNegation

Like RegionUnion, RegionNegation takes multiple regions and combines them into a single region with the same methods as the BasePartRegion. The main difference is that the constructor takes a Region (can be any of the classes) and another Region to negate from that. Basically just like unions in Roblox, this allows for the formation of complex shapes.


const regionPart = new Instance("Part");

regionPart.Position = new Vector3(0, 0, 0);

regionPart.Size = new Vector3(10, 20, 10);

regionPart.Shape = Enum.PartType.Cylinder;

const negationPart = new Instance("Part");

negationPart.Position = new Vector3(0, 0, 0);

negationPart.Size = new Vector3(10, 10, 10);

negationPart.Shape = Enum.PartType.Ball;

const region = new RegionNegation(BasePartRegion.fromPart(regionPart), BasePartRegion.fromPart(negationPart));

constructor


constructor(region: Region, negation: Region)

Example

This is a short script that prints when a part enters or exits a region:


import { BasePartRegion, RegionNegation } from "@rbxts/region";

import { Workspace } from "@rbxts/services";

const regionPart = new Instance("Part");

regionPart.Position = new Vector3(0, 0, 0);

regionPart.Size = new Vector3(10, 20, 10);

regionPart.Shape = Enum.PartType.Cylinder;

regionPart.Anchored = true;

regionPart.Transparency = 0.5;

regionPart.Color = new Color3(0, 1, 0);

regionPart.Parent = Workspace;

const negationPart = new Instance("Part");

negationPart.Position = new Vector3(0, 0, 0);

negationPart.Size = new Vector3(10, 10, 10);

negationPart.Shape = Enum.PartType.Ball;

negationPart.Anchored = true;

negationPart.Transparency = 0.5;

negationPart.Color = new Color3(1, 0, 0);

negationPart.Parent = Workspace;

const region = new RegionNegation(BasePartRegion.fromPart(regionPart), BasePartRegion.fromPart(negationPart));

const part = new Instance("Part");

part.Size = new Vector3(1, 1, 1);

part.Position = new Vector3(0, 0, 0);

part.Anchored = true;

part.Shape = Enum.PartType.Ball;

part.Parent = Workspace;

function onEnter() {

print("entered!");

region.leftRegion(part).then(onLeave);

}

function onLeave() {

print("left!");

region.enteredRegion(part).then(onEnter);

}

region.enteredRegion(part).then(onEnter);

8 Likes

Amazing!!! This will definitely be useful for some of my projects! Thank you :smiley: