Smooth movement based on momentum based walkspeed

I’ve not been able to implement momentum based movement into my game because of my incredibly static movement system. Here’s a demonstration of my switching walking modes (This is a visual demo, not implementation. This controls walking movement in my game)

https://gyazo.com/ca404eca7beb0de522d13089f44738d5
https://gyazo.com/2722ec66e0e7d680d4a7a3a898a210dc

Notice when the walkspeed changes it gets very jittery. I also have specific modes set for each walk state. Something based off of walkspeed would be much more useful and would drastically improve the quality of my game. I have more walk modes than the ones shown (Stuff for aiming, stopped, walking, aimingwalking, etc etc)

I’d like to move away from this state based intensity setting, but I’m not sure where to begin. I’m not exactly a math expert. Any help would be heavily appreciated <3

local easing = require(script:WaitForChild("Easing"))
local UI = script.Parent:WaitForChild("ScreenGui")
local renderstepped = game:GetService('RunService').RenderStepped
local AnimTime = 2.3
local lastTick = tick()
local PercentPosition = 0
local lastpercent = PercentPosition

local MotionCode = nil
local MoveIntensity = 0
local MoveDuration = 1

function startBreathing()
	spawn(function()
		while renderstepped:wait() do
			--character.Properties.Walkspeed:update()
			--game.Players.LocalPlayer.Character:FindFirstChild("Humanoid").WalkSpeed = character.Properties.Walkspeed.position
			local ElapsedTime = (tick() - lastTick)/(MoveDuration * AnimTime)
			lastTick = tick()
			PercentPosition = PercentPosition + ElapsedTime
			local x = math.sin(math.rad((PercentPosition) * 360)) * 4
			local y = math.sin(math.rad((PercentPosition * 2) * 360)) * 4
			game.Workspace.Move.CFrame = game.Workspace.Core.CFrame * CFrame.new((x*MoveIntensity) * 2, (y*MoveIntensity) * 2,0)
	
			lastpercent = PercentPosition
			if PercentPosition >= 1 then
				PercentPosition = 0
			end
		end
	end)
end




function SetIntensity(newIntensity, newDuration)
	spawn(function()
		local NewCode = math.random(-1e9,1e9)
		MotionCode = NewCode
		local renderstepped = game:GetService('RunService').RenderStepped
		local startIntensity = MoveIntensity
		local startDuration = MoveDuration
		local distance = math.abs(startIntensity - newIntensity)
		local startTime = tick()
		local TotalTime = math.clamp(distance, 0, .2)
			
		if newIntensity ~= startIntensity then
			while renderstepped:wait() do
				local alpha = math.clamp((tick() - startTime) / TotalTime, 0, 1)
				if MotionCode ~= NewCode then
					break
				end
				MoveIntensity = easing.inQuad(alpha, startIntensity, newIntensity - startIntensity, 1)
				MoveDuration = easing.inQuad(alpha, startDuration, newDuration - startDuration, 1)
				if alpha >= 1 then
					break
				end
			end
		elseif startDuration ~= newDuration then
			local distance = math.abs(startDuration - newDuration)
			local TotalTime = distance/1.5
			TotalTime = math.clamp(TotalTime, 0, .2)
			while renderstepped:wait() do
				local alpha = math.clamp((tick() - startTime) / TotalTime, 0, 1)
				if MotionCode ~= NewCode then
					break
				end
				MoveDuration = easing.inQuad(alpha, startDuration, newDuration - startDuration, 1)
				if alpha >= 1 then
					break
				end
			end
		end
	end)
end

function setWalkspeed(speed)
	local S = speed
	if (S >= 1) and (S <= 10) then
		SetIntensity(.2,4)
	elseif (S > 10) and (S <= 19) then
		SetIntensity(.6,.5)
	elseif S > 19  then
		SetIntensity(1.8,.35)
	elseif (S < 1) then
		SetIntensity(.2,4)
	end
end

I have an opensource demo here if you want to experiment:

6 Likes

I am confused on what you want help with. The video shows a part flying around the place with the speed correlated with a “Walkspeed” value. What is the problem?

3 Likes

I mean i just wrote a codeblock on whats wrong with it

1 Like

The issue is that the “Move” part’s CFrame is a strict function of the “Core” part’s CFrame and the desired movement pattern. Since the Core part’s CFrame is constant, how far out the part is from the Core is only dependent upon the intensity and current alpha. For any alpha, the intensity controls how far out it is possible for the Move part to be from the Core part and bounds its movement. When the intensity decreases, it will always move the part inward as the possible movement space is restricted.

To make it smooth, you would have to allow the Move part to shift away from the Core part. For example if you decreased the intensity when the Move part was very far away from the Core part, the Move part would stay far away and perform its little movement there instead of returning. This could be used to make it travel anywhere. Concretely, this translates to having to store some state somewhere that represents the current offset of the movement. I recommend reversing the equations so that given a new intensity and current alpha, you can find where the center (Core) should be. As the intensity and alpha transition, change the Core’s CFrame to match where it should be for the intensity and alpha.

1 Like

I’m starting to see the problem.

The first image shows the path of the motion, and the blue point shows where the moving frame is:


The second image has the motion shown in dark-red, which would be the result of the walkspeed change:

I’ve drawn those really poorly, but they at least kinda show my point (pun intended). The Move object is trying to transfer from the lighter path to the darker one, which causes motion. As the darker path is longer, the particle would move faster under it than under the lighter path. That simultaneous motion would cause a jolt of speed.

In your case, the motion is from the darker to the lighter path. The tweening motion I explained earlier is now going against the direction of the path. Again, that simultaneous motion would cause a jolt of speed.