You can write your topic however you want, but you need to answer these questions:
Hello,
I am attempting to create a WMMT style game, focusing on the ‘brake blocking’ physics. I’ve created a system for slowing down vehicle against walls, ‘zero-stopping’ on certain walls/areas, and a proper rubber-banding system. However, I am struggling with getting the physics right for ‘shoving’ other players while in a vehicle. I more so run into an issue where player A will be in front of player B, and player A will lean into player B’s car to shove them into a wall, however player A’s vehicle will get shoved instead and sometimes flung back. I also notice when testing if player B’s vehicle is static, and player A slowly taps player B (just to simulate a brake block) player A’s vehicle gets pushed back instead of player B’s. Any suggestions on how I can tackle this issue would be greatly appreciated!
Here is a video explaining ‘brake blocking’ for those unfamiliar.
Here is my current code for handling the brake blocking:
local RunService = game:GetService("RunService")
local CollectionService = game:GetService("CollectionService")
local blockerVehicle = script.Parent
local primaryPart = blockerVehicle.PrimaryPart
local VEHICLE_TAG = "PlayerVehicle"
local BRAKE_FACTOR = 0.7
local PUSH_FORCE = 40 -- high value just for testin
local COOLDOWN = 1.0
local SIDE_IMPACT_THRESHOLD = 0.5
local ATTACKER_VELOCITY_THRESHOLD = -0.5
local blockIsActive = false
local lastBlockTime = 0
local velocityBeforeHit = Vector3.new()
local impactNormalForPush = Vector3.new()
local targetVehicle = nil
local velocityOfTargetBeforeHit = Vector3.new()
CollectionService:AddTag(blockerVehicle, VEHICLE_TAG)
local function findAncestorWithTag(instance, tag)
local current = instance
while current do
if CollectionService:HasTag(current, tag) then
return current
end
current = current.Parent
end
return nil
end
local function onTouched(otherPart)
if os.clock() - lastBlockTime < COOLDOWN then return end
local otherModel = findAncestorWithTag(otherPart, VEHICLE_TAG)
if not otherModel or otherModel == blockerVehicle or not otherModel.PrimaryPart then
return
end
local targetVelocity = otherModel.PrimaryPart.AssemblyLinearVelocity
if targetVelocity.Magnitude < 1 then
return
end
local origin = otherModel.PrimaryPart.Position
local direction = primaryPart.Position - origin
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {otherModel}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
local result = workspace:Raycast(origin, direction, raycastParams)
if not (result and result.Instance:IsDescendantOf(blockerVehicle)) then return end
local impactNormal = result.Normal
local sideImpactDot = primaryPart.CFrame.LookVector:Dot(impactNormal)
if math.abs(sideImpactDot) > SIDE_IMPACT_THRESHOLD then
return
end
local targetVelocityDot = targetVelocity.Unit:Dot(impactNormal)
if targetVelocityDot > ATTACKER_VELOCITY_THRESHOLD then
return
end
velocityBeforeHit = primaryPart.AssemblyLinearVelocity
velocityOfTargetBeforeHit = targetVelocity
impactNormalForPush = impactNormal
targetVehicle = otherModel
blockIsActive = true
lastBlockTime = os.clock()
end
RunService.Stepped:Connect(function(gameTime, deltaTime)
if blockIsActive then
if not (targetVehicle and targetVehicle.PrimaryPart and targetVehicle.Parent) then
blockIsActive = false
return
end
local currentTargetCFrame = targetVehicle:GetPrimaryPartCFrame()
targetVehicle:SetPrimaryPartCFrame(currentTargetCFrame + impactNormalForPush * 0.1)
local n = impactNormalForPush
local v = velocityOfTargetBeforeHit
local closingSpeed = v:Dot(n)
local closingVelocity = closingSpeed * n
local parallelVelocity = v - closingVelocity
local pushVelocity = n * PUSH_FORCE
local finalTargetVelocity = (parallelVelocity * BRAKE_FACTOR) + pushVelocity
targetVehicle.PrimaryPart.AssemblyLinearVelocity = finalTargetVelocity
targetVehicle.PrimaryPart.AssemblyAngularVelocity = Vector3.new(0, 0, 0) -- Prevent spinouts maybe???
primaryPart.AssemblyLinearVelocity = velocityBeforeHit * BRAKE_FACTOR
primaryPart.AssemblyAngularVelocity = Vector3.new(0, 0, 0) -- Prevent spinouts for own vehicle
blockIsActive = false
targetVehicle = nil
end
end)
local bodyContainer = blockerVehicle:FindFirstChild("Body")
if bodyContainer and bodyContainer:IsA("PVInstance") then
for _, bodyPart in ipairs(bodyContainer:GetDescendants()) do
if bodyPart:IsA("BasePart") then
bodyPart.Touched:Connect(onTouched)
end
end
else
primaryPart.Touched:Connect(onTouched)
end
blockerVehicle.Destroying:Connect(function()
if CollectionService:HasTag(blockerVehicle, VEHICLE_TAG) then
CollectionService:RemoveTag(blockerVehicle, VEHICLE_TAG)
end
end)
Truly I am unsure if this would even be possible with the physics and network involved, but any suggestions would be appreciated!
So a little update on this. I ended up getting some of the physics better, and made only the front end of the vehicle ‘detectable’ for the pushing physics. I think im moreso going for a ‘bumper kart’ type instead of full on brake blocking cause im still not sure how accurate the physics can get
local RunService = game:GetService("RunService")
local CollectionService = game:GetService("CollectionService")
local blockerVehicle = script.Parent
blockerVehicle:SetAttribute("CanBrakeBlock", true)
local primaryPart = blockerVehicle.PrimaryPart
local VEHICLE_TAG = "PlayerVehicle"
local PUSH_FORCE_MULTIPLIER = 150
local SHOVE_DURATION = 0.25
local COOLDOWN = 1.0
local blockState = "Inactive"
local lastBlockTime = 0
local blockStartTime = 0
local targetVehicle = nil
local impactNormal = Vector3.new()
local pushForceInstance = nil
CollectionService:AddTag(blockerVehicle, VEHICLE_TAG)
local ignoreParts = {}
for _, part in ipairs(blockerVehicle:GetDescendants()) do
if part:IsA("BasePart") then
ignoreParts[part] = true
end
end
local function findTaggedVehicle(instance)
local current = instance
while current and current ~= workspace do
if CollectionService:HasTag(current, VEHICLE_TAG) then
return current
end
current = current.Parent
end
return nil
end
local function findSeatVehicle(instance)
local current = instance
while current and current ~= workspace do
if current:IsA("Model") and current.PrimaryPart and current.PrimaryPart:IsA("VehicleSeat") then
return current
end
current = current.Parent
end
return nil
end
local function onTouched(otherPart)
if not blockerVehicle:GetAttribute("CanBrakeBlock") then
return
end
if ignoreParts[otherPart] then
return
end
if blockState ~= "Inactive" or os.clock() - lastBlockTime < COOLDOWN then
return
end
local otherModel = findTaggedVehicle(otherPart) or findSeatVehicle(otherPart)
if not otherModel or otherModel == blockerVehicle or not otherModel.PrimaryPart then
return
end
if not otherModel:GetAttribute("CanBrakeBlock") then
return
end
impactNormal = (otherModel.PrimaryPart.Position - primaryPart.Position).Unit
targetVehicle = otherModel
blockState = "Starting"
lastBlockTime = os.clock()
blockerVehicle:SetAttribute("CanBrakeBlock", false)
otherModel:SetAttribute("CanBrakeBlock", false)
print("Push initiated by", blockerVehicle.Name, "against", otherModel.Name)
end
RunService.Heartbeat:Connect(function()
if blockState == "Starting" then
if not (targetVehicle and targetVehicle.PrimaryPart) then
blockState = "Inactive"
blockerVehicle:SetAttribute("CanBrakeBlock", true)
if targetVehicle and targetVehicle.Parent then
targetVehicle:SetAttribute("CanBrakeBlock", true)
end
return
end
local tp = targetVehicle.PrimaryPart
local tMass = tp.AssemblyMass
local forceMag = tMass * PUSH_FORCE_MULTIPLIER
local attach = Instance.new("Attachment", tp)
pushForceInstance = Instance.new("VectorForce", attach)
pushForceInstance.RelativeTo = Enum.ActuatorRelativeTo.World
pushForceInstance.Attachment0 = attach
pushForceInstance.Force = impactNormal * forceMag
blockStartTime = os.clock()
blockState = "Active"
elseif blockState == "Active" then
if os.clock() - blockStartTime >= SHOVE_DURATION then
blockState = "Ending"
end
elseif blockState == "Ending" then
if pushForceInstance and pushForceInstance.Parent then
pushForceInstance.Parent:Destroy()
pushForceInstance = nil
end
blockerVehicle:SetAttribute("CanBrakeBlock", true)
if targetVehicle and targetVehicle.Parent then
targetVehicle:SetAttribute("CanBrakeBlock", true)
end
targetVehicle = nil
blockState = "Inactive"
end
end)
local brakePoints = blockerVehicle:FindFirstChild("BrakeBlockPoints")
if brakePoints then
for _, part in ipairs(brakePoints:GetDescendants()) do
if part:IsA("BasePart") then
part.Touched:Connect(onTouched)
end
end
else
local bodyFolder = blockerVehicle:FindFirstChild("Body")
if bodyFolder then
for _, part in ipairs(bodyFolder:GetDescendants()) do
if part:IsA("BasePart") then
part.Touched:Connect(onTouched)
end
end
else
primaryPart.Touched:Connect(onTouched)
end
end
blockerVehicle.Destroying:Connect(function()
CollectionService:RemoveTag(blockerVehicle, VEHICLE_TAG)
blockState = "Ending"
end)
BUT I did want to ask, running a few play test I notice there’s a pretty big desync in placement of 2 vehicles. So for example on player A’s screen player A and player B may be close next to each other, but on player B’s screen player A could be further in front or further behind. I’m wondering whats the best way to approach the desyncing, or is that something that cannot be addressed?
I’ve made more progress on this, ofc items are place holders for now until i get the physics and system to a good point. I’m even using the racing template roblox provided, mostly because its a good platform to test on.
I’ve switch up to instead make it so instead of ‘pushing’ the other vehicles, i’ve added a stabilizer that helps keep the vehicle ‘stiff’, even if its at an angle and colliding with another object. the idea came to me yesterday and i’ve seemed to got it mostly working. its not perfect, but its a lot better. The ‘brake blocking’ now is set by the front end and a bit of the side of the vehicle detecting if its hit the other vehicles front end, and instead of ‘pushing’ it decreases the maxspeed for a second and then restores the original max speed. I may make it so the player has to ‘brake’ for this to connect, but im not sure yet cause that’d be really hard on mobile.
Heres a ‘slow’ version of me and my wife testing the brake blocking
Heres areal good example of the ‘brake blocking’ in action.
I also made it so you cannot ‘brake block’ the person in front of you. its a bit janky but im makin progress!
(and ofc she wrecks herself at the end after doing so well )
Will do some more research on the networkownership, during the playtest I could see our vehicles where quite desynced which i think it going to cause more problems later on
I actually did this, but I got an annoying effect where the other player would ‘freeze’ or ‘jitter’ really bad when hitting them or attempting to hit them, so it made performing the ‘brake block’ nearly impossible. not sure if i set something up wrong or if its just networking being silly
So i found this wonderful thread as I am running into this exact issue
I see as of May 19 a staff member stated they are working on a server-authority system that should address this exact issue I am having with the desync.
So brake block handling and stabilization is completed. I will await for the server-authority system to come out to address this. I think that’ll overall help the desyncing issue
EDIT: of course if anyone has any suggestions please feel free to reply
I’ve tried >
setting network ownership nil (to server) but the input delay was quite bad
setting network ownership to player when brake blocking / colliding, but this doesn’t really work since the position(s) of the vehicles on each client side will be different so its quite hard to tell when your colliding with someone
setting network ownership to player w/ lowest ping. this rlly only ‘works’ for the player with the lowest ping bc other players will get input lag like in #1
A little update on this. I whipped together a game using these physics. Im moreso going to wait for the server-authority system to come out to complete this but here is a little ‘demo’