Need help getting this coroutine-based hit stun system to work

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.

5 Likes

Try coroutine.wrap(timeTick)()

1 Like

replace coroutine.wrap(function) with spawn(function) and do not make it a local variable. Ex:

local eventFired = function()
	spawn(timeTick)
	Time = 1
	doTick = true
	startStun()
end

event.Event:Connect(spawn(eventFired))
1 Like

This just causes the stun not to work at all.

1 Like

This, too, just results in no stun whatsoever.

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.

Hope this helps!

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)
image

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.

That did the trick. Thanks a lot!

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