Skip to content

Physics and spatial queries

Nova uses the classic PGS solver, with modern solver-selection and era rules layered on top. Workspace.Gravity, FallenPartsDestroyHeight, part properties, joints, and the legacy BodyMover family work.

What is present

  • BasePart physics properties: Anchored, CanCollide, Massless, Velocity, RotVelocity, CustomPhysicalProperties, CollisionGroup, CollisionGroupId.
  • Joints: Weld, Motor6D, WeldConstraint, Snap, Rotate, Glue, ManualWeld.
  • Constraints: HingeConstraint, BallSocketConstraint, SpringConstraint, RopeConstraint, AlignPosition, AlignOrientation, LinearVelocity, AngularVelocity, VectorForce, and the rest of the current class list.
  • Body movers: BodyPosition, BodyGyro, BodyVelocity, BodyForce, BodyThrust.
  • Terrain, Region3, Ray, PhysicalProperties, collision groups on BasePart.

What is missing

  • WorldRoot and the modern spatial-query family: Raycast, Blockcast, Spherecast, Shapecast, GetPartBoundsInBox, GetPartsInPart.
  • RaycastParams, RaycastResult, OverlapParams.
  • PrismaticConstraint, CylindricalConstraint, UniversalConstraint, PlaneConstraint.
  • The modern PhysicsService collision-group methods. BasePart.CollisionGroup and CollisionGroupId exist, but CreateCollisionGroup and friends are not exposed.

Raycasting

The modern API:

-- Roblox
local params = RaycastParams.new()
params.FilterDescendantsInstances = { character }
params.FilterType = Enum.RaycastFilterType.Exclude

local result = workspace:Raycast(origin, direction, params)
if result then
    print(result.Instance, result.Position, result.Normal)
end

The Nova equivalent:

local function raycast(origin: Vector3, direction: Vector3, ignore: { Instance })
    local ray = Ray.new(origin, direction)

    -- Returns: part, position, normal, material
    local part, position, normal, material =
        workspace:FindPartOnRayWithIgnoreList(ray, ignore, false, true)

    if part then
        return {
            Instance = part,
            Position = position,
            Normal = normal,
            Material = material,
        }
    end
end

local hit = raycast(
    camera.CFrame.Position,
    camera.CFrame.LookVector * 500,
    { character }
)

if hit then
    print(hit.Instance:GetFullName(), hit.Position)
end

FindPartOnRay is the simpler version without an ignore list:

local part, position, normal, material = workspace:FindPartOnRay(ray)

The terrainCellsAreCubes and ignoreWater arguments default to false. Pass true for ignoreWater if you do not want water hits.

Region queries

FindPartsInRegion3WithIgnoreList is the replacement for GetPartBoundsInBox/GetPartsInPart:

local function partsInBox(center: Vector3, size: Vector3, ignore: { Instance })
    local half = size / 2
    local region = Region3.new(center - half, center + half)

    -- The last argument caps the number of returned parts.
    return workspace:FindPartsInRegion3WithIgnoreList(region, ignore, 100)
end

To test whether a region is empty, use IsRegion3EmptyWithIgnoreList. For terrain-specific queries, the legacy methods still use Region3.

Collision groups

Parts have a CollisionGroup name and a numeric CollisionGroupId. The modern management API is not exposed, so treat collision groups as a property set by the place/Studio rather than something a game script configures at runtime.

Solver behavior

Nova uses PGS. The classic Workspace.PGSPhysicsSolverEnabled property was removed from Roblox in 2019; Nova follows the modern behavior and auto-enables PGS for era-dated saves. Workspace:PGSIsEnabled() is available for scripts that need to inspect it.

Do not port assumptions that depend on a specific modern solver iteration count, sleep behavior, or network-ownership heuristic. Re-test vehicles, ragdolls, and stacked parts in Nova; expect small differences.

Common porting problems

Symptom Likely cause Fix
Parts pass through each other CanCollide off, or both parts anchored, or collision group mismatch Check properties and collision groups.
A ray never hits Direction is zero, or the origin is inside the target Normalize the direction and offset the origin.
Ray hits the character Missing ignore list Pass the character (and its descendants) to FindPartOnRayWithIgnoreList.
Vehicle behaves differently Solver/constraint differences Re-tune forces; prefer constraints over BodyMovers.
Region query returns nothing Region3 bounds are inverted Build from center - half and center + half.

Next