How To Go About In Making A NPC Swim?

Hello Devs, Im Making A game where NPC’s will be capable of swimming. But I have No Idea On How I should Go About In Making them be able to swim. So If anyone has any suggestions Please feel free to share, Thanks For Reading!

1 Like

Can’t you just use a NPC animation script, then use :MoveTo() to make the NPC go in the water and swim around?

2 Likes

This + I would probably change the NPC’s HRP density to make them sink or float

1 Like

I will give this a try although I was planning to use a swimmable part Thanks for the suggetion!

1 Like

You can use the same approach as you would with a player.

You can use a BodyGyro to control the rotation of the NPC, and a BodyVelocity to control the velocity.

If you want to make it more realistic, you can use a BodyAngularVelocity to control the angular velocity of the NPC, and a BodyForce to control the force applied to the NPC.

Here’s a quick example of a script that will make a character swim.

local character = script.Parent
local humanoid = character:FindFirstChildWhichIsA("Humanoid")

local swimming = false

local function startSwimming()
	swimming = true
	local bodyGyro = Instance.new("BodyGyro")
	bodyGyro.Parent = character.PrimaryPart
	bodyGyro.maxTorque = Vector3.new(0, math.huge, 0)
	bodyGyro.cframe = character.PrimaryPart.CFrame
	bodyGyro.cframe = bodyGyro.cframe * CFrame.Angles(0, math.pi / 2, 0)

	local bodyVelocity = Instance.new("BodyVelocity")
	bodyVelocity.Parent = character.PrimaryPart
	bodyVelocity.maxForce = Vector3.new(0, math.huge, 0)
	bodyVelocity.velocity = Vector3.new(0, 0, -10)
end

local function stopSwimming()
	swimming = false
	local bodyGyro = character.PrimaryPart:FindFirstChildWhichIsA("BodyGyro")
	if bodyGyro then
		bodyGyro:Destroy()
	end

	local bodyVelocity = character.PrimaryPart:FindFirstChildWhichIsA("BodyVelocity")
	if bodyVelocity then
		bodyVelocity:Destroy()
	end
end

local function onHumanoidDied(humanoid)
	stopSwimming()
end

local function onHumanoidJumping(humanoid)
	stopSwimming()
end

local function onHumanoidRunning(humanoid)
	stopSwimming()
end

local function onHumanoidSwimming(humanoid)
	startSwimming()
end

local function onHumanoidFreeFalling(humanoid)
	stopSwimming()
end

local function onHumanoidSeated(humanoid, active)
	stopSwimming()
end

local function onHumanoidClimbing(humanoid, active)
	stopSwimming()
end

local function onHumanoidGettingUp(humanoid)
	stopSwimming()
end

local function onHumanoidFallingDown(humanoid)
	stopSwimming()
end

local function onHumanoidStateChanged(humanoid, state)
	if state == Enum.HumanoidStateType.Dead then
		onHumanoidDied(humanoid)
	elseif state == Enum.HumanoidStateType.Jumping then
		onHumanoidJumping(humanoid)
	elseif state == Enum.HumanoidStateType.Running then
		onHumanoidRunning(humanoid)
	elseif state == Enum.HumanoidStateType.Swimming then
		onHumanoidSwimming(humanoid)
	elseif state == Enum.HumanoidStateType.Freefall then
		onHumanoidFreeFalling(humanoid)
	elseif state == Enum.HumanoidStateType.Seated then
		onHumanoidSeated(humanoid, true)
	elseif state == Enum.HumanoidStateType.Climbing then
		onHumanoidClimbing(humanoid, true)
	elseif state == Enum.HumanoidStateType.GettingUp then
		onHumanoidGettingUp(humanoid)
	elseif state == Enum.HumanoidStateType.FallingDown then
		onHumanoidFallingDown(humanoid)
	end
end

local function onHumanoidChangedState(humanoid, state)
	if state == Enum.HumanoidStateType.Seated then
		onHumanoidSeated(humanoid, false)
	elseif state == Enum.HumanoidStateType.Climbing then
		onHumanoidClimbing(humanoid, false)
	end
end

local function onHumanoidAdded(newHumanoid)
	humanoid = newHumanoid
	humanoid.StateChanged:Connect(function(state)
		onHumanoidStateChanged(humanoid, state)
	end)
	humanoid.ChangedState:Connect(function(state)
		onHumanoidChangedState(humanoid, state)
	end)
end

if humanoid then
	onHumanoidAdded(humanoid)
else
	character.ChildAdded:Connect(function(child)
		if child:IsA("Humanoid") then
			onHumanoidAdded(child)
		end
	end)
end
1 Like

I’ve made a swimmable part not too long ago.
Create a ServerScript inside the NPC and paste the following code:

local runService = game:GetService("RunService")

local character = script.Parent
local head = character:WaitForChild("Head")

local humanoid = character:FindFirstChildWhichIsA("Humanoid")
local rootpart = humanoid.RootPart

local isSwimming = false
local toFind = "Water"

local currentBody = nil

humanoid.UseJumpPower = true

function swimIn(hit)
	if hit:IsA("BasePart") and string.find(hit.Name, toFind) then
		if not isSwimming then
			isSwimming = true
			currentBody = hit

			local swimSupport = Instance.new("BodyVelocity")
			swimSupport.Name = "SwimSupport"
			swimSupport.P = game.Workspace.Gravity * rootpart.Mass / 3
			swimSupport.MaxForce = Vector3.new(4000, 5000, 4000)
			swimSupport.Parent = rootpart

			humanoid.JumpPower = 0
			humanoid:SetStateEnabled(Enum.HumanoidStateType.GettingUp, false)
		end
	end
end

function swimOut(hit)
	if hit:IsA("BasePart") and string.find(hit.Name, toFind) then
		if isSwimming then
			isSwimming = false
			currentBody = nil

			if rootpart:FindFirstChild("SwimSupport") then
				rootpart:WaitForChild("SwimSupport"):Destroy()
			end

			humanoid.JumpPower = 50
			humanoid:ChangeState(Enum.HumanoidStateType.Jumping)			
		end
	end
end

function onJump()
	if isSwimming then
		local swimSupport = rootpart:WaitForChild("SwimSupport")
		local floatVelocity = Vector3.new(0, 3, 0)

		if swimSupport.Velocity == floatVelocity then
			swimSupport.Velocity = Vector3.new(0, humanoid.WalkSpeed + 2, 0)
		end

		if rootpart:FindFirstChild("SwimFloat") then
			rootpart:WaitForChild("SwimFloat"):Destroy()
		end
	end
end

function handleSwimming(delta)
	if isSwimming then
		local swimSupport = rootpart:WaitForChild("SwimSupport")			
		swimSupport.Velocity = humanoid.MoveDirection * humanoid.WalkSpeed

		if swimSupport.Velocity == Vector3.new(0, 0, 0) then
			local surfaceY = currentBody.Position.Y + currentBody.Size.Y / 2 - 0.5
			swimSupport.Velocity = Vector3.new(0, 3, 0)

			task.delay(0.2, function()
				if head.Position.Y >= surfaceY - 2 then
					if not rootpart:FindFirstChild("SwimFloat") then
						local swimFloat = Instance.new("BodyPosition")
						swimFloat.Name = "SwimFloat"
						swimFloat.P = 4000
						swimFloat.MaxForce = Vector3.new(0, 8000, 0)
						swimFloat.Position = Vector3.new(0, surfaceY, 0)
						swimFloat.Parent = rootpart
					end
				end
			end)
		else
			if rootpart:FindFirstChild("SwimFloat") then
				rootpart:WaitForChild("SwimFloat"):Destroy()
			end
		end

		humanoid:ChangeState(Enum.HumanoidStateType.Swimming)
	else
		if rootpart:FindFirstChild("SwimFloat") then
			rootpart:WaitForChild("SwimFloat"):Destroy()
		end
	end
end

head.Touched:connect(swimIn)
head.TouchEnded:connect(swimOut)

runService.Heartbeat:connect(handleSwimming)
humanoid:GetPropertyChangedSignal("Jump"):connect(onJump)

And yes, I did modify it to only work for NPC’s
Feel free to use! :grin:

You should just be able to use Pathfinding and Pathfinding Modifiers to get NPCs to swim wherever you want.