Better way to creating custom HipHeight?

I have a script that updates every frame to cast a ray down and see if there is a ground under them. If so, it would fire a remote event to the server to update the server position. But if not, it would lerp the character down on the client until it finds a ground.

My code is very laggy with 50 NPC’s raycasting down every frame and I want a better way of doing this.

Client:

local RunService = game:GetService("RunService")

local character = script.Parent.Parent
local HumanoidRootPart = character:WaitForChild("HumanoidRootPart")

local lerpCoroutine = nil
local lerpSpeed = 200

local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {character}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude

local function StopLerpCoroutine()
	if lerpCoroutine and coroutine.status(lerpCoroutine) ~= "running" then
		coroutine.close(lerpCoroutine)
		lerpCoroutine = nil
	end
end

local function GetGroundPosition()
	local HipHeight = character:GetAttribute("HipHeight")
	local rayOrigin = HumanoidRootPart.Position
	local rayDirection = Vector3.new(0, -HipHeight, 0)
	local raycast = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

	if raycast then
		local position = raycast.Position
		return position
	end
end

local function LerpToGround()
	StopLerpCoroutine()

	local origin = HumanoidRootPart.Position
	local oldPos = origin

	local foundGround = GetGroundPosition()

	if not foundGround then
		lerpCoroutine = coroutine.create(function()
			repeat
				origin = HumanoidRootPart.Position - Vector3.new(0, 1, 0)

				local distance = (oldPos - origin).Magnitude
				local timeToReachTarget = distance / lerpSpeed

				local alpha = math.clamp(timeToReachTarget, 0, 1)
				local lerp = oldPos:Lerp(origin, alpha)
				character:MoveTo(lerp)

				oldPos = HumanoidRootPart.Position
			until GetGroundPosition()

			local position = GetGroundPosition()

			if position then
				script.Parent.UpdateHeight:FireServer(position)
			end
		end)

		coroutine.resume(lerpCoroutine)
	else
		script.Parent.UpdateHeight:FireServer(foundGround)
	end
end

while true do
	LerpToGround()
	RunService.Heartbeat:Wait()
end

Server:

local character = script.Parent

script.UpdateHeight.OnServerEvent:Connect(function(_, position)
	local HipHeight = character:GetAttribute("HipHeight")
	local newPosition = position + Vector3.new(0, HipHeight, 0)
	character:MoveTo(newPosition)
end)

Video:

I got it to work!

Here is the code:

local RunService = game:GetService("RunService")

local character = script.Parent
local HumanoidRootPart = character:WaitForChild("Part")

local RayVisualizer = workspace:WaitForChild("RayVisualizer")

local lerpCoroutine = nil
local lerpSpeed = 200

local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {character}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude

local function StopLerpCoroutine()
	if lerpCoroutine and coroutine.status(lerpCoroutine) ~= "running" then
		coroutine.close(lerpCoroutine)
		lerpCoroutine = nil
	end
end

local function VisualizeRay(raycast)
	local origin = HumanoidRootPart.Position
	local distance = raycast.Distance
	local position = raycast.Position

	local newSize = Vector3.new(0.1, distance, 0.1)
	RayVisualizer.Size = newSize

	local newPos = position + Vector3.new(0, distance/2, 0)
	RayVisualizer.Position = newPos
end

local function GetGroundPosition()
	local HipHeight = character:GetAttribute("HipHeight")
	local size = HumanoidRootPart.Size.Y / 2

	local rayOrigin = HumanoidRootPart.Position
	rayOrigin -= Vector3.new(0, size, 0)

	local rayDirection = Vector3.new(0, -HipHeight, 0)
	local raycast = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

	if raycast then
		VisualizeRay(raycast)

		local position = raycast.Position
		return true, position
	end
end

local function LerpToGround()
	StopLerpCoroutine()

	local origin = HumanoidRootPart.Position
	local oldPos = origin

	local foundGround, position = GetGroundPosition()

	if not foundGround then
		origin = HumanoidRootPart.Position - Vector3.new(0, 10, 0)

		local distance = (oldPos - origin).Magnitude
		local timeToReachTarget = distance / lerpSpeed

		local alpha = math.clamp(timeToReachTarget, 0, 1)
		local lerp = oldPos:Lerp(origin, alpha)
		character:MoveTo(lerp)
	else
		local HipHeight = character:GetAttribute("HipHeight")
		local newPosition = position + Vector3.new(0, HipHeight, 0)
		character:MoveTo(newPosition)
	end
end

RunService.Heartbeat:Connect(function()
	LerpToGround()
end)
3 Likes

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