Character jump with Bezier curve

I’m making this frog follow a bezier curve

Steps so far:

  1. Find the start, end, and middle points
  2. Generate x amount of waypoints
  3. Make character traverse waypoints

the problem right now is with step 3, I’ve tried regular cframing, alignposition, and tweening

tweening is the best solution so far, and is what I used in the video above
– tweening character

local function jump(jumpDistance, jumpHeight, jumpTime, wayPointCount)
	if jumping then return end
	jumping = true
	
	local tweenInfo = TweenInfo.new(jumpTime/wayPointCount, Enum.EasingStyle.Linear, Enum.EasingDirection.In)
	
	local startPosition = humanoidRootPart.Position
	local endPosition = humanoidRootPart.Position + humanoidRootPart.CFrame.LookVector * Vector3.new(jumpDistance, 1, jumpDistance)
	local ray = Ray.new(endPosition + Vector3.new(0, 50, 0), Vector3.new(0, -600, 0))
	local _, finalEndPosition = workspace:FindPartOnRayWithIgnoreList(ray, {character})
	finalEndPosition = finalEndPosition + Vector3.new(0, .5, 0)
	local middlePosition = startPosition:Lerp(finalEndPosition, .5) + Vector3.new(0, jumpHeight, 0)
	
	frogJumpAnimation:Play()
	frogJumpAnimation:AdjustSpeed(.5)
	
	local function getWayPoints()
		local wayPoints = {}
		for i = 1, wayPointCount do
			local alpha = i/wayPointCount
			local waypoint = (1 - alpha)^2 * startPosition + 2 * (1 - alpha) * alpha * middlePosition + alpha^2 * finalEndPosition
			wayPoints[i] = waypoint
		end
		return wayPoints
	end
	
	local wayPoints = getWayPoints()
	table.insert(wayPoints, finalEndPosition)
	
	if DEBUG_VISUALS then
		coroutine.wrap(function()
		for _, waypoint in ipairs(wayPoints) do
			local newDebugPart = debugPart:Clone()
			newDebugPart.Position = waypoint
			newDebugPart.Parent = workspace
			Debris:AddItem(newDebugPart, 4)
		end
		end)()
	end
	
	humanoidRootPart.Anchored = true
	frogWalkAnimation:Stop()
	
	for i = 1, #wayPoints do
		local waypoint = wayPoints[i]
		local x, y, z = humanoidRootPart.CFrame:ToOrientation()
		local goal = CFrame.new(waypoint) * CFrame.fromOrientation(x, y, z)
		local tween = TweenService:Create(humanoidRootPart, tweenInfo, {CFrame = goal})
		tween:Play()
		tween.Completed:Wait()
	end
	
	humanoidRootPart.Anchored = false
	frogJumpAnimation:Stop()
	jumping = false
end

The problem is that it isn’t 100% smooth with all speeds, and the jumpTime isn’t accurate, and I really need both

the jumpTime needs to be accurate so I can sync animations with the jump

is there a better way to do this?

BTW the code is a bit messy because I’m just trying to get this to work properly, and then I’m going to polish it

6 Likes

Can you further elaborate on what you mean by smooth and jump time not being accurate?

1 Like

The more waypoints the slower the jumping ends up, but it makes it smoother

local tweenInfo = TweenInfo.new(jumpTime/wayPointCount, Enum.EasingStyle.Linear, Enum.EasingDirection.In)

you can see here that I divided the jumpTime by the waypoints, so it evenly tweens, and I want the jump to be accurate

so if I put .4 for jumpTime I want the jump to actually be for .4 seconds start to finish, but it actually ends up elapsing for like 1 second if I have a lot of waypoints

I need near 100% accuracy here so I can play a animation over the duration of the jump

Have you tried linear interpolation?

Calculate which time you are at, find two points that this time falls in between, and calculate a point between those two points that exactly represents the current time.

For example, if you have a points table with 10 points in it, and your t is from 0 to 1, try this maybe? (untested, make your own tweaks!)

local i = math.min(math.ceil(t * 10), 9)
local firstPoint = points[i]
local secondPoint = points[i + 1]
local perfect = firstPoint:Lerp(secondPoint, i % (1/10))

Also, if you aren’t already, try using RenderStepped for the animation and calculating the time from the animation start there using tick(). That way, it will be smooth on the client.

2 Likes

Yeah I’ve already tried lerping and I had issues with it, I needed to have really small increments for it to appear smooth, and it also needed to be a really quick jump and the small increments were slowing it down too much (and renderstepped wasn’t quick enough in the loop)

and this is already 100% on client

That can’t possibly slow it down if you’re using RenderStepped. You would need to do a lot more to cause a slowdown doing that. If you’re using wait() in a loop though, there’s your problem.

Disclaimer: I haven’t read much of your code

Then use RenderStepped. There is no excuse not to. You are playing an animation. RenderStepped runs every frame.

1 Like

A lot of what I was doing unnecessary, rather than generating points and tweening/lerping between them I can generate waypoints and set the character’s position to each one in a renderstepped loop, and have less waypoints if I want it to be faster, and have more waypoints if I want it to be slower

there wasn’t a wait() slowing me down, but I was looping through waypoints, and then lerping the character in a loop towards the waypoint (like 10+ times ) which is what was slowing the loops down

I haven’t tested the animation part yet because I redid my system completely and haven’t gotten around to adding that but I assume the issue will be gone since it’s a lot faster now

You should only need to set the position once during RenderStepped… which is the position you should be at during that frame. Is that what you were doing?

That’s what I’m doing now

what I was doing before was like

for i = 1, #waypoints do
   local waypoint = waypoints[i]
   for i2 = 1, lerpIncrement do
      humanoidRootPart.Position = humanoidRootPart.Position:Lerp(waypoint, i2*lerpIncrement)
      RunService.RenderStepped:Wait()
   end
end

code is reproduced above, it isnt the exact code but it gets the point across, and don’t do what I did lol

what I’m doing now is basically generating all waypoint positions and looping through them in a renderstepped loop and setting the position to each waypoint

1 Like