How to smoothly transition between M1 animations?

hey guys, lately I’ve been working on M1s for my fighting game, and I’ve encountered an animation transition issue. this is what I have right now:
(Watch knife m1 showcase 2 | Streamable)
(even in this clip, u can see a little bit of overlap error)

it has to do with animations with the same animation priority overlapping on each other, and it looks rlly weird when I play 2 at once. I have read abt how to stop and transition animations, but I’m not rlly sure on how to translate that into my code that I have right now (issue shown more clearly)
(Watch knife anim issue | Streamable)

block of code
local tool = script.Parent
local handle = tool:FindFirstChild("Handle")

local player = nil
local char = nil
local humanoid = nil
local animator = nil

local m1Folder = tool:FindFirstChild("M1s")
local m1Count = 0
local m1Debounce = false

local trail = handle:FindFirstChild("Trail")

tool.Equipped:Connect(function()
	char = tool.Parent
	player = game.Players:GetPlayerFromCharacter(char)
	humanoid = char:FindFirstChild("Humanoid")
	animator = humanoid:WaitForChild("Animator")
end)

tool.Activated:Connect(function()
	local m1Anims = {
		[1] = animator:LoadAnimation(m1Folder:FindFirstChild("1")),
		[2] = animator:LoadAnimation(m1Folder:FindFirstChild("2"))
	}
	
	--M1 logic
	local m1Min = 1
	local m1Max = 2
	
	if m1Debounce then
		print("debounce!")
		return
	end
	
	--THIS IS THE PART FOR THE ANIMATION LOGIC
	if m1Count < m1Max then	
		m1Count += 1
		m1Anims[m1Count]:Play(0.2)
		
	elseif m1Count == m1Max then
		m1Count = 1
		m1Anims[m1Count]:Play()
	end
	
	--First M1 events
	m1Anims[1]:GetMarkerReachedSignal("Stun"):Connect(function()
		m1Debounce = true
		humanoid.WalkSpeed = 8
		print("stunned!")
	end)
	
	m1Anims[1]:GetMarkerReachedSignal("Unstun"):Connect(function()
		m1Debounce = false
		humanoid.WalkSpeed = 16
		print("unstunned!")
	end)
	
	m1Anims[1]:GetMarkerReachedSignal("TrailStart"):Connect(function()
		trail.Enabled = true
	end)

	m1Anims[1]:GetMarkerReachedSignal("TrailEnd"):Connect(function()
		trail.Enabled = false
	end)
	
	--Second M1 events
	m1Anims[2]:GetMarkerReachedSignal("Stun"):Connect(function()
		m1Debounce = true
		humanoid.WalkSpeed = 8
		print("stunned!")
	end)
	
	m1Anims[2]:GetMarkerReachedSignal("Unstun"):Connect(function()
		m1Debounce = false
		humanoid.WalkSpeed = 16
		print("unstunned!")
	end)
end)

if anybody has any solutions, please help me out! thanks :happy2:

I know only a bit about animating, but I do know scripting. My first thought would be to add a script that plays a different animation if the player is walking, but I realized you can just remove the frames for the legs to not make the legs be bricks.

HAHA yeah, I knew about it but I haven’t fixed that part, thanks tho

You’re running into overlap because the M1 animations are playing at the same priority without properly stopping the previous one. You should always stop the last animation before playing the next one.

Don’t stack animations. Make sure each one fully stops before the next starts. That should fix the blending issue.

yep, I figured that was the case. I read about stopping animations and transitioning them, but I’m just not sure how to implement that into my code

this is what I tried writing, but sadly it didn’t work

if m1Count < m1Max then
	--I've tried writing logic for stopping it here, but not sure how to incorporate it
	if m1Count > 1 then
		m1Anims[m1Count - 1]:Stop(0.1)
	end
		
	m1Count += 1
	m1Anims[m1Count]:Play(0.2)
		
elseif m1Count == m1Max then
	m1Count = 1
	m1Anims[m1Count]:Play()
end

Yeah, you’re on the right track. The problem is that you’re not fully stopping the last animation every time before playing the next one.

Instead of just checking if m1Count > 1, always stop any currently playing M1 before starting a new one. You can store the last played animation in a variable and stop it:

if lastAnim then
    lastAnim:Stop(0.1)
end

lastAnim = m1Anims[m1Count]
lastAnim:Play(0.2)

That makes sure only one is playing at a time, and you avoid any overlap or blending.

AYOOO THANK U THIS APPROACH WORKED, I rewritten using ur idea and it actually works! although not smoothened yet, I’m happy this actually was possible

lastly, could u review my code to check if there’s any way I could optimize it? thanks so so much!

local tool = script.Parent
local handle = tool:FindFirstChild("Handle")

local player = nil
local char = nil
local humanoid = nil
local animator = nil

local m1Folder = tool:FindFirstChild("M1s")
local m1Count = 0
local m1Debounce = false
local lastAnim = nil

local trail = handle:FindFirstChild("Trail")

tool.Equipped:Connect(function()
	char = tool.Parent
	player = game.Players:GetPlayerFromCharacter(char)
	humanoid = char:FindFirstChild("Humanoid")
	animator = humanoid:WaitForChild("Animator")
end)

tool.Activated:Connect(function()
	--Debounce clause
	if m1Debounce then return end
	
	--M1 logic
	local m1Min = 1
	local m1Max = 2
	
	local m1Anims = {
		[1] = {
			["Track"] = animator:LoadAnimation(m1Folder:FindFirstChild("1")),
			["TransitionPos"] = 0
		},
		[2] = {
			["Track"] = animator:LoadAnimation(m1Folder:FindFirstChild("2")),
			["TransitionPos"] = 0.05
		}
	}

	if m1Count < 1 then
		lastAnim = m1Anims[2]
	end
	
	
	if m1Count < m1Max then
		--Checking last animation
		lastAnim["Track"]:Stop(0.05)
		
		m1Count += 1
		m1Anims[m1Count]["Track"]:Play(0.05)
		m1Anims[m1Count]["Track"].TimePosition = lastAnim["TransitionPos"]
		lastAnim = m1Anims[m1Count]
		
	elseif m1Count == m1Max then
		lastAnim["Track"]:Stop(0.05)

		m1Count = 1
		m1Anims[m1Count]["Track"]:Play(0.05)
		m1Anims[m1Count]["Track"].TimePosition = lastAnim["TransitionPos"]
		lastAnim = m1Anims[m1Count]
		
	end
	
	--First M1 events
	m1Anims[1]["Track"]:GetMarkerReachedSignal("Stun"):Connect(function()
		m1Debounce = true
		humanoid.WalkSpeed = 8
		print("stunned")
	end)
	
	m1Anims[1]["Track"]:GetMarkerReachedSignal("Unstun"):Connect(function()
		m1Debounce = false
		humanoid.WalkSpeed = 16
		print("unstunned")
	end)
	
	m1Anims[1]["Track"]:GetMarkerReachedSignal("TrailStart"):Connect(function()
		trail.Enabled = true
	end)

	m1Anims[1]["Track"]:GetMarkerReachedSignal("TrailEnd"):Connect(function()
		trail.Enabled = false
	end)
	
	--Second M1 events
	m1Anims[2]["Track"]:GetMarkerReachedSignal("Stun"):Connect(function()
		m1Debounce = true
		humanoid.WalkSpeed = 8
		print("stunned")
	end)
	
	m1Anims[2]["Track"]:GetMarkerReachedSignal("Unstun"):Connect(function()
		m1Debounce = false
		humanoid.WalkSpeed = 16
		print("unstunned")
	end)
end)

I’m glad it worked!

The script is functional overall, but if you want me to pinpoint some things I’d change…

You’re rebuilding the m1Anims table every time the tool is activated, which isn’t necessary. You should move that outside and just load the animations once when the tool is equipped. Same with the marker connections, they’re being set up on every click, which can cause the events to fire multiple times later on. Just connect them once right after loading the animations.

Also, your animation cycling logic can be simplified a bit to make it more readable and easier to scale if you add more animations later.

You’re doing great!

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