Fallen Character/Parts Recall Height
In response to the following request from the last AMA:
I’ve just created two different scripts for a non-built-in custom solution. Place either script into “ServerScriptService”. You’ll only need one of the scripts.
With either solution, I’d recommend setting your Workspace’s “FallenPartsDestroyHeight” property to a large negative number or running this in your command line:
workspace.FallenPartsDestroyHeight = -math.huge
You can also set a custom height for the recall by creating a number attribute with the correct name and value. Instructions are at the top of either script.
Player Characters Only:
The first script only works with player characters:
Model: Fallen Character Recall Height
Raw Script
--[[
For the best experience, do one of the following:
1. Change the Workspace's "FallenPartsDestroyHeight" to a large negative number,
then add a number attribute to the Workspace. Name it "FallenCharacterRecallHeight"
and set its value to "-500" or your desired height.
OR
2. Run the following code in your command line:
workspace.FallenPartsDestroyHeight=-math.huge workspace:SetAttribute('FallenCharacterRecallHeight',-500)
]]
local fallenCharacterRecallHeight = workspace:GetAttribute('FallenCharacterRecallHeight') or workspace.FallenPartsDestroyHeight + 10
if not workspace:GetAttribute('FallenCharacterRecallHeight') and workspace.FallenPartsDestroyHeight <= -50000 then
fallenCharacterRecallHeight = -500
end
local runService = game:GetService('RunService')
function Raycast(humanoidRootPart)
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {humanoidRootPart.Parent}
local raycastCenter = workspace:Raycast(humanoidRootPart.Position,Vector3.new(0,-10,0),raycastParams)
local raycastFront = workspace:Raycast(humanoidRootPart.Position+Vector3.new(0,0,2),Vector3.new(0,-10,0),raycastParams)
local raycastBack = workspace:Raycast(humanoidRootPart.Position+Vector3.new(0,0,-2),Vector3.new(0,-10,0),raycastParams)
local raycastLeft = workspace:Raycast(humanoidRootPart.Position+Vector3.new(2,0,0),Vector3.new(0,-10,0),raycastParams)
local raycastRight = workspace:Raycast(humanoidRootPart.Position+Vector3.new(-2,0,0),Vector3.new(0,-10,0),raycastParams)
local result
if raycastCenter and raycastFront and raycastBack and raycastLeft and raycastRight then
if raycastCenter.Position and raycastFront.Position and raycastBack.Position and raycastLeft.Position and raycastRight.Position then
result = humanoidRootPart.CFrame
end
end
raycastParams,raycastCenter,raycastFront,raycastBack,raycastLeft,raycastRight = nil
return result
end
function ResetAssembly(basePart,networkOwner)
basePart.AssemblyLinearVelocity = Vector3.new()
basePart.AssemblyAngularVelocity = Vector3.new()
pcall(function()
if networkOwner then
basePart:SetNetworkOwner(networkOwner)
elseif basePart:GetNetworkOwnershipAuto() then
basePart:SetNetworkOwnershipAuto()
end
end)
end
game:GetService('Players').PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
local humanoidRootPart = character:WaitForChild('HumanoidRootPart')
local cframe = humanoidRootPart.CFrame
while character do
if humanoidRootPart.Position.Y <= fallenCharacterRecallHeight then
humanoidRootPart.CFrame = cframe
for _,basePart in character:GetDescendants() do
if basePart:IsA('BasePart') then
ResetAssembly(basePart,player)
end
end
elseif humanoidRootPart then
local result = Raycast(humanoidRootPart)
if result and cframe then
cframe = result
end
result = nil
end
task.wait()
end
local removed
removed = character:GetPropertyChangedSignal('Parent'):Connect(function()
if not character.Parent then
removed:Disconnect()
character,humanoidRootPart,cframe,removed = nil
end
end)
end)
end)
workspace:GetAttributeChangedSignal('FallenCharacterRecallHeight'):Connect(function()
fallenCharacterRecallHeight = workspace:GetAttribute('FallenCharacterRecallHeight')
end)
workspace:SetAttribute('FallenCharacterRecallHeight',fallenCharacterRecallHeight)
All Physics Parts:
This second script will work with all physics parts, including models that have an unanchored PrimaryPart:
Model: Fallen Parts Recall Height
Raw Script
--[[
For the best experience, do one of the following:
1. Change the Workspace's "FallenPartsDestroyHeight" to a large negative number,
then add a number attribute to the Workspace. Name it "FallenPartsRecallHeight"
and set its value to "-500" or your desired height.
OR
2. Run the following code in your command line:
workspace.FallenPartsDestroyHeight=-math.huge workspace:SetAttribute('FallenPartsRecallHeight',-500)
]]
local fallenPartsRecallHeight = workspace:GetAttribute('FallenPartsRecallHeight') or workspace.FallenPartsDestroyHeight + 10
if not workspace:GetAttribute('FallenPartsRecallHeight') and workspace.FallenPartsDestroyHeight <= -50000 then
fallenPartsRecallHeight = -500
end
workspace:GetAttributeChangedSignal('FallenPartsRecallHeight'):Connect(function()
fallenPartsRecallHeight = workspace:GetAttribute('FallenPartsRecallHeight')
end)
workspace:GetPropertyChangedSignal('FallenPartsDestroyHeight'):Connect(function()
fallenPartsRecallHeight = workspace.FallenPartsDestroyHeight + 10
end)
workspace:SetAttribute('FallenPartsRecallHeight',fallenPartsRecallHeight)
local physicsObjects = {}
function GetModelFromPart(basePart)
local model
local parent = basePart
while not model or parent == game do
parent = parent.Parent
if parent:IsA('Model') then
model = parent
end
end
parent,basePart = nil
return model
end
function SetupInstance(instance)
if instance:IsA('BasePart') and not instance.Anchored then
local object = {}
object.Instance = instance
object.CFrame = instance.CFrame
local model = GetModelFromPart(instance)
if model then
local primaryPart = model.PrimaryPart
if primaryPart and not primaryPart.Anchored then
object.Model = model
object.CFrame = primaryPart.CFrame
end
model,primaryPart = nil
end
table.insert(physicsObjects,object)
end
end
for _,instance in workspace:GetDescendants() do
SetupInstance(instance)
end
workspace.DescendantAdded:Connect(function(instance)
SetupInstance(instance)
end)
function ResetAssembly(basePart,networkOwner)
basePart.AssemblyLinearVelocity = Vector3.new()
basePart.AssemblyAngularVelocity = Vector3.new()
pcall(function()
if networkOwner then
basePart:SetNetworkOwner(networkOwner)
elseif basePart:GetNetworkOwnershipAuto() then
basePart:SetNetworkOwnershipAuto()
end
end)
end
function Raycast(basePart,params)
local raycastCenter = workspace:Raycast(basePart.Position,Vector3.new(0,-10,0),params)
local raycastFront = workspace:Raycast(basePart.Position+Vector3.new(0,0,2),Vector3.new(0,-10,0),params)
local raycastBack = workspace:Raycast(basePart.Position+Vector3.new(0,0,-2),Vector3.new(0,-10,0),params)
local raycastLeft = workspace:Raycast(basePart.Position+Vector3.new(2,0,0),Vector3.new(0,-10,0),params)
local raycastRight = workspace:Raycast(basePart.Position+Vector3.new(-2,0,0),Vector3.new(0,-10,0),params)
local result
if raycastCenter and raycastFront and raycastBack and raycastLeft and raycastRight then
if raycastCenter.Position and raycastFront.Position and raycastBack.Position and raycastLeft.Position and raycastRight.Position then
result = basePart.CFrame
end
end
basePart,params,raycastCenter,raycastFront,raycastBack,raycastLeft,raycastRight = nil
return result
end
workspace.DescendantRemoving:Connect(function(instance)
local index = 0
for _,object in physicsObjects do
index+=1
if object.Instance == instance then
table.remove(physicsObjects,index)
object.Instance = nil
object.CFrame = nil
object.Model = nil
object = nil
end
end
instance,index = nil
end)
while true do
for _,object in physicsObjects do
if not object.Instance.Anchored then
if object.Instance.Position.Y < fallenPartsRecallHeight then
local networkOwner
pcall(function()
if not object.Instance:GetNetworkOwnershipAuto() then
networkOwner = object.Instance:GetNetworkOwner()
end
end)
if object.Model then
object.Model:SetPrimaryPartCFrame(object.CFrame)
for _,basePart in object.Model:GetDescendants() do
if basePart:IsA('BasePart') and not basePart.Anchored then
ResetAssembly(basePart,networkOwner)
end
end
modelCFrame,position = nil
else
object.Instance.CFrame = object.CFrame
ResetAssembly(object.Instance,networkOwner)
end
networkOwner = nil
else
local params = RaycastParams.new()
if object.Model then
params.FilterDescendantsInstances = {object.Model}
else
params.FilterDescendantsInstances = {object.Instance}
end
local result
local results
pcall(function()
if object.Model then
result = Raycast(object.Model.PrimaryPart,params)
else
result = Raycast(object.Instance,params)
end
end)
if result then
object.CFrame = result
end
result = nil
end
end
end
task.wait()
end
This script also works with models that have an unanchored PrimaryPart (including player characters), otherwise it will treat the part as a normal part.
(This script will obviously be more costly than the other script due to it tracking all physics part rather than just the root parts of player characters. It worked fine for my tests of about 100 parts, but it could lead to issues if your experience has a lot more physics parts.)
Let me know if there are any bugs or other issues you come across.
Other Resources:
- Area Sound Kit
- API Service
- Gif UI Element
- Explorer Info
- And more…