I’m trying to create Animations that gradually blend with the next Animation. I don’t know how to apply smooth transitioning for the Animations.
THIS IS A EXAMPLE:
ANIMATION SCRIPT:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local MODULES = ReplicatedStorage.MODULES
local EVENTS = ReplicatedStorage.EVENTS
local ANIMATIONS = ReplicatedStorage.ANIMATIONS
local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:FindFirstChildOfClass("Humanoid")
if not Humanoid or Humanoid.Name == "AI" then return end
local HRP = Character:WaitForChild("HumanoidRootPart")
local SPEED_CONFIGURATION = require(MODULES:WaitForChild("SPEED_CONFIG"))
-- PLAYER ANIMATIONS
local IDLE_ANIMATION = ANIMATIONS.PLAYER.IDLE
local WALK_ANIMATION = ANIMATIONS.PLAYER.WALK
local RUN_ANIMATION = ANIMATIONS.PLAYER.RUN
local JUMP_ANIMATION = ANIMATIONS.PLAYER.JUMP
-- PLAYER HURT ANIMATIONS
local HURT_IDLE__ANIMATION = ANIMATIONS.PLAYER.HURT.HURT_IDLE
local HURT_WALK_ANIMATION = ANIMATIONS.PLAYER.HURT.HURT_WALK
local HURT_RUN_ANIMATION = ANIMATIONS.PLAYER.HURT.HURT_RUN
local UpdatePhase_EVENT = EVENTS.SPEED:WaitForChild("UpdatePhase")
local currentPhase = 1
local currentAnimSpeed = 1
local targetAnimSpeed = 1
local tweenSpeedRate = 3
local Hurt = false
local hurtTimer = nil
local walkTrack, runTrack, idleTrack
local hurtIdleTrack, hurtWalkTrack, hurtRunTrack
local jumpTrack
local function GetTargetSpeed(phase)
return 1 + (phase - 1) * SPEED_CONFIGURATION.ANIMATION_SPEED_MULTIPLIER
end
local function PlayHurtAnimation()
if walkTrack.IsPlaying then walkTrack:Stop() end
if runTrack.IsPlaying then runTrack:Stop() end
if idleTrack.IsPlaying then idleTrack:Stop() end
local speed = HRP.Velocity
local horizontalSpeed = Vector3.new(speed.X, 0, speed.Z).Magnitude
local STANDSTILL_THRESHOLD = 0.1
if horizontalSpeed <= STANDSTILL_THRESHOLD then
if not hurtIdleTrack.IsPlaying then
hurtIdleTrack.TimePosition = 0
hurtIdleTrack:Play()
end
if hurtWalkTrack.IsPlaying then hurtWalkTrack:Stop() end
if hurtRunTrack.IsPlaying then hurtRunTrack:Stop() end
else
if currentPhase == 1 then
if not hurtWalkTrack.IsPlaying then
hurtWalkTrack.TimePosition = 0
hurtWalkTrack:Play()
end
if hurtIdleTrack.IsPlaying then hurtIdleTrack:Stop() end
if hurtRunTrack.IsPlaying then hurtRunTrack:Stop() end
else
if not hurtRunTrack.IsPlaying then
hurtRunTrack.TimePosition = 0
hurtRunTrack:Play()
end
if hurtIdleTrack.IsPlaying then hurtIdleTrack:Stop() end
if hurtWalkTrack.IsPlaying then hurtWalkTrack:Stop() end
end
end
end
local function UpdateMovementAnimation()
if not Humanoid or not Humanoid:IsDescendantOf(workspace) then return end
if Hurt then
PlayHurtAnimation()
return
end
if not walkTrack or not runTrack or not idleTrack then return end
targetAnimSpeed = GetTargetSpeed(currentPhase)
local speed = HRP.Velocity
local horizontalSpeed = Vector3.new(speed.X, 0, speed.Z).Magnitude
local STANDSTILL_THRESHOLD = 0.1
if horizontalSpeed <= STANDSTILL_THRESHOLD then
if walkTrack.IsPlaying then walkTrack:Stop() end
if runTrack.IsPlaying then runTrack:Stop() end
if not idleTrack.IsPlaying then
idleTrack.TimePosition = 0
idleTrack:Play()
end
else
if idleTrack.IsPlaying then idleTrack:Stop() end
if currentPhase == 1 then
if runTrack.IsPlaying then runTrack:Stop() end
if not walkTrack.IsPlaying then
walkTrack.TimePosition = 0
walkTrack:Play()
end
else
if walkTrack.IsPlaying then walkTrack:Stop() end
if not runTrack.IsPlaying then
runTrack.TimePosition = 0
runTrack:Play()
end
end
end
end
local function SetupAnimator()
if not Character or not Character:IsDescendantOf(workspace) then return end
Humanoid = Character:FindFirstChildOfClass("Humanoid")
if not Humanoid or Humanoid.Name == "AI" then return end
local Animator = Humanoid:FindFirstChildOfClass("Animator") or Humanoid:WaitForChild("Animator")
-- PLAYER ANIMATIONS SET-UP
walkTrack = Animator:LoadAnimation(WALK_ANIMATION)
runTrack = Animator:LoadAnimation(RUN_ANIMATION)
idleTrack = Animator:LoadAnimation(IDLE_ANIMATION)
jumpTrack = Animator:LoadAnimation(JUMP_ANIMATION)
walkTrack.Looped = true
runTrack.Looped = true
idleTrack.Looped = true
jumpTrack.Looped = false
-- PLAYER HURT ANIMATIONS SET-UP
hurtIdleTrack = Animator:LoadAnimation(HURT_IDLE__ANIMATION)
hurtWalkTrack = Animator:LoadAnimation(HURT_WALK_ANIMATION)
hurtRunTrack = Animator:LoadAnimation(HURT_RUN_ANIMATION)
hurtIdleTrack.Looped = true
hurtWalkTrack.Looped = true
hurtRunTrack.Looped = true
UpdateMovementAnimation()
Humanoid.StateChanged:Connect(function(oldState, newState)
if newState == Enum.HumanoidStateType.Jumping then
if walkTrack.IsPlaying then walkTrack:Stop() end
if runTrack.IsPlaying then runTrack:Stop() end
if idleTrack.IsPlaying then idleTrack:Stop() end
if hurtIdleTrack.IsPlaying then hurtIdleTrack:Stop() end
if hurtWalkTrack.IsPlaying then hurtWalkTrack:Stop() end
if hurtRunTrack.IsPlaying then hurtRunTrack:Stop() end
if not jumpTrack.IsPlaying then
jumpTrack.TimePosition = 0
jumpTrack:Play()
end
elseif newState == Enum.HumanoidStateType.Landed or newState == Enum.HumanoidStateType.Running or newState == Enum.HumanoidStateType.RunningNoPhysics then
if jumpTrack.IsPlaying then jumpTrack:Stop() end
if Hurt then
PlayHurtAnimation()
else
UpdateMovementAnimation()
end
end
end)
local lastHealth = Humanoid.Health
Humanoid.HealthChanged:Connect(function(newHealth)
if newHealth < lastHealth then
if hurtTimer then
hurtTimer:Cancel()
hurtTimer = nil
end
Hurt = true
PlayHurtAnimation()
hurtTimer = task.delay(SPEED_CONFIGURATION.HURT_DURATION, function()
Hurt = false
hurtTimer = nil
if hurtIdleTrack.IsPlaying then hurtIdleTrack:Stop() end
if hurtWalkTrack.IsPlaying then hurtWalkTrack:Stop() end
if hurtRunTrack.IsPlaying then hurtRunTrack:Stop() end
UpdateMovementAnimation()
end)
end
lastHealth = newHealth
end)
end
SetupAnimator()
Player.CharacterAdded:Connect(function(char)
Character = char
Humanoid = Character:WaitForChild("Humanoid")
HRP = Character:WaitForChild("HumanoidRootPart")
SetupAnimator()
end)
RunService.RenderStepped:Connect(function(dt)
if math.abs(currentAnimSpeed - targetAnimSpeed) > 0.01 then
currentAnimSpeed = currentAnimSpeed + (targetAnimSpeed - currentAnimSpeed) * math.clamp(tweenSpeedRate * dt, 0, 1)
if currentPhase == 1 and walkTrack and walkTrack.IsPlaying then
walkTrack:AdjustSpeed(currentAnimSpeed)
elseif currentPhase > 1 and runTrack and runTrack.IsPlaying then
runTrack:AdjustSpeed(currentAnimSpeed)
end
end
UpdateMovementAnimation()
end)
UpdatePhase_EVENT.Event:Connect(function(newPhase)
currentPhase = newPhase
UpdateMovementAnimation()
end)
1 Like
You could use inverse kinetics for that but its very hard to implement (i think)
THIS IS A SHOWCASE OF THE ISSUE:
1 Like
I’ve fixed the issue:
ANIMATION SCRIPT:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local MODULES = ReplicatedStorage.MODULES
local EVENTS = ReplicatedStorage.EVENTS
local SOUNDS = ReplicatedStorage.SOUNDS
local ANIMATIONS = ReplicatedStorage.ANIMATIONS
local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:FindFirstChildOfClass("Humanoid")
if not Humanoid or Humanoid.Name == "AI" then return end
local HRP = Character:WaitForChild("HumanoidRootPart")
local SPEED_CONFIGURATION = require(MODULES:WaitForChild("SPEED_CONFIG"))
-- PLAYER ANIMATIONS
local IDLE_ANIMATION = ANIMATIONS.PLAYER.IDLE
local WALK_ANIMATION = ANIMATIONS.PLAYER.WALK
local RUN_ANIMATION = ANIMATIONS.PLAYER.RUN
local JUMP_ANIMATION = ANIMATIONS.PLAYER.JUMP
-- PLAYER HURT ANIMATIONS
local HURT_IDLE__ANIMATION = ANIMATIONS.PLAYER.HURT.HURT_IDLE
local HURT_WALK_ANIMATION = ANIMATIONS.PLAYER.HURT.HURT_WALK
local HURT_RUN_ANIMATION = ANIMATIONS.PLAYER.HURT.HURT_RUN
local UpdatePhase_EVENT = EVENTS.SPEED:WaitForChild("UpdatePhase")
local Heartbeat_SOUND = SOUNDS.Heartbeat
local currentPhase = 1
local currentAnimSpeed = 1
local targetAnimSpeed = 1
local tweenSpeedRate = 3
local Hurt = false
local hurtTimer = nil
local walkTrack, runTrack, idleTrack
local hurtIdleTrack, hurtWalkTrack, hurtRunTrack
local jumpTrack
local currentPlayingTrack = nil
local currentHurtTrack = nil
local ANIMATION_SPEED = SPEED_CONFIGURATION.ANIMATION_SPEED
local OTHER_ANIMATION_SPEED = SPEED_CONFIGURATION.OTHER_ANIMATION_SPEED
local function CrossfadeAnimation(oldTrack, newTrack)
if newTrack == idleTrack then
if oldTrack and oldTrack.IsPlaying then
oldTrack:Stop(OTHER_ANIMATION_SPEED)
end
if newTrack then
newTrack:Play(OTHER_ANIMATION_SPEED)
end
else
if oldTrack and oldTrack.IsPlaying then
oldTrack:Stop(OTHER_ANIMATION_SPEED)
end
if newTrack then
newTrack:Play(OTHER_ANIMATION_SPEED)
end
end
end
local function PlayHurtAnimation()
if currentPlayingTrack and currentPlayingTrack.IsPlaying then
currentPlayingTrack:Stop(ANIMATION_SPEED)
currentPlayingTrack = nil
end
local speed = HRP.Velocity
local horizontalSpeed = Vector3.new(speed.X, 0, speed.Z).Magnitude
local STANDSTILL_THRESHOLD = 0.1
local newHurtTrack = nil
if horizontalSpeed <= STANDSTILL_THRESHOLD then
newHurtTrack = hurtIdleTrack
else
if currentPhase == 1 then
newHurtTrack = hurtWalkTrack
else
newHurtTrack = hurtRunTrack
end
end
if currentHurtTrack ~= newHurtTrack then
CrossfadeAnimation(currentHurtTrack, newHurtTrack)
currentHurtTrack = newHurtTrack
end
end
local function StopHurtAnimations()
if currentHurtTrack and currentHurtTrack.IsPlaying then
currentHurtTrack:Stop(ANIMATION_SPEED)
currentHurtTrack = nil
end
end
function TargetSpeed(phase)
return 1 + (phase - 1) * SPEED_CONFIGURATION.ANIMATION_SPEED_MULTIPLIER
end
local function UpdateMovementAnimation()
if not Humanoid or not Humanoid:IsDescendantOf(workspace) then return end
if Hurt then
PlayHurtAnimation()
return
end
if not walkTrack or not runTrack or not idleTrack then return end
targetAnimSpeed = TargetSpeed(currentPhase)
local speed = HRP.Velocity
local horizontalSpeed = Vector3.new(speed.X, 0, speed.Z).Magnitude
local STANDSTILL_THRESHOLD = 0.1
local newTrack = nil
if horizontalSpeed <= STANDSTILL_THRESHOLD then
newTrack = idleTrack
else
if currentPhase == 1 then
newTrack = walkTrack
else
newTrack = runTrack
end
end
if currentPlayingTrack ~= newTrack then
CrossfadeAnimation(currentPlayingTrack, newTrack)
currentPlayingTrack = newTrack
end
StopHurtAnimations()
end
local function SetupAnimator()
if not Character or not Character:IsDescendantOf(workspace) then return end
Humanoid = Character:FindFirstChildOfClass("Humanoid")
if not Humanoid or Humanoid.Name == "AI" then return end
local Animator = Humanoid:FindFirstChildOfClass("Animator") or Humanoid:WaitForChild("Animator")
-- PLAYER ANIMATIONS SET-UP
walkTrack = Animator:LoadAnimation(WALK_ANIMATION)
runTrack = Animator:LoadAnimation(RUN_ANIMATION)
idleTrack = Animator:LoadAnimation(IDLE_ANIMATION)
jumpTrack = Animator:LoadAnimation(JUMP_ANIMATION)
walkTrack.Looped = true
runTrack.Looped = true
idleTrack.Looped = true
jumpTrack.Looped = false
-- PLAYER HURT ANIMATIONS SET-UP
hurtIdleTrack = Animator:LoadAnimation(HURT_IDLE__ANIMATION)
hurtWalkTrack = Animator:LoadAnimation(HURT_WALK_ANIMATION)
hurtRunTrack = Animator:LoadAnimation(HURT_RUN_ANIMATION)
hurtIdleTrack.Looped = true
hurtWalkTrack.Looped = true
hurtRunTrack.Looped = true
currentPlayingTrack = nil
currentHurtTrack = nil
UpdateMovementAnimation()
Humanoid.StateChanged:Connect(function(oldState, newState)
if newState == Enum.HumanoidStateType.Jumping then
-- Stop all other tracks smoothly
if currentPlayingTrack and currentPlayingTrack.IsPlaying then
currentPlayingTrack:Stop(ANIMATION_SPEED)
currentPlayingTrack = nil
end
if currentHurtTrack and currentHurtTrack.IsPlaying then
currentHurtTrack:Stop(ANIMATION_SPEED)
currentHurtTrack = nil
end
if jumpTrack and not jumpTrack.IsPlaying then
jumpTrack.TimePosition = 0
jumpTrack:Play(0.2)
end
elseif newState == Enum.HumanoidStateType.Landed or
newState == Enum.HumanoidStateType.Running or
newState == Enum.HumanoidStateType.RunningNoPhysics then
if jumpTrack and jumpTrack.IsPlaying then
jumpTrack:Stop(0.2)
end
if Hurt then
PlayHurtAnimation()
else
UpdateMovementAnimation()
end
end
end)
local lastHealth = Humanoid.Health
local currentHeartbeatTween
local heartbeatStopTask
Humanoid.HealthChanged:Connect(function(newHealth)
if newHealth < lastHealth then
if hurtTimer then
task.cancel(hurtTimer)
hurtTimer = nil
end
Hurt = true
PlayHurtAnimation()
if Heartbeat_SOUND.IsPlaying then
Heartbeat_SOUND:Stop()
end
Heartbeat_SOUND.PlaybackSpeed = 0.8
Heartbeat_SOUND:Play()
if currentHeartbeatTween then
currentHeartbeatTween:Cancel()
end
local tweenInfo = TweenInfo.new(10, Enum.EasingStyle.Linear)
currentHeartbeatTween = TweenService:Create(Heartbeat_SOUND, tweenInfo, { PlaybackSpeed = 1.9 })
currentHeartbeatTween:Play()
if heartbeatStopTask then
task.cancel(heartbeatStopTask)
end
heartbeatStopTask = task.delay(10, function()
if not Hurt and Heartbeat_SOUND.IsPlaying then
Heartbeat_SOUND:Stop()
end
end)
hurtTimer = task.delay(SPEED_CONFIGURATION.HURT_DURATION, function()
Hurt = false
hurtTimer = nil
if Heartbeat_SOUND.IsPlaying then
Heartbeat_SOUND:Stop()
end
StopHurtAnimations()
UpdateMovementAnimation()
end)
end
lastHealth = newHealth
end)
end
SetupAnimator()
Player.CharacterAdded:Connect(function(char)
Character = char
Humanoid = Character:WaitForChild("Humanoid")
HRP = Character:WaitForChild("HumanoidRootPart")
SetupAnimator()
end)
RunService.RenderStepped:Connect(function(dt)
if currentPlayingTrack then
if math.abs(currentAnimSpeed - targetAnimSpeed) > 0.01 then
currentAnimSpeed = currentAnimSpeed + (targetAnimSpeed - currentAnimSpeed) * math.clamp(tweenSpeedRate * dt, 0, 1)
currentPlayingTrack:AdjustSpeed(currentAnimSpeed)
end
elseif currentHurtTrack then
if math.abs(currentAnimSpeed - targetAnimSpeed) > 0.01 then
currentAnimSpeed = currentAnimSpeed + (targetAnimSpeed - currentAnimSpeed) * math.clamp(tweenSpeedRate * dt, 0, 1)
currentHurtTrack:AdjustSpeed(currentAnimSpeed)
end
end
UpdateMovementAnimation()
end)
UpdatePhase_EVENT.Event:Connect(function(newPhase)
currentPhase = newPhase
UpdateMovementAnimation()
end)
1 Like
If you have found a solution, please mark this topic as solved.
1 Like