BasePart:GetLastContact()

As a Roblox developer, it is currently too hard to accurately access detailed collision contact data.
This lack of precise collision information limits my ability to create advanced physics interactions and realistic game behaviors that rely on exact impact details.

If this issue is addressed, it would improve my development experience because I could implement more immersive and dynamic effects—such as realistic vehicle destruction, precise sound localization, and accurate impact visuals—without resorting to workarounds that compromise performance or fidelity.

Proposed Function API

-- Returns a table containing detailed contact data from the most recent collision.
-- Structure: { Part: BasePart, Position: Vector3, Impulse: Vector3 }
local contactData = somePart:GetLastContact()
  • Part: The other BasePart involved in the collision.
  • Position: The exact Vector3 position of the contact point.
  • Impulse: The Vector3 value representing the impact force at the contact.

Note: This function should update and be usable immediately before the Touched event fires, ensuring that it reflects the collision data that triggered the event.

Rationale for a Function Instead of Additional Touched Parameters

Directly integrating extra parameters into the Touched event could introduce performance overhead, although the physics engine already (probably) calculates this collision data. By implementing GetLastContact() as an opt-in function, the extra data is only retrieved when needed, ensuring that the default Touched event remains lightweight and reliable.

Benefits & Use Cases

  • Realistic Car Destruction Physics:
    Developers can access precise impact data to determine which parts of a vehicle should disconnect based on collision forces and part durability. This enables dynamic, believable destruction where debris and vehicle components behave in a realistic manner during crashes or explosions.

  • Accurate Sound Localization:
    With the exact collision contact point available, sound effects can originate from the precise location of impact rather than a part’s center point. This precision improves immersion by aligning audio cues with the visual impact, which is especially useful in action or racing games.

  • Precise Visual Impact Effects:
    The ability to pinpoint where a collision occurs allows for more accurate placement of visual effects—such as particles or damage animations. This can enhance interactive environmental effects where gameplay elements (like hit markers or localized damage responses) depend on the exact collision point.

By combining the benefits and use cases, it’s clear that an opt-in function like BasePart:GetLastContact() would not only provide developers with essential collision data but also open up new possibilities for creating richer, more interactive experiences in Roblox games.

22 Likes

Wouldn’t you be able to achieve something similar by just indexing this info from a BasePart.Touched connection? Maybe I’m missing something here.

8 Likes

What’s stopping you from using raycasts here? All of your use cases can be accomplished already using them.

1 Like

Yes, this can be done inaccurately with Raycast and slightly less inaccurately with Shapecast. That’s what I have always done, but it requires extra computation for something that the physics engine has already calculated, and you don’t even get the impact force.

4 Likes

BasePart.Touched only provides the hit BasePart and nothing else.

3 Likes

They should also add the ability to hook into engine functions, like add a new global function called “Hooks” with some methods that allow scripts to run a custom function when this instance’s method is called, there can also be a way to override methods but care should be taken with it.
This would be great since it’d allow developers to further customize the way Roblox works, also because hell even Garry’s Mod allows some way to hook into it’s engine functions.

Like this idea would be better since you don’t need to run for loops just to get parts in the map

3 Likes

I understand, but you can grab all this information on a touched connection and then store it for later as a LastContact dictionary or whatever you’d like:

local contactData = {}

Part.Touched:Connect(function(touching)
	local contactData = {
		["Part"] = touching,
		--if you want a more accurate representation use raycasting to get the 3d space of overlap
		["Position"] = Part.CFrame:ToObjectSpace(touching.CFrame),
		--this only accounts for initial velocity at time of impact, because that is when Part.Touched is called
		["Impulse"] = Part.AssemblyMass*Part.AssemblyLinearVelocity + touching.AssemblyMass*touching.AssemblyLinearVelocity
	}
end)
2 Likes

This addition sounds like extra memory usage

3 Likes

At first glance, it looks like this would work. It’s more complicated than this though. What if the touching part is rotating quickly, but has no positional velocity? Your impulse calculation would be inaccurate. The contact position would also be inaccurate, I’m not sure what you were trying to do…

Any addition to the engine increases memory usage. You add anything, you add memory.

For very simple (and predictable) collisions, yes, you can use raycasts. For more complicated objects like Meshes with a lot of holes, this wouldn’t work out. The physics engine does its own raycasts for touches, and exposing the intersections of those would be exactly what the OP is looking for!

2 Likes

You can factor in BasePart:AssemblyAngularVelocity to account for that; I chose not to to keep the example script simple. In terms of position, this is the touching part’s CFrame relative to the part that is being touched. If you want the overlapping space you need to do a bit more work. Here’s a script I wrote for getting and displaying the overlap between two BaseParts:

local Part = workspace.Part
local Part2 = workspace.Part2

local function dictSwap(var1, var2)
	local temp = var1
	var1 = var2
	var2 = temp
end

local function getOverlapResult(Part1, Part2)
	--ur gonna have to do more math if the parts are rotated
	--grab min and max x,y,z values for both parts
	--there will also likely be some problems with floating point precision so round if you so desire
	local MinX1, MinY1, MinZ1 = Part1.Position.X - Part1.Size.X/2, Part1.Position.Y - Part1.Size.Y/2, Part1.Position.Z - Part1.Size.Z/2
	local MaxX1, MaxY1, MaxZ1 = Part1.Position.X + Part1.Size.X/2, Part1.Position.Y + Part1.Size.Y/2, Part1.Position.Z + Part1.Size.Z/2
	local MinX2, MinY2, MinZ2 = Part2.Position.X - Part2.Size.X/2, Part2.Position.Y - Part2.Size.Y/2, Part2.Position.Z - Part2.Size.Z/2
	local MaxX2, MaxY2, MaxZ2 = Part2.Position.X + Part2.Size.X/2, Part2.Position.Y + Part2.Size.Y/2, Part2.Position.Z + Part2.Size.Z/2
	
	local OverlapRange = {
		["StartX"] = math.min(MaxX1, MaxX2), ["EndX"] = math.max(MinX1, MinX2),
		["StartY"] = math.min(MaxY1, MaxY2), ["EndY"] = math.max(MinY1, MinY2),
		["StartZ"] = math.min(MaxZ1, MaxZ2), ["EndZ"] = math.max(MinZ1, MinZ2),
	}
	--swaps
	if OverlapRange.StartX > OverlapRange.EndX then
		dictSwap(OverlapRange.StartX, OverlapRange.EndX)
	end
	if OverlapRange.StartY > OverlapRange.EndY then
		dictSwap(OverlapRange.StartY, OverlapRange.EndY)	
	end
	if OverlapRange.StartZ > OverlapRange.EndZ then
		dictSwap(OverlapRange.StartZ, OverlapRange.EndZ)	
	end
	
	return OverlapRange
end

--assuming that the two parts Part and Part2 have overlap
local Result = getOverlapResult(Part, Part2)

--create a new part in the object space of the overlap to display
local SizeX, SizeY, SizeZ = math.abs(Result.EndX - Result.StartX), math.abs(Result.EndY - Result.StartY), math.abs(Result.EndZ - Result.StartZ)
local PosX, PosY, PosZ = (Result.StartX + Result.EndX)/2, (Result.StartY + Result.EndY)/2, (Result.StartZ + Result.EndZ)/2
local NewPart = Instance.new("Part")
NewPart.Parent = workspace
NewPart.Anchored = true
NewPart.Size = Vector3.new(SizeX, SizeY, SizeZ)
NewPart.Position = Vector3.new(PosX, PosY, PosZ)
local Highlight = Instance.new("Highlight")
Highlight.Parent = NewPart
Highlight.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop
Highlight.FillColor = Color3.new(250,0,0)

I can see why it can be perceived as difficult to get the overlapped space between two parts, (especially rotated ones) but it might just be better to add that as a raycast mathod rather than introducing a BasePart:GetLastContact() method.

2 Likes

That only solves the problem if you want a close enough estimation for collisions. The issue is that Roblox does 4 physics steps per frame with no way of knowing what each of them did. Your part could have bounced off another part in one of those steps, meaning it won’t overlap with anything before or after the frame. You can assume it did something using Touched and linear/angular velocity changes, but good luck extracting any meaningful information from that.

That doesn’t change the fact that we need a way of getting more detailed information like the exact time the collision happened, the velocities of both parts when it happened, an average offset of the collision from both parts, etc. But, GetLastContact wouldn’t be a good solution, because as someone already said, it’s simply not worth the memory usage. You’d basically be doubling the memory dedicated to physics for all parts in your game, just so that you can get collision info for a single part.

An better solution would be a new CollisionTracker instance that can be parented to a BasePart. It would hold boolean properties to select which info you want, and an array containing dictionaries for each step.

3 Likes