local function animateGemCount(oldamount, newAmount)
local gemText = script.Parent.Amount
local duration = 1 -- Total duration for the count-up animation
local increments = newAmount - oldamount -- Number of increments (differences between old and new amount)
-- Each increment will take this amount of time to complete
local incrementDuration = duration / increments
local currentValue = oldamount
-- Loop until we reach the newAmount
for i = 1, increments do
currentValue = currentValue + 1 -- Increment by 1
gemText.Text = tostring(currentValue) -- Update the text
-- Wait for the duration of each increment (so the total duration is 1 second)
task.wait(incrementDuration)
end
end
but it takes longer than the set duration and is most likely laggy. Any help is appreciated
local Module = {}
function Module.UrdateText(Gui: TextLabel, goal: number)
local actualValue = tonumber(GUI.Text)
local time = (actualValue / goal) * 0.5
coroutine.wrap(function()
for v = actualValue, goal, 10 do
Gui.Text = goal
task.wait(time)
end
end)()
end
return Module
The reason this takes longer is because task.wait has a minimum wait time.
Here is how I would code this:
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local displayLabel = script.Parent
local player = Players.LocalPlayer
-- Write a function to get the most up-to-date gem count
function getGemCount()
-- TODO: Write a function to get your gem counts. This is an example:
local leaderstats = player:FindFirstChild("leaderstats")
if not leaderstats then return 0 end
local gems = leaderstats:FindFirstChild("Gems")
if not gems then return 0 end
return gems.Value
end
-- Write a function to convert a gem count to text for the label
local function countToLabelText(gemCount)
-- TODO: make a function to match how you want the text to look
return "+"..tostring(gemCount)
end
local animationDuration = 1 -- It takes one second to finish the animation
local lastChangeTime = tick()
local lastGemCount = getGemCount()
local lastChangeGemCount = lastGemCount
local displayedGemCount = lastGemCount
-- Update the text every heartbeat (this is totally fine in terms of performance)
RunService.Heartbeat:Connect(function()
local gemCount = getGemCount()
local currentTime = tick()
if lastGemCount ~= gemCount then
lastChangeTime = currentTime
lastChangeGemCount = displayedGemCount
end
-- Animate between the `lastChangeGemCount` and `gemCount` based on the time since the last change and the `animationDuration`
local animationPercent = (currentTime - lastChangeTime) / animationDuration
animationPercent = math.min(animationPercent, 1) -- Finish animating once the `animationDuration` is reached
displayedGemCount = lastChangeGemCount + (gemCount - lastChangeGemCount) * animationPercent
displayedGemCount = math.round(displayedGemCount) -- Round to a whole number
displayedGemCount = math.min(displayedGemCount, gemCount) -- Make sure not to show gems above the actual count
displayLabel.Text = countToLabelText(displayedGemCount)
lastGemCount = gemCount
end)
local function animateGemCount(goal: number)
local flip = true
local actualValue = tonumber(script.Parent.Amount.Text)
local difference = goal - actualValue
local waitTime = 0.005 -- that means you need this times 200 to reach our goal of 1 sec
local increment = math.floor(difference / 200)
coroutine.wrap(function()
for v = actualValue, goal, increment do
script.Parent.Amount.Text = v
if flip then
flip = false
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
else
flip = true
script.Parent.Amount.Size = UDim2.new(0.5, 0,0.67, 0)
end
task.wait(waitTime)
end
end)()
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
script.Parent.Amount.Text = goal
end
I would recommend thinking about potential overlapping loops. For example, if two calls are made to animateGemCount, there would probably be two loops trying to modify the text at once but to different values.
That is a good fix though other than that! If you know you won’t be calling animate within waitTime * 200 it seems like that should work!
The local function ONLY gets called when it’s called by a remote event. This only happens when you get a gem reward and you can logically only get one at a time in my game. I could add a debounce tho so it doesn’t happen
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local displayLabel = script.Parent
local player = Players.LocalPlayer
-- Write a function to get the most up-to-date gem count
function getGemCount()
return tonumber(script.Parent.Amount.Text)
end
-- Write a function to convert a gem count to text for the label
local function countToLabelText(gemCount)
return tostring(gemCount)
end
local animationDuration = 1 -- It takes one second to finish the animation
local flipsPerSecond = 15 -- How many times to flip the size per second
local lastChangeTime = tick()
local lastGemCount = getGemCount()
local lastChangeGemCount = lastGemCount
local displayedGemCount = lastGemCount
-- Update the text every heartbeat (this is totally fine in terms of performance)
RunService.Heartbeat:Connect(function()
local gemCount = getGemCount()
local currentTime = tick()
if lastGemCount ~= gemCount then
lastChangeTime = currentTime
lastChangeGemCount = displayedGemCount
end
-- Animate between the `lastChangeGemCount` and `gemCount` based on the time since the last change and the `animationDuration`
local animationPercent = (currentTime - lastChangeTime) / animationDuration
animationPercent = math.min(animationPercent, 1) -- Finish animating once the `animationDuration` is reached
displayedGemCount = lastChangeGemCount + (gemCount - lastChangeGemCount) * animationPercent
displayedGemCount = math.round(displayedGemCount) -- Round to a whole number
displayedGemCount = math.min(displayedGemCount, gemCount) -- Make sure not to show gems above the actual count
displayLabel.Text = countToLabelText(displayedGemCount)
-- Do the flipping:
if displayedGemCount == gemCount then
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
else
-- Flip every `1 / flipsPerSecond` seconds
if currentTime % (1 / flipsPerSecond * 2) > 1 / flipsPerSecond then
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
else
script.Parent.Amount.Size = UDim2.new(0.5, 0,0.67, 0)
end
end
lastGemCount = gemCount
end)
currently in my script im calling a local function "animateGemCount(currentGemValue) " and providing the changed gem value. Your version seems to “always” run because of the run service?
just confused on how your version works
It basically uses one loop to update the value. It’s just like your code basically but it never stops and it doesn’t ever make multiple loops. It’s fine for performance because it’s event based and the amount of processing is absolutely tiny (e.g. each Humanoid has dozens of loops like that internally).
The RunService lets you get an event that runs every update cycle. Basically the code in the function connected to Heartbeat runs like code in a loop.
Ah wait. Sorry, there is a problem. Let me fix it. (The problem is that the loop uses the “real” value as the text label but is also updating the text label.)
Now you can use your animateGemCount function to animate the text.
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local displayLabel = script.Parent
local player = Players.LocalPlayer
local realGemCount = 0
local function animateGemCount(goal: number)
realGemCount = goal
end
-- Write a function to get the most up-to-date gem count
function getGemCount()
return realGemCount
-- Write a function to convert a gem count to text for the label
local function countToLabelText(gemCount)
return tostring(gemCount)
end
local animationDuration = 1 -- It takes one second to finish the animation
local flipsPerSecond = 15 -- How many times to flip the size per second
local lastChangeTime = tick()
local lastGemCount = getGemCount()
local lastChangeGemCount = lastGemCount
local displayedGemCount = lastGemCount
-- Update the text every heartbeat (this is totally fine in terms of performance)
RunService.Heartbeat:Connect(function()
local gemCount = getGemCount()
local currentTime = tick()
if lastGemCount ~= gemCount then
lastChangeTime = currentTime
lastChangeGemCount = displayedGemCount
end
-- Animate between the `lastChangeGemCount` and `gemCount` based on the time since the last change and the `animationDuration`
local animationPercent = (currentTime - lastChangeTime) / animationDuration
animationPercent = math.min(animationPercent, 1) -- Finish animating once the `animationDuration` is reached
displayedGemCount = lastChangeGemCount + (gemCount - lastChangeGemCount) * animationPercent
displayedGemCount = math.round(displayedGemCount) -- Round to a whole number
displayedGemCount = math.min(displayedGemCount, gemCount) -- Make sure not to show gems above the actual count
displayLabel.Text = countToLabelText(displayedGemCount)
-- Do the flipping:
if displayedGemCount == gemCount then
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
else
-- Flip every `1 / flipsPerSecond` seconds
if currentTime % (1 / flipsPerSecond * 2) > 1 / flipsPerSecond then
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
else
script.Parent.Amount.Size = UDim2.new(0.5, 0,0.67, 0)
end
end
lastGemCount = gemCount
end)
TBH I think you might just want to use a tween . They’re nice because they cancel themselves.
local tweenValue = -- IntValue
local function animateGemCount(goal)
tweenValue.Value = tostring(gemCount)
tween = -- Todo: Make a tween for tweenValue.Value to goal
local connection
connection = tweenValue.Changed:Connect(function()
displayLabel.Text = tostring(tweenValue.Value)
-- Flipping
if tweenValue.Value % 2 == 0 then
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
else
script.Parent.Amount.Size = UDim2.new(0.5, 0,0.67, 0)
end
-- End the connection when the end is reached
if tween.PlaybackState ~= Enum.PlaybackState.Playing then
connection:Disconnect()
tween:Destroy()
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
end
end
end
It sets a variable that the continuous loop then looks at and updates based on.
The tweening code might be easier though:
local tweenValue = Instance.new("IntValue")
local animationDuration = 1
local function animateGemCount(goal)
-- Tween from the current displayed value to goal
tweenValue.Value = tostring(script.Parent.Amount.Text)
tween = TweenService:Create(tweenValue, TweenInfo.new(animationDuration), {Value = goal})
-- Update the label every time the tween updates
local valueConnection
valueConnection = tweenValue.Changed:Connect(function()
-- Update the text
displayLabel.Text = tostring(tweenValue.Value)
-- Flip between even and odd numbers
if tweenValue.Value % 2 == 0 then
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
else
script.Parent.Amount.Size = UDim2.new(0.5, 0,0.67, 0)
end
end
-- End the connection when the end is reached
local endConnection
endConnection = tween.Completed:Connect(function()
endConnection:Disconnect()
valueConnection:Disconnect()
tween:Destroy()
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
end)
-- Start the animation
tween:Play()
end
Sorry, I might have over complicated things. The tween code matches the coding pattern you were using.
local tweenValue = Instance.new("IntValue")
local animationDuration = 1
local function animateGemCount(goal)
tweenValue.Value = tostring(script.Parent.Amount.Text)
local tween = TweenService:Create(tweenValue, TweenInfo.new(animationDuration), {Value = goal})
local connection
connection = tweenValue.Changed:Connect(function()
script.Parent.Amount.Text = tostring(tweenValue.Value)
if tweenValue.Value % 2 == 0 then
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
else
script.Parent.Amount.Size = UDim2.new(0.5, 0,0.67, 0)
end
if tween.PlaybackState ~= Enum.PlaybackState.Playing then
connection:Disconnect()
tween:Destroy()
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
end
end)
tween:Play()
end
local tweenValue = Instance.new("IntValue")
local animationDuration = 1
local function animateGemCount(goal)
-- Tween from the current displayed value to goal
tweenValue.Value = tostring(script.Parent.Amount.Text)
tween = TweenService:Create(tweenValue, TweenInfo.new(animationDuration), {Value = goal})
-- Update the label every time the tween updates
local valueConnection
valueConnection = tweenValue.Changed:Connect(function()
-- Update the text
displayLabel.Text = tostring(tweenValue.Value)
-- Flip between even and odd numbers
if tweenValue.Value % 2 == 0 then
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
else
script.Parent.Amount.Size = UDim2.new(0.5, 0,0.67, 0)
end
end
-- End the connection when the end is reached
local endConnection
endConnection = tween.Completed:Connect(function()
endConnection:Disconnect()
valueConnection:Disconnect()
tween:Destroy()
script.Parent.Amount.Size = UDim2.new(0.472, 0,0.625, 0)
end)
-- Start the animation
tween:Play()
end