E to Dive script like Football Fusion

Hello! I am trying to recreate a dive script that is almost exactly like Football Fusion 2. I’ve attempted reading through old posts but they are outdated and now longer work.

What I’m trying to achieve:

I’ve tried old scripts but they didn’t work (as mentioned above)
Here’s what I have:

Server-side

local Settings = require(script.Settings)

local diveLength = Settings.DiveLength
local diveHeight = Settings.DiveHeight

local Event

game.Players.PlayerAdded:Connect(function(player)
	-- establish main script as well as the event, parent them.
	local DoesEventAlreadyExist = false
	
	local character = player.Character or player.CharacterAdded:Wait()
	local NewScript = script.Important.DiveScript:Clone()
	NewScript.Parent = character
	
	if not DoesEventAlreadyExist then
		DoesEventAlreadyExist = true
		local NewEvent = script.Important:WaitForChild("PressedE"):Clone()
		NewEvent.Parent = game:GetService("ReplicatedStorage")
		
		Event = NewEvent
	end
	
	local HRP = character:WaitForChild("HumanoidRootPart")
	
	Event.OnServerEvent:Connect(function()
		HRP:SetNetworkOwner(nil)
		HRP:ApplyImpulse(HRP:GetMass()*HRP.CFrame.LookVector * diveLength + Vector3.new(0,diveHeight,0))
	end)
end)

Client-side

local Settings = require(workspace.Dive.Settings)

local UIS = game:GetService("UserInputService")
local event = game:GetService("ReplicatedStorage"):WaitForChild("PressedE")

local debounce = false

UIS.InputBegan:Connect(function(input, IsChatting)
	if not IsChatting then
		if input.KeyCode == Enum.KeyCode.E then
			if not debounce then
				debounce = true
				event:FireServer()
				task.wait(Settings.DiveDebounce)
				debounce = false
			end
		end
	end
end)

Settings (module script)

local Settings = {
	
	DiveLength = 5, -- Dive length in studs
	DiveHeight = 5, -- Dive height in studs
	DiveDebounce = 3 -- The delay between each dive in seconds
	
}

return Settings

Here’s the explorer if necessary
image

6 Likes

You need to use ChangeState to make the character flop. In this case, using Enum.HumanoidStateType.Ragdoll will make the player lose control of the character, and applying any impulse to it will work.

I made the changes to the server script below. I also set the network owner of the root part back to the player, or the movement would feel laggy after the dive.

    local humanoid = character:WaitForChild("Humanoid")
	local HRP = character:WaitForChild("HumanoidRootPart")

	Event.OnServerEvent:Connect(function()
		humanoid:ChangeState(Enum.HumanoidStateType.Ragdoll)
		HRP:SetNetworkOwner(nil)
		HRP:ApplyImpulse(HRP:GetMass()*HRP.CFrame.LookVector * diveLength + Vector3.new(0,diveHeight,0))
		task.wait(1)
		HRP:SetNetworkOwner(player)
	end)

Hope this helps!

1 Like

Thanks for the idea! I’ll give it a try when I get a chance

It partially works, it does dive but the legs keep on moving. Is there any way to make it so the player is completely still during the ragdoll?

You can look at this thread to stop all of the animations when you press e in the client script.

I made some adjustments to the script as well as adding in what you said.

Here’s how it currently looks:

The server-side script with adjustments:

Event.OnServerEvent:Connect(function(plr)
	local NewChar = plr.Character

	local Humanoid = NewChar:WaitForChild("Humanoid")
	local HRP = NewChar:WaitForChild("HumanoidRootPart")

	local oldWS = Humanoid.WalkSpeed
	local oldJH = Humanoid.JumpHeight

	HRP:SetNetworkOwner(nil)
	HRP:ApplyImpulse(HRP:GetMass()*HRP.CFrame.LookVector * diveLength + Vector3.new(0,diveHeight,0))

	task.spawn(function()
		for _, track in pairs(Humanoid.Animator:GetPlayingAnimationTracks()) do
			track:Stop()
		end
	end)

	local GetAnimateScript = character.Animate

	GetAnimateScript.Parent = workspace
	GetAnimateScript = workspace.Animate

	Humanoid:ChangeState(Enum.HumanoidStateType.Ragdoll)

	Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)

	Humanoid.WalkSpeed = 0
	Humanoid.StateChanged:Connect(function(old,new)
		if old == Enum.HumanoidStateType.Ragdoll and new == Enum.HumanoidStateType.GettingUp then
			GetAnimateScript.Parent = character
			Humanoid.WalkSpeed = oldWS
			Humanoid.JumpHeight = oldJH
			HRP:SetNetworkOwner(player)
		end
	end)
end)

Is there any way to make it cleaner? I’m thinking I should change the way I move the actual character, like something other than ApplyImpulse.

I was looking at the different forces you could apply to the character, and it seems like ApplyImpulse is the best one for diving. The other forces are applied constantly overtime, so I’m not sure if that’ll give the right effect. (its still worth experimenting though, you can probably apply the force and remove it from the character quickly)

I noticed the little delay right before diving. I was thinking that it could be because it doesn’t take into account the current momentum of the character. So, you might need to add into calculating the impulse somehow.

The dive also looks like it doesn’t go very far.

In FF the character dives and lands flat on the ground. The dive right now looks like it only goes straight on by a little bit before stumbling forward. Maybe you could increase the settings for the dive length and height and see if it makes the character go farther.

You might also need to use ApplyImpulseAtPosition instead so that you can apply the impulse slightly above the root part of the character so that it rotates to the ground. (or apply the impulse on its head)

I’ll also look at any old dive scripts I can find later to see if we can see how they applied the force to the character.

1 Like

I’ve added some things. I found out what the delay before the dive is, it’s from setting the network owner to nil. I read into the documentation and a couple forum posts, here’s how Roblox explains it. They say it can create jittery physics, and when I remove SetNetworkOwner(nil) it just breaks and stops working. I also changed ApplyImpulse() to ApplyImpulseAtPosition, I simply put the HRP position and added Vector3.new(0,2,0). It works pretty well but not perfectly. The character still wont land flat and won’t really stay still when it IS on the ground. Any ideas?

I’m kind of running out of ideas. You could avoid using SetNetworkOwner all together and put everything on the client. (with the obvious problem of exploiting and I’m not sure if ChangeState and ApplyImpulse works on the client)

Could you also send a video of what the dive looks like now?

Also, you did mention you found old dive scripts, but I couldn’t find them. Send the scripts to me so I can take a look at it.

1 Like

Most useful thread, this is where I got the original ApplyImpulse idea from. Another thread, this one didn’t work very well but it kinda introduced the idea of using CFrame. Final thread this one gave me the idea of BodyVelocity’s/Forces, I tested them out but they didn’t work as I wanted them to.

Here’s the video: (I updated the overall power of the dive so it goes farther)

I also made an interesting discovery about the dive in Football Fusion, when I was actually playing it I saw that I landed upright but it was as if I was sitting down, like my legs were under the ground but my body was still up. (Let me know if you need me to go into more detail about that), I’m thinking I could sit the player while the dive goes on so it would possibly limit movement.

There’s also this built-in velocity function on all BaseParts. I think it acts a BodyVelocity but I’m not entirely sure how it works considering it is deprecated.

1 Like

I used your idea of making everything client-side and it worked! Thanks for all your help and patience!

Here’s the final script: (with added comments for you)

local UIS = game:GetService("UserInputService")

local debounce = false

local char = game.Players.LocalPlayer.Character or game.Players.LocalPlayer.CharacterAdded:Wait()

local HRP = char:WaitForChild("HumanoidRootPart")
local Humanoid = char:WaitForChild("Humanoid")

local diveHeight = 20
local diveLength = 20

UIS.InputBegan:Connect(function(input, IsChatting)
	local character = game.Players.LocalPlayer.Character
	if not IsChatting then
		if input.KeyCode == Enum.KeyCode.E then
			if not debounce then
				debounce = true
				
				local oldWS = Humanoid.WalkSpeed -- getting their old ws incase its something like 34
				local oldJH = Humanoid.JumpHeight -- same as walk speed
				
				HRP:ApplyImpulseAtPosition(HRP.CFrame.LookVector * diveLength + Vector3.new(0,diveHeight,0),HRP.Position + Vector3.new(0,1,0)) -- impulse

				task.spawn(function() -- stop animations without holding up script
					for _, track in pairs(Humanoid.Animator:GetPlayingAnimationTracks()) do
						track:Stop()
					end
				end)

				local GetAnimateScript = character.Animate

				GetAnimateScript.Parent = workspace
				GetAnimateScript = workspace.Animate -- essentially disable the animate script

				Humanoid:ChangeState(Enum.HumanoidStateType.Ragdoll)

				Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false) -- disable jumping

				Humanoid.WalkSpeed = 0
				Humanoid.JumpHeight = 0
				
				Humanoid.PlatformStand = true -- immoblize, the part where they were halfway in the ground (previous reply)
			
				repeat task.wait() until Humanoid:GetState() == Enum.HumanoidStateType.GettingUp -- wait until player gets up
				
				while Humanoid.PlatformStand == true do -- setting to true and undoing it disables PlatformStand, since the player can't move when its on
					Humanoid.Sit = true
					wait()
					Humanoid.Sit = false
				end
				
				Humanoid:ChangeState(Enum.HumanoidStateType.Running) -- change back to running to prevent any flinging
				
				GetAnimateScript.Parent = character -- add back animations
				Humanoid.WalkSpeed = oldWS -- ws and jh
				Humanoid.JumpHeight = oldJH
				Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, true) -- add jumping
				task.wait(.2) -- debounce
				debounce = false
			end
		end
	end
end)
2 Likes

Nice, I’m glad I was able to help!

I see that the character is a bit bouncy when hitting the ground. I looked into it, and you could make a part less bouncy by changing its CustomPhysicalProperties.

This thread has an explanation of how it works, and the docs has the definitions of what each property is.

But, I went ahead and added that, as well as make a few changes to clean up the code and fix a few problems when I tested the script out.

This also got me interested in recreating the dive mechanic since I also play FF. So if you want to you can view all the changes below.

Script Changes
Cleaner Code

I removed where you set the parent of the character’s animate script to disable it. There’s an enabled property for scripts, so you could just set it to true/false.

I also turned the nested if statements into one if statement.

But, I didn’t want to go overboard and basically rewrite your script in my style, so I kept everything mostly the same.

New Condition

I added a new condition (Humanoid.MoveDirection ~= Vector.zero) which prevents the player from diving if they aren’t moving.

Dive Cancel Problem

The first problem I noticed was that I could cancel the dive when jumping even though the jumpPower is set to 0. I think it’s because when you press space regardless of the jump power, it triggers the GettingUp state for the player. So, found a way to disable the jump action temporarily and added it in.

Dive Strength

I also wasn’t satisfied with the dive’s strength. It still didn’t look like it goes that far, and I tested out bigger numbers for the diveLength and height and it didn’t seem to affect the character.

So, I removed the diveHeight, and I applied another impulse with a diveStrength variable that’s set to 150. I think it made a big difference and it looks much more like the dive in FF.

The reason why I separated these two was because the impulse we had before was useful to point the character down, but not far. The impulse without the small offset helps the character go forward.

Dive Height

Edit: I removed diveHeight because I thought it didn’t affect anything. But, I fixed it and I put the code if you want to add it back. (note that I renamed diveLength to diveTurnStrength)

Basically, you needed to add the diveHeight with the look vector first before you multiply it with the diveTurnStrength.

HRP:ApplyImpulseAtPosition((HRP.CFrame.LookVector + Vector3.new(0,diveHeight,0)) * diveTurnStrength, HRP.Position + Vector3.new(0,1,0))
Jump Power

This isn’t in the script, but I also lowered the jump power to 45 to make it feel more accurate.

Less Bounciness

Each material has their own physical properties. But I found out you couldn’t just change one of its values, so had to create a new variable to override it. After that, you apply it do each of the character’s parts.

One of the constructors took in three variables for the density, friction, and elasticity. Each value goes from 0 to 1 so I set density to 1, friction to 0.15, and elasticity to 0. You might need to adjust the density and friction if it doesn’t feel right.

Resetting

Resetting the character will make the script not work anymore so I just added code where the character variables are reinitialized when the player dies.

Diving in Shift Lock (not implemented, but you should think about it)

While I was in shift lock, I noticed that you can’t dive to the side or backwards. It’s because of how we only use the LookVector which always makes the character dive forward.

Thinking about it, you could try to find a way to detect when the player is moving sideways or backwards and use the right vector. I’m not sure how to exactly do it yet, so I might update your if I find a solution.

Final Final Script
local Players = game:GetService("Players")
local UIS = game:GetService("UserInputService")
local ContextActionService = game:GetService("ContextActionService")

local debounce = false

local LocalPlayer = Players.LocalPlayer
local char = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait()

local HRP = char:WaitForChild("HumanoidRootPart")
local Humanoid = char:WaitForChild("Humanoid")

local diveTurnStrength = 50
local diveStrength = 150

local FREEZE_ACTION = "FreezeJump"

local physicalProperties = PhysicalProperties.new(1,0.15,0)

local function setPhysicalProperties()
	for _, part in char:GetChildren() do
		if part:IsA("Part") then
			part.CustomPhysicalProperties = physicalProperties
		end
	end
end

local function freezeJump()
	ContextActionService:BindAction(
		FREEZE_ACTION,
		function()
			return Enum.ContextActionResult.Sink
		end,
		false,
		Enum.PlayerActions.CharacterJump
	)
end

Humanoid.Died:Connect(function()
	char = LocalPlayer.CharacterAdded:Wait()

	HRP = char:WaitForChild("HumanoidRootPart")
	Humanoid = char:WaitForChild("Humanoid")
	
	setPhysicalProperties()
end)

UIS.InputBegan:Connect(function(input, IsChatting)
	if not IsChatting and input.KeyCode == Enum.KeyCode.E and not debounce and Humanoid and Humanoid.MoveDirection ~= Vector3.zero then
		debounce = true

		local oldWS = Humanoid.WalkSpeed -- getting their old ws incase its something like 34
		local oldJH = Humanoid.JumpHeight -- same as walk speed

		task.spawn(function() -- stop animations without holding up script
			for _, track in pairs(Humanoid.Animator:GetPlayingAnimationTracks()) do
				track:Stop()
			end
		end)

		local animateScript = char.Animate
		animateScript.Enabled = false
		
		freezeJump()
		
		HRP:ApplyImpulse(HRP.CFrame.LookVector * diveStrength)
		HRP:ApplyImpulseAtPosition(HRP.CFrame.LookVector * diveTurnStrength, HRP.Position + Vector3.new(0,1,0)) -- impulse
		
		Humanoid:ChangeState(Enum.HumanoidStateType.Ragdoll)

		Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false) -- disable jumping

		Humanoid.WalkSpeed = 0
		Humanoid.JumpHeight = 0

		Humanoid.PlatformStand = true -- immoblize, the part where they were halfway in the ground (previous reply)
		
		repeat task.wait() until Humanoid:GetState() == Enum.HumanoidStateType.GettingUp -- wait until player gets up

		while Humanoid.PlatformStand == true do -- setting to true and undoing it disables PlatformStand, since the player can't move when its on
			Humanoid.Sit = true
			task.wait()
			Humanoid.Sit = false
		end
		
		ContextActionService:UnbindAction(FREEZE_ACTION)

		Humanoid:ChangeState(Enum.HumanoidStateType.Running) -- change back to running to prevent any flinging

		animateScript.Enabled = true
		
		Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, true) -- add jumping
		
		Humanoid.WalkSpeed = oldWS -- ws and jh
		Humanoid.JumpHeight = oldJH
		
		task.wait(0.2) -- debounce
		debounce = false
	end
end)
2 Likes

Thank you so much for this!!! The customphysicalproperties worked great and it looks a lot smoother now!! I saw that you said that it doesn’t really work in shift lock when going to the side, so I used MoveDirection instead of LookVector. HRP:ApplyImpulseAtPosition(Humanoid.MoveDirection * diveTurnStrength, HRP.Position + Vector3.new(0,1,0)) It works the same as LookVector, just goes diagonal when in shift-lock, but let me know if you find a solution.

Update: I forgot to change the diveStrength ApplyImpulse to MoveDirection, it works good now!

2 Likes

Hey! Sorry for bothering you but I’ve been continously tweaking it to make the dive just how I want it and works really good!

Just one request, would there be any way to sort of freeze the player when they’re on the ground for a set amount of time, and THEN let them get back up? Like the original game? I tried using anchored but it didn’t look very smooth. None of the other DevForum posts helped out either.

About the shift-lock issue, when the player dives, I turned off the Humanoid’s AutoRotate property so it would stay in place (and not spaz out), and then re-enabled it when they got up. Going sideways still doesn’t work though, neither does backwards.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.