How to detect when an animation has ended roblox? - lua

I have a script designed to make an npc in my game do the default roblox idle animation but once Animation2 starts, it doesn't end, how can I fix this?
local idle1 = script.idle.Animation1
local idle2 = script.idle.Animation2
local animator = script.Parent.Humanoid.Animator
local LoadedIdle1 = animator:LoadAnimation(idle1)
local LoadedIdle2 = animator:LoadAnimation(idle2)
local function PlayAnimation()
local roll
local animation
roll = math.random(1, 10)
if roll == 1 then
animation = LoadedIdle2
LoadedIdle2:Play()
else
animation = LoadedIdle1
LoadedIdle1:Play()
end
animation.KeyframeReached:Connect(PlayAnimation)
end
PlayAnimation()
I tried adding animation.Ended:Connect(PlayAnimation) but that didn't fix it.

According to the documentation, there are a few events that an AnimationTrack will fire at different times.
KeyframeReached, which fires every time playback of an AnimationTrack reaches a Keyframe that does not have the default name - "Keyframe."
Stopped, which fires whenever the AnimationTrack finishes playing.
Ended, which fires when the AnimationTrack is completely done moving anything in the world. The animation has finished playing, the "fade out" is finished, and the subject is in a neutral pose.
DidLoop, which fires whenever a looped AnimationTrack completes a loop, on the next update.
It sounds like you want the Stopped event.
local function PlayAnimation()
local roll = math.random(1, 10)
local animation = if roll == 1 then LoadedIdle2 else LoadedIdle1
animation:Play()
animation.Stopped:Wait()
PlayAnimation()
end

Related

Music not muting when I press the GUI button but it will print the volume

local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local mutebutton = script.Parent
while true do
wait()
local b = game.StarterGui.MusicPlayer.Playlist:GetChildren()
local c = math.random(1, #b)
local d = b[c]
d:Play()
wait(d.TimeLength)
mutebutton.MouseButton1Click:Connect(function()
d.Volume = 0
end)
end
If i was to replace d.Volume = 0 with print(d.Volume) or print("testing") then it will work however when I change it to actually mute the audio, it dosent want to work. Anyone know why?
If you want to stop playing a Sound, I would suggest using the :Pause() method, like so:
mutebutton.MouseButton1Click:Connect(function()
d:Pause()
end)
The main problem with your script is that the MouseButton1Click:Connect (listening for clicks) is done only after the wait(d.TimeLength) (after the song has finished playing).
Adding on from ivy's suggestion of using d:Pause(), I would suggest doing something like this:
while true do
local b = game.StarterGui.MusicPlayer.Playlist:GetChildren()
local d = b[math.random(1, #b)]
d:Play()
local connection = mutebutton.MouseButton1Click:Connect(function()
if d.IsPaused then -- Makes the pause button a play button if the song is paused already
d:Resume()
else
d:Pause()
end
end)
d.Ended:Wait() -- Wait until music ends
connection:Disconnect() -- Remove connection (to prevent multiple functions on the same button click)
end

Is there a way to listen for any key press, and mute microphone?

I'm not familiar with LUA or hammerspoon, but I want to listen for any button on the keyboard being pressed.
I believe I can use hs.eventtap.event.newKeyEvent for this, but I'm not sure how to catch any and all key press. I don't care what is pressed, I just want to mute the microphone, and unmute it once there has been x number of seconds of no key being pressed.
Is this possible with hammerspoon? Please point me in the right direction.
I use the following to toggle my audio input devices between muted and unmuted.
function toggleMute()
local mic = hs.audiodevice.defaultInputDevice();
local state = not mic:muted()
hs.fnutils.each(hs.audiodevice.allInputDevices(), function(device)
device:setInputMuted(state)
end)
if mic:muted() then
hs.alert('Muted')
else
hs.alert('Unmuted')
end
end
local hyper = {"⌥", "⌃"}
hs.hotkey.bind(hyper, "m", function() toggleMute() end)
That will toggle between muted and unmuted when you press ⌥+⌃+m.
If you want to automatically mute after X seconds of no keyboard activity, then have a look at the hs.eventtap documentation.
https://www.hammerspoon.org/docs/hs.eventtap.event.html
You could set up a listener for key up (keyUp) or key down (key down) events.
keyboardTracker = hs.eventtap.new({ events.keyDown }, function (e)
...
end
keyboardTracker:start() // start monitoring the keyboard
keyboardTracker:stop() // stop monitoring the keyboard
To accomplish this:
create a timer for X seconds (see hs.timer)
start the timer
setup an event tap for the keyDown event type
start the event tap
every time you detect a key down event reset the timer
when the timer is triggered mute the audio input device and stop the timer
Once the timer has triggered it will mute the audio input devices and stop the timer. After that as soon as you press a key on the keyboard the timer will be restarted.
For example:
local timer = hs.timer.new(5, mute)
function mute()
hs.fnutils.each(hs.audiodevice.allInputDevices(), function(device)
device:setInputMuted(false)
end)
hs.alert('Muted')
timer:stop()
end
timer:start()
local events = hs.eventtap.event.types
keyboardTracker = hs.eventtap.new({ events.keyDown }, function (e)
timer:stop()
timer:start()
end)
keyboardTracker:start()
Reacting to mouse events is similar.

Lua Roblox API: How can I debounce a player/character that runs into a box

Background:
Bear minimal objects. Just a player and a part (rectangular prism) that's floating in the air and anchored.
I honestly don't understand what is happening under the hood so its hard to figure out. Without debounce, upon event firing, the callback function is invoked by connect() or the event handler (not sure), and, without debouncing, the function is invoked multiple times as print statements are repeated on the output box. So, with a variable (of type boolean) that stores a debounce flag, that solves it. I then try to "un-debounce" when the player's model is outside out of the box's model. But, I don't know how to do that properly.
Here's my code attempt:
local box = game.Workspace.BoxModel;
local debounce = false;
local function onTouchedDebounced()
if (debounce == false)
then
debounce = true;
print("Hello! onTouchedDebounced() has run.");
box.Touched:Connect(onTouchedDebounced));
end
end
local function onTouchedUndebounced()
if (debounce == true)
then
print("Hello! onTouchedUndebounced() has run.");
debounce = false;
end
end
box.Touched:Connect(onTouchedDebounced);
box.TouchEnded:Connect(onTouchedUndebounced);
The heart of what you're doing is sound : start blocking after the first event, and unblock some time later. If this were with button presses or mouse-clicks, your solution would work fine. This is complicated by the Touched event, as it fires with any part that touches it, and a player's character could have multiple touch points.
The Touched and TouchEndeded events give you a reference to the instance that has touched the part.
If the goal is to only fire the box event once per player or once while anyone is touching it, you can keep a dictionary of parts that are currently touching the box. When a part touches, you increment a counter. When a part stops, you decrement it. You only remove the debounced flag once all touch points have been removed.
local box = game.Workspace.BoxModel
local playersTouching = {} --<string playerName, int totalParts>
local function onTouchedDebounced(otherPart)
-- check that the thing that touched is a player
local playerModel = otherPart.Parent
if not playerModel:IsA("Model") then
warn(otherPart.Name .. " isn't a child of a character. Exiting")
return
end
-- check whether this player is already touching the box
local playerName = playerModel.Name
local total = playersTouching[playerName]
if total and total > 0 then
warn(playerName .. " is already touching the box")
playersTouching[playerName] = total + 1
return
end
-- handle a new player touching the box
playersTouching[playerName] = 1
-- Do a thing here that you only want to happen once per event...
print(string.format("Hello! onTouchedDebounced() has ran with %s and %s", playerName, otherPart.Name))
end
local function onTouchedUndebounced(otherPart)
-- decrement the counter for this player
local playerName = otherPart.Parent.Name
if playersTouching[playerName] == nil then
return
end
local newTotal = playersTouching[playerName] - 1
playersTouching[playerName] = newTotal
-- if the total is back down to zero, clear the debounce flag
if newTotal == 0 then
playersTouching[playerName] = nil
print(string.format("Hello! onTouchedUndebounced() has ran and %s has no more touching parts", playerName))
end
end
box.Touched:Connect(onTouchedDebounced)
box.TouchEnded:Connect(onTouchedUndebounced)

avplayer pause or stop notification based on the rate

I need to send notifications when the AVPlayer is Play/Paused and Stopped.
For play and pause below is the code
if (self.player.rate > 0.0f) {
NSLog(#" Playing ..")
}
if (self.player.rate == 0.0f) {
NSLog(#" Paused ..")
}
But for stopped also the rate = 0.0 then is there any other property or way to identify the difference between paused and stopped.
For both, paused and stopped the rate = 0.0 and hence need another way for it.
Thanks
There is no stop command for an AVPlayer. So there is no such thing as stopped as distinct from paused. Either the rate is zero (not playing) or it is more than zero (playing).
You can distinguish where the player is within its item (currentTime) so you can tell whether we are at the start, end, or middle; and you can arrange to be notified periodically during play or when the end is reached.
Apart from that, there are no distinctions to be drawn.

Corona Timer and memory issue

Say I am using timer in my game for example
timer.performWithDelay(1000, function() end, 1)
Is it necessary that I should assign this to an local variable and cancel after its use i.e,
local timerVar = timer.performWithDelay(1000, function() end, 1)
timer.cancel(timerVar)
timerVar = nil
Help me with this .....
If you don't expect the timer to be cancelled, you don't have to store it's reference. But I also recommend to not use anonymous function in timer. It's harder to debug then, because you'll not know the name of a function if it's throw an error.
the cancel() method is for stopping and removing the timer before it finishes. There's no need to call it after the timer is done.
i think you try this below.
local timerVar = timer.performWithDelay(1000, function(e)
timer.cancel(e.source)
e.source = nil
end, 1)

Resources