Hi, i want to detect all players involved in the death of an NPC within the last 10 seconds (I think the quickest way is through the “CollectionService”(The intention is to be a big game, so the faster the detection happens, the better it will be))
I watched the “kill brick” and “Collection Service Door” tutorial a few times,and I read the CollectionService topic, but I can’t understand how it applies to finding a player who damaged the NPC.
I tried to use an ObjectValue as a “creator” tag, but when I do that I can’t find the player:
local Debris = game:GetService("Debris")
local Humanoid = script.Parent:WaitForChild("Humanoid")
local Players = game:GetService("Players")
Humanoid.ChildAdded:Connect(function (Child)
Humanoid.Died:Connect(function ()
if Child:IsA("ObjectValue") and Child.Name:lower() == "creator" then
local player = Child.Value
print(Child.Value)
print(Players:FindFirstChild(player, true).Status.XP.Value)
end
end)
if Child:IsA("ObjectValue") and Child.Name:lower() == "creator" then
Debris:AddItem(Child, 10)
end
end)
It detects my name, but I can’t find the “Status” setting in: game.Players.hungria.Status (this is the correct directory)
this is where the tag comes from:
function attacking(player)
local Character = player.Character
local Play1 = Character.Humanoid:LoadAnimation(animation1)
local Play2 = Character.Humanoid:LoadAnimation(animation2)
--attack
local Sword = Character:FindFirstChild(Mode.RightHandItem(player))
local Blade = Sword.Sword
--Hitbox
local NewHitbox = RaycastHitboxModule:Initialize(Blade, {Character} )
RaycastHitboxModule:DebugMode(true)
NewHitbox.OnHit:Connect(function(hit, humanoid)
local creator = Instance.new("ObjectValue", humanoid)
creator.Name = "creator"
creator.Value = player
local Damage = Mode.Strength(player) * Blade.Damage.Value
-- print(hit.Parent.Name)
humanoid:TakeDamage(Damage)
end)
Character.Humanoid.WalkSpeed = 0
If someone can help me, I’m about 8 hours into it and I’ve tried more things than I can remember
Sorry if the English is bad, done mostly by the translator
You could create a table and update that table with new entries each time the NPC is damaged, and include the time at which the damage happened using tick(). When the NPC dies, iterate over that table and compare each timestamp to the current time to determine if it happened within the last 10 seconds.
It doesn’t have to be a table, you could create a new NumberValue named to the UserId of the player who attacked with its value set to the time of attack each time the NPC is damaged, but creating a table is a far simpler solution.
Yes, it could, but my idea is to make the game processing be used as little as possible, since there will be many variables being triggered at certain times. asking the server to store users, the time it happened and then see which is less than 10 seconds since the last entry, does not seem very light, especially if there are 20 or more players killing enemies at the same time, and each player can kill more than one enemy at a time … so I want to understand how to use CollectionService for this, I think it will be much lighter
It is extremely light. There will be almost no performance overhead from storing a few bytes of data in a table and iterating over it; iterating over a table with only a few entries will literally take less than milliseconds. I just tested it on a table of over 40 entries. Even if we’re talking thousands of entries it has no performance overhead.
Even using CollectionService, you’d still have to iterate over a table of items with the tag you’re looking for.
The reason why you don’t get a Player Object in your first script you posted is that you are trying to do FindFirstChild with an Instance and not a string.
local Debris = game:GetService("Debris")
local Humanoid = script.Parent:WaitForChild("Humanoid")
local Players = game:GetService("Players")
Humanoid.ChildAdded:Connect(function (Child)
Humanoid.Died:Connect(function ()
if Child:IsA("ObjectValue") and Child.Name:lower() == "creator" then
local player = Child.Value
print(Child.Value) -- also player
print(player.Status.XP.Value)
end
end)
if Child:IsA("ObjectValue") and Child.Name:lower() == "creator" then
Debris:AddItem(Child, 10)
end
end)
What I changed is that instead of using FindFirstChild I get the player directly with the ObjectValue. Theoretically this should fix your problem.
oh, now it’s explained why it wasn’t working, Ty. what @vastqud suggested using the UserId also worked, but like I said, this path I used must be heavy
I do recommend checking in your script that creates the tags to destroy the previous tag from the player if there is already one to make sure there is always one, which should be the most recent one.
NewHitbox.OnHit:Connect(function(hit, humanoid)
for i,v in pairs(humanoid:GetChildren()) do
if v:IsA("ObjectValue") and v.Name == "creator" and v.Value == player then
v:Destroy()
end
end
local creator = Instance.new("ObjectValue")
creator.Parent = humanoid
creator.Name = "creator"
creator.Value = player
local Damage = Mode.Strength(player) * Blade.Damage.Value
humanoid:TakeDamage(Damage)
end)
Somewhat like this, iterate through all Instances, check if they are a ObjectValue, and if yes check if they match with player. There may be better solutions than this, but this should be sufficient for now.
using “For” and “in pairs”, or any other variation of it, is something difficult for me to understand, I don’t know why, I see it quite often, but I always forget how it works
for [...] do is called a loop. It executes the code X times inside it and there are multiple variants of it, it either allows you to count up/down from 0 to 10 (or reversed if counting down) or go through all values in a table. pairs/ipairs comes into the game when you want to iterate through a table (but there are other methods for it as well).
There is only one Instance in there, but you are not unbinding the previous connection, that’s why it gets called X times.
Try changing the code of the first script you sent to the following:
local Debris = game:GetService("Debris")
local Humanoid = script.Parent:WaitForChild("Humanoid")
local Players = game:GetService("Players")
Humanoid.ChildAdded:Connect(function (Child)
if Child:IsA("ObjectValue") and Child.Name:lower() == "creator" then
Debris:AddItem(Child, 10)
end
end)
Humanoid.Died:Connect(function()
for Index, Child in pairs(Humanoid:GetChildren()) do
if Child:IsA("ObjectValue") and Child.Name:lower() == "creator" then
local player = Child.Value
print(Child.Value)
print(Players:FindFirstChild(player, true).Status.XP.Value)
end
end
end)
What I did is move the Humanoid.Died Event outside of the ChildAdded Event, making it that it gets only called once, despite on how many Childs got added.
NewHitbox.OnHit:Connect(function(hit, humanoid)
if not humanoid:FindFirstChild(player.Name) then
local creator = Instance.new("ObjectValue")
creator.Parent = humanoid
creator.Name = "creator"
creator.Value = player
end
local Damage = Mode.Strength(player) * Blade.Damage.Value
humanoid:TakeDamage(Damage)
end)
local Debris = game:GetService("Debris")
local Humanoid = script.Parent:WaitForChild("Humanoid")
local Players = game:GetService("Players")
Humanoid.ChildAdded:Connect(function (Child)
if Child:IsA("ObjectValue") and Child.Name:lower() == "creator" then
Debris:AddItem(Child, 10)
end
end)
Humanoid.Died:Connect(function()
for Index, Child in pairs(Humanoid:GetChildren()) do
if Child:IsA("ObjectValue") then
local player = Child.Value
print(Child.Value)
print(Players:FindFirstChild(player, true).Status.XP.Value)
end
end
end)
I am unsure why the other attempt failed, but this one should work I hope.
Primary things I changed:
Instead of naming the ObjectValue to creator, I name it to the Players name and then check if it is already created, and if it isn’t, we create it.
My bad, I just noticed I forgot to change "creator" to player.Name.
NewHitbox.OnHit:Connect(function(hit, humanoid)
if not humanoid:FindFirstChild(player.Name) then
local creator = Instance.new("ObjectValue")
creator.Parent = humanoid
creator.Name = player.Name
creator.Value = player
end
local Damage = Mode.Strength(player) * Blade.Damage.Value
humanoid:TakeDamage(Damage)
end)
Sorry for wasting your time like this on that typo.
My bad, I goofed up once more and used the old code and not the new one in Humanoid.Died.
Humanoid.Died:Connect(function()
for Index, Child in pairs(Humanoid:GetChildren()) do
if Child:IsA("ObjectValue") then
local player = Child.Value
print(Child.Value)
print(player.Status.XP.Value)
end
end
end)