Help with Goalkeeper

Hello developers :smiley:
I’ve been trying to develop a realistic goalkeeper using IK, BodyVelocity and AlignOrientation. (Ragdoll as well)

The problem emerges when I use ragdoll when the GK is diving.
Here is a video

The results of the radgoll are not I was looking for. I’m looking for the ragdoll follows the dive and the orientation don’t change so much like in real life or in videogames.
This is an example of I want:


The ragdoll’s script (it’s a module script) for the GK is this:

local module = {}
local PhysicsService = game:GetService("PhysicsService")

local function getNearestPlayer( position )
	local Players = game.Players:GetChildren();
	local dist = math.huge;
	local ret = nil;
	for i=1,#Players do
		local Player = Players[i];
		local Character = Player.Character;
		if ( Character ~= nil ) then
			local Root = Character:FindFirstChild("HumanoidRootPart");
			if ( Root ~= nil ) then
				local t = (position - Root.Position).magnitude;
				if ( t < dist ) then
					dist = t;
					ret = Player;
				end
			end
		end
	end

	return ret;
end

local RootLimbData = {
	{
		["WeldTo"]			= "LowerTorso",
		["WeldRoot"]		= "HumanoidRootPart",
		["AttachmentName"]	= "Root",
		["NeedsCollider"]	= false,
		["UpperAngle"]		= 10
	},
	{
		["WeldTo"]			= "UpperTorso",
		["WeldRoot"]		= "LowerTorso",
		["AttachmentName"]	= "Waist",
		["ColliderOffset"]	= CFrame.new(0, 0.5, 0),
		["UpperAngle"]		= 0
	},
	{
		["WeldTo"]			= "Head",
		["WeldRoot"]		= "UpperTorso",
		["AttachmentName"]	= "Neck",
		["ColliderOffset"]	= CFrame.new(),
		["UpperAngle"]		= 20
	},
	{
		["WeldTo"]			= "LeftUpperLeg",
		["WeldRoot"]		= "LowerTorso",
		["AttachmentName"]	= "LeftHip",
		["ColliderOffset"]	= CFrame.new(0, -0.5, 0)
	},
	{
		["WeldTo"]			= "RightUpperLeg",
		["WeldRoot"]		= "LowerTorso",
		["AttachmentName"]	= "RightHip",
		["ColliderOffset"]	= CFrame.new(0, -0.5, 0)
	},
	{
		["WeldTo"]			= "RightLowerLeg",
		["WeldRoot"]		= "RightUpperLeg",
		["AttachmentName"]	= "RightKnee",
		["ColliderOffset"]	= CFrame.new(0, -0.5, 0)
	},
	{
		["WeldTo"]			= "LeftLowerLeg",
		["WeldRoot"]		= "LeftUpperLeg",
		["AttachmentName"]	= "LeftKnee",
		["ColliderOffset"]	= CFrame.new(-0.05, -0.5, 0)
	},
	{
		["WeldTo"]			= "RightUpperArm",
		["WeldRoot"]		= "UpperTorso",
		["AttachmentName"]	= "RightShoulder",
		["ColliderOffset"]	= CFrame.new(0.05, 0.45, 0.15),
	},
	{
		["WeldTo"]			= "LeftUpperArm",
		["WeldRoot"]		= "UpperTorso",
		["AttachmentName"]	= "LeftShoulder",
		["ColliderOffset"]	= CFrame.new(0, 0.45, 0.15),
	},
	{
		["WeldTo"]			= "LeftLowerArm",
		["WeldRoot"]		= "LeftUpperArm",
		["AttachmentName"]	= "LeftElbow",
		["ColliderOffset"]	= CFrame.new(0, 0.125, 0),
		["UpperAngle"]		= 10
	},
	{
		["WeldTo"]			= "RightLowerArm",
		["WeldRoot"]		= "RightUpperArm",
		["AttachmentName"]	= "RightElbow",
		["ColliderOffset"]	= CFrame.new(0, 0.125, 0),
		["UpperAngle"]		= 10
	},
	{
		["WeldTo"]			= "RightHand",
		["WeldRoot"]		= "RightLowerArm",
		["AttachmentName"]	= "RightWrist",
		["ColliderOffset"]	= CFrame.new(0, 0.125, 0),
		["UpperAngle"]		= 0
	},
	{
		["WeldTo"]			= "LeftHand",
		["WeldRoot"]		= "LeftLowerArm",
		["AttachmentName"]	= "LeftWrist",
		["ColliderOffset"]	= CFrame.new(0, 0.125, 0),
		["UpperAngle"]		= 0
	},
	{
		["WeldTo"]			= "LeftFoot",
		["WeldRoot"]		= "LeftLowerLeg",
		["AttachmentName"]	= "LeftAnkle",
		["NeedsCollider"]	= false,
		["UpperAngle"]		= 0
	},
	{
		["WeldTo"]			= "RightFoot",
		["WeldRoot"]		= "RightLowerLeg",
		["AttachmentName"]	= "RightAnkle",
		["NeedsCollider"]	= false,
		["UpperAngle"]		= 0
	},
}

local RootPart = nil;
local MotorList = {};
local GlueList = {};
local ColliderList = {};

-- Función para habilitar el ragdoll
function module:activate(Char)
	print("Ragdolling");
	local Character = Char;
	local Humanoid = Char:FindFirstChild("Humanoid");
	local HumanoidRoot = Char:FindFirstChild("HumanoidRootPart");
	if ( HumanoidRoot == nil ) then
		print("Cannot create ragdoll");
		return;
	end
	local Position = Char.HumanoidRootPart.Position;
	Humanoid.PlatformStand = true;
	local ActivateOnDeath = Instance.new("BoolValue"); --ActivateOnDeath.Value = true
	-- Handle death specific ragdoll. Will Clone you, then destroy you.
	local RagDollModel = Character;
	if ActivateOnDeath.Value then
		Character:FindFirstChild("HumanoidRootPart"):Destroy();
		Character.Archivable = true;
		RagDollModel = Character:Clone();
		RagDollModel.Name = "RagDoll";

		local t = RagDollModel:GetChildren();
		for i=1,#t do
			local t2 = t[i];
			if ( t2:IsA("Script") or t2:IsA("LocalScript") ) then
				t2:Destroy();
			end
		end

		spawn(function()
			wait();
			RagDollModel.Humanoid.PlatformStand = true;
			--game.Debris:AddItem(RagDollModel, 10);
		end)

		RagDollModel.Humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None;
		RagDollModel.Humanoid.HealthDisplayType = Enum.HumanoidHealthDisplayType.AlwaysOff;
		RagDollModel.Humanoid.Health = 0;
		RagDollModel.Parent = game.Workspace;

		local RagDollPointer = Instance.new("ObjectValue");
		RagDollPointer.Value = RagDollModel;
		RagDollPointer.Name = "RagDoll";
		RagDollPointer.Parent = Character;
	end
	local ApplyRandomVelocity  = Instance.new("BoolValue"); ApplyRandomVelocity.Value = true
	local Force  = Instance.new("NumberValue"); Force.Value = 20

	local WeldHead = Instance.new("BoolValue"); WeldHead.Value = true
	-- Reglue The Character
	for i=1,#RootLimbData do
		local limbData = RootLimbData[i];
		local partName = limbData["WeldTo"];
		local weldName = limbData["WeldRoot"];
		local PartTo = RagDollModel:FindFirstChild(partName);
		local WeldTo = RagDollModel:FindFirstChild(weldName);

		if ( PartTo ~= nil and WeldTo ~= nil ) then
			if ( RagDollModel ~= nil ) then
				if ( ApplyRandomVelocity.Value ) then
					local scale = Force.Value;
					local vecX = (math.random()-math.random())*scale;
					local vecY = (math.random()-math.random())*scale;
					local vecZ = (math.random()-math.random())*scale;
					--PartTo.Velocity = PartTo.Velocity + Vector3.new(vecX, vecY, vecZ);
				end
				PartTo.Parent = RagDollModel;
			end
			-- Create New Constraint
			local UpperAngle = limbData["UpperAngle"];
			local Joint = Instance.new("BallSocketConstraint");
			if ( (UpperAngle ~= nil and UpperAngle == 0) or (WeldHead.Value and partName == "Head") ) then
				Joint = Instance.new("HingeConstraint");
				Joint.UpperAngle = 0;
				Joint.LowerAngle = 0;
			end
			Joint.Name = limbData["AttachmentName"];
			Joint.LimitsEnabled = true;
			if not Joint:IsA("HingeConstraint") then
				Joint.TwistLimitsEnabled = true;
			end
			Joint.Attachment0 = PartTo:FindFirstChild(Joint.Name .. "RigAttachment");
			Joint.Attachment1 = WeldTo:FindFirstChild(Joint.Name .. "RigAttachment");
			Joint.Parent = PartTo;
			GlueList[#GlueList+1] = Joint;
			if ( UpperAngle ~= nil ) then
				Joint.UpperAngle = UpperAngle;
			end

			-- Destroy the motor attaching it
			local Motor = PartTo:FindFirstChildOfClass("Motor6D");
			if ( Motor ~= nil ) then
				if ( Humanoid.Health <= 0 ) then
					Motor:Destroy();
				else
					MotorList[#MotorList+1] = { PartTo, Motor };
					Motor.Parent = nil;
				end
			end

			-- Create Collider
			local needsCollider = limbData["NeedsCollider"];
			if ( needsCollider == nil ) then
				needsCollider = true;
			end
			if ( needsCollider ) then
				local B = Instance.new("Part");
				B.CanCollide = true;
				B.TopSurface = 0;
				B.BottomSurface = 0;
				B.formFactor = "Symmetric";
				B.Size = Vector3.new(0.7, 0.7, 0.7);
				B.Transparency = 1;
				B.BrickColor = BrickColor.Red();
				B.Parent = RagDollModel;
				local W = Instance.new("Weld");
				W.Part0 = PartTo;
				W.Part1 = B;
				W.C0 = limbData["ColliderOffset"];
				W.Parent = PartTo;
				ColliderList[#ColliderList+1] = B;
			end
		end
	end

	-- Destroy Root Part
	local root = Character:FindFirstChild("HumanoidRootPart");
	if ( root ~= nil ) then
		RootPart = root;
		if ( Humanoid.Health <= 0 ) then
			RootPart:Destroy();
		else
			RootPart.Parent = nil;
		end
	end	

	-- Delete all my parts if we made a new ragdoll
	if ( RagDollModel ~= Character ) then
		print("Deleting character");
		local children = Character:GetChildren();
		for i=1,#children do
			local child = children[i];
			if ( child:IsA("BasePart") or child:IsA("Accessory") ) then
				child:Destroy();
			end
		end
	end

	-- Give player physics
	local GivePlayerPhysics = Instance.new("BoolValue"); GivePlayerPhysics.Value = true
	local ForceNearestPlayer = Instance.new("BoolValue")
	if ( GivePlayerPhysics.Value ) then
		local PlayerPhysics = Char;
		if ( ForceNearestPlayer.Value ) then
			PlayerPhysics = getNearestPlayer( Position );
		end

		local Children = RagDollModel:GetChildren();
		for i=1,#Children do
			local Child = Children[i];
			if ( Child:IsA("BasePart") ) then
				Child:SetNetworkOwner(PlayerPhysics);
			end
		end
	end

	-- Copy plugins into ragdoll
	--local Plugins = script.Plugins:GetChildren();
	--for i=1,#Plugins do
	--	local Plugin = Plugins[i];
	--	local Copy = Plugin:Clone();
	--	if ( Copy:IsA("Script") ) then
	---		Copy.Disabled = false;
	--	end
	--	Copy.Parent = RagDollModel;
	--end
end

I’ve tried to modify the alignOrientation (Didn’t work) and I was thinking in a solution but my mind is clear rn.

If you have any suggestions or ideas, it’ll be greatly appreciated! :smirk_cat:
Thanks for reading!

function module:activate(Char)
print(“Ragdolling”)
local Character = Char
local Humanoid = Character:FindFirstChild(“Humanoid”)
local HumanoidRoot = Character:FindFirstChild(“HumanoidRootPart”)
if HumanoidRoot == nil then
print(“Cannot create ragdoll: HumanoidRootPart not found”)
return
end
local Position = HumanoidRoot.Position

-- Disable ragdolling during certain actions (e.g., diving)
if IsGoalkeeperDiving() then
    print("Goalkeeper is diving, skipping ragdoll activation")
    return
end

-- Ragdoll activation code
-- Your existing ragdoll activation code here...

end

– Function to check if the goalkeeper is currently diving
function IsGoalkeeperDiving()
– Insert your logic to determine if the goalkeeper is diving
– For example, check if the goalkeeper is in a specific animation state
return false – Return true if the goalkeeper is diving, false otherwise
end

Thank you for your suggestion! I appreciate your help. However, my goal is not to disable the ragdoll during the dive but to make the ragdoll follow the dive motion more realistically.

Specifically, I want the ragdoll to retain the diving orientation and position without drastically changing when the ragdoll activates. In real life or video games, when a goalkeeper dives, their body follows the motion fluidly even when they lose control. I’m looking for a way to achieve that smooth transition. :cat:

1 Like