ParticleEmitter on cloned attachment only shows after shard falls

So I’m making a projectile ability where I clone an Attachment with a ParticleEmitter from ReplicatedStorage and parent it to a moving Part (a shard). The problem is: the particle emitter doesn’t show immediately. It only starts showing once the shard starts falling.

I’ve tried:

  • task.wait()
  • task.spawn + task.wait(0)
  • setting Enabled = true
  • emitting more particles

Nothing works. I attached a video showing the issue.

Here’s my code:

local ArcaneFlick = {}

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Debris = game:GetService("Debris")
local CollectionService = game:GetService("CollectionService")
local TweenService = game:GetService("TweenService")

local ABILITY_COOLDOWN = script:GetAttribute("Cooldown") or 5
local CHARACTER_NAME_REQUIRED = "Street Sorcerer"
local MIN_ABILITY_COOLDOWN = 5
local SHARD_SPEED = 120
local MAX_DISTANCE = 30
local BASE_DAMAGE = 10

local STREET_SORCERER = ReplicatedStorage:WaitForChild("Abilties"):WaitForChild("Street Sorcerer")
local ANIM_FOLDER = STREET_SORCERER:WaitForChild("Animations")
local VFX_FOLDER = STREET_SORCERER:WaitForChild("VFX").ArcaneFlick
local SOUND_FOLDER = STREET_SORCERER:WaitForChild("Sounds").ArcaneFlick

local ANIMATION = ANIM_FOLDER:WaitForChild("ArcaneFlick") :: Animation
local SHARD_VFX_ATTACHMENT_TEMPLATE = VFX_FOLDER:WaitForChild("Shard") :: Attachment
local IMPLOSION_VFX_ATTACHMENT_NAME = VFX_FOLDER:WaitForChild("Implosion") :: Attachment
local SHOOT_SOUND = SOUND_FOLDER:WaitForChild("Shoot") :: Sound

local lastAbilityTime = {}

local function toggleParticleEmitters(attachment: Attachment, emitCount: number?)
	for _, child in ipairs(attachment:GetDescendants()) do
		if child:IsA("ParticleEmitter") then
				child:Emit(emitCount or 50)
			end
		end
end

function ArcaneFlick.Trigger(player: Player, targetPos: Vector3)
	local character = player.Character
	if not character or not character:FindFirstChild("HumanoidRootPart") then return end
	local humanoid = character:FindFirstChildOfClass("Humanoid")
	local hrp = character.HumanoidRootPart
	local animator = humanoid and humanoid:FindFirstChildOfClass("Animator")
	local rHand = character:FindFirstChild("RightHand") or character:FindFirstChild("Right Arm")
	if not animator or not rHand then return end
	if character:GetAttribute("Character") ~= CHARACTER_NAME_REQUIRED then return end

	-- Cooldown
	local cooldownReduction = player:GetAttribute("CooldownReduction") or 0
	local effectiveCooldown = math.max(MIN_ABILITY_COOLDOWN, ABILITY_COOLDOWN * (1 - cooldownReduction))
	if lastAbilityTime[player.UserId] and (tick() - lastAbilityTime[player.UserId] < effectiveCooldown) then return end
	lastAbilityTime[player.UserId] = tick()

	-- Animation
	local animTrack = animator:LoadAnimation(ANIMATION)
	animTrack:Play()
	animTrack:GetMarkerReachedSignal("Shoot"):Wait()

	-- Shoot sound
	if SHOOT_SOUND then
		local s = SHOOT_SOUND:Clone()
		s.Parent = rHand
		s:Play()
		Debris:AddItem(s, 2)
	end

	-- Implosion VFX
	local explodeVFX = IMPLOSION_VFX_ATTACHMENT_NAME:Clone()
	explodeVFX.Parent = rHand
	toggleParticleEmitters(explodeVFX, true, 20)
	Debris:AddItem(explodeVFX, 1)

	-- Create shard and physics
	local shardPart = Instance.new("Part")
	shardPart.Name = "ArcaneShard"
	shardPart.Size = Vector3.new(1,1,1)
	shardPart.Transparency = 0.6
	shardPart.Anchored = false
	shardPart.Massless = true
	shardPart.CanCollide = true
	shardPart.CFrame = CFrame.lookAt(rHand.Position, targetPos)
	shardPart.Parent = workspace
	shardPart.CustomPhysicalProperties = PhysicalProperties.new(0.01,0,1,0,0)

	local spawnPos = rHand.CFrame * CFrame.new(0,0,-1)
	shardPart.CFrame = CFrame.lookAt(spawnPos.Position, targetPos)

	-- Apply velocity
	local bv = Instance.new("BodyVelocity")
	bv.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
	bv.Velocity = (targetPos - spawnPos.Position).Unit * SHARD_SPEED
	bv.Parent = shardPart
	Debris:AddItem(bv, 2)

	-- Clone and parent VFX
	local vfxAttachment = SHARD_VFX_ATTACHMENT_TEMPLATE:Clone()
	vfxAttachment.Parent = shardPart

	local emitter = vfxAttachment:FindFirstChildOfClass("ParticleEmitter")
	if emitter then
		emitter.Enabled = true

		task.spawn(function()
			task.wait(0)
			emitter:Emit(30)
		end)
	end
	
	local function stopVFX()
		if emitter then
			emitter.Enabled = false
		end
	end
	
	local hasHit = false
	local startPos = shardPart.Position

	-- Touch detection
	local connection
	connection = shardPart.Touched:Connect(function(hit)
		if hasHit or hit:IsDescendantOf(character) then return end
		hasHit = true

		-- Damage logic (placeholder)
		if hit.Name == "VFXHitbox" or hit:FindFirstChild("VFXHitbox") or CollectionService:HasTag(hit, "Event") then
			local eventPart = if CollectionService:HasTag(hit, "Event") then hit else (if hit.Name == "VFXHitbox" then hit.Parent else hit:FindFirstChild("VFXHitbox").Parent)
			if eventPart and eventPart:IsA("BasePart") then
				local powerMult = player:GetAttribute("PowerDamageMultiplier") or 1
				print("Damage Taken")
			end
		end
		
		stopVFX()

		-- Cleanup
		connection:Disconnect()
		Debris:AddItem(shardPart, 25)
	end)

	-- Distance check
	task.spawn(function()
		while shardPart and shardPart.Parent and not hasHit do
			local dist = (shardPart.Position - startPos).Magnitude
			if dist >= MAX_DISTANCE then
				hasHit = true
				stopVFX()
				Debris:AddItem(shardPart, 25)
				if connection then connection:Disconnect() end
				break
			end
			task.wait(0.05)
		end
	end)
end

return ArcaneFlick
1 Like