Hello, people of the devforum. I’m new to coroutines. This is my first script that I’ve attempted to use them in and I think I’m doing something wrong because my script doesn’t work properly, but there are no errors in the output.
Basically, I’m trying to revamp my old hitstun system with one that’s less buggy, because the old one would let random split-seconds of nostun slip through in the middle of a combo, which would result in unintuitive LMB-spam type fighting which I really didn’t like.
You can see in this video that after I hit the NPC, he gets stuck walking slowly and unable to attack me. This must mean that somewhere in my script something went wrong resuming his normal unstunned state, but I’m not sure what.
Here’s my code:
--// Variables
local char = script.Parent
local hum = char.Humanoid
local event = char.Hitstun
local stunval = char.Stunned
local anims = char.dmganims
--// Controls
local Time = 0
local doTick
--// Functions
function startStun()
stunval.Value = true
hum.WalkSpeed = 8
local stunanim = hum:LoadAnimation(anims["Damaged".. math.random(1,4)])
stunanim:Play()
end
function stopStun()
hum.WalkSpeed = 16
stunval.Value = false
doTick = false
end
--// Coroutine Functions
local timeTick = function()
repeat
if Time < .1 then
stopStun()
else
Time -= .1
end
task.wait(.1)
until doTick == false
end
local eventFired = function()
local tickthread = coroutine.wrap(timeTick)
Time = 1
doTick = true
startStun()
end
event.Event:Connect(coroutine.wrap(eventFired))
The goal here is to stun the NPC, making him slower and unable to attack for one second after he’s received a hit from any source. Each time the Hitstun event gets fired, that means he’s been hit by a source so I want the one second timer to restart when that happens.
For some context, this is a script inside the NPC that handles his stun. The Stunned value is where his attack and AI script go to ensure that he is unstunned, and capable of attacking. Dmganims is irrelevant.
Thanks to anyone who can help, any pointers you can give are seriously appreciated.
Oh! Sorry, move the Time = 1 line to immediately before the spawn(timeTick) function! You’re accessing a changed time variable before it gets changed to a workable time for the function.
This is what you mean, right? I think you might be on to something with moving the Time = 1 line, but I tested it and it still just results in no stun at all.
local eventFired = function()
Time = 1
spawn(timeTick)
doTick = true
startStun()
end
event.Event:Connect(spawn(eventFired))
To help debug, I put in some prints like so:
--// Variables
local char = script.Parent
local hum = char.Humanoid
local event = char.Hitstun
local stunval = char.Stunned
local anims = char.dmganims
--// Controls
local Time = 0
local doTick
--// Functions
function startStun()
print("character stunned")
stunval.Value = true
hum.WalkSpeed = 8
local stunanim = hum:LoadAnimation(anims["Damaged".. math.random(1,4)])
stunanim:Play()
end
function stopStun()
print("character stun stopped")
hum.WalkSpeed = 16
stunval.Value = false
doTick = false
end
--// Coroutine Functions
local timeTick = function()
print("time tick began")
repeat
if Time < .1 then
stopStun()
else
Time -= .1
end
task.wait(.1)
until doTick == false
end
local eventFired = function()
print("hitstun registered")
Time = 1
spawn(timeTick)
doTick = true
startStun()
end
event.Event:Connect(spawn(eventFired))
And it printed this before I even hit the NPC, and printed nothing every hit after that.
It doesn’t seem to care at all about when the event fires, and just does everything once regardless and never again. I don’t know why though, any ideas?
Sorry for the late response, I had gone to sleep when you responded. Reading through your code now on a full nights rest, I noticed that your timeTick function is relying on doTick to be true before entering into the function, which right now it is not. I would recommend moving the spawn(timeTick) to the bottom of the if function, and replacing the spawn() with task.spawn() as I have also learned that spawn() got deprecated and replaced with the new task.spawn() which is faster and more efficient.
Example of the if function:
local eventFired = function()
print("hitstun registered")
Time = 1
doTick = true
startStun()
task.spawn(timeTick)
end
This should fix it, as both doTick & Time must be set before entering a function that checks for them to be the opposite state. As for startStun(), it doesn’t need to be before it, but it should be before for just better efficiency + readability.
I went and tried this, and it resulted in the same issue with no stun happening at all. I tried making the same adjustments with the original coroutine.wrap version (only I made the local coroutine part global), and the same initial issue I was having returned.
With coroutine.wrap, when I hit the NPC the stun script doesn’t make it past stunning the character and it can only do it once. (I changed the print strings here to be simpler)
With task.spawn, it runs everything chronologically correctly, except with no regard for when the actual event it should be relying on is fired - instead it just runs it on spawn once and no matter how many times I hit the NPC, he doesn’t get stunned from it.
By the way, the information line in that image that references line 50 event.Event:Connect(spawn(eventFired)) does not have an error attached to it. I figured something was up with that line, but I just don’t know what it is, or how I should proceed.
Nevertheless, thanks for your help so far. I hope this gives some more clarity.
Oh, I see why. Remove the event.Event:Connect(spawn(eventFired)) and replace with event.Event:Connect(eventFired). I don’t think this will outright solve the problem however :Connect() tends to throw a fit when you call a function with arguments inside of it.