I’m in the process of making a humanoid turret enemy that turns it’s turret barrel towards you and fires. I’ve gotten a pretty good ways along but I’ve run into a bit of a snag when it comes to certain things:
I want the turret’s aiming to operate as smoothly as it can (this means being independent of the firing cooldown and running on either a “while true do wait()” or a RunService loop) while the projectiles fire at a set rate using what’s basically FastCast. In past, all my solutions either ended up having the aiming happen concurrent with the aforementioned set rate or having the turret spew thousands of bullets within a matter of a few seconds. The current solution I have seems to do what I want it to, but I can’t tell, plus it now has the problem of not stopping its firing when the turret kills you.
My current code is here:
local CS = game:GetService("CollectionService")
local SS = game:GetService("ServerStorage")
local RS = game:GetService("ReplicatedStorage")
local run = game:GetService("RunService")
local events = RS:WaitForChild("Events")
local posEvent = events:WaitForChild("PositionsChanged")
local projectiles = SS:WaitForChild("Projectiles")
local turretBullet = projectiles:WaitForChild("Turret")
local turret = script.Parent
local humanoid = turret:WaitForChild("Humanoid")
local HRP = turret:WaitForChild("HumanoidRootPart")
local head = turret:WaitForChild("Head")
local barrel = head:WaitForChild("Barrel")
local aim = head:WaitForChild("Aim")
local COM = head:WaitForChild("CenterOfMass")
local config = turret:WaitForChild("Config")
local MAX_AGGRO = config:WaitForChild("MaxAggro")
local DAMAGE = config:WaitForChild("Damage")
local FIRE_RATE = config:WaitForChild("FireRate")
local enemies = CS:GetTagged("Enemy")
local firing = false
humanoid:ChangeState(Enum.HumanoidStateType.Running)
local targets = {}
turret.PrimaryPart:SetNetworkOwner(nil)
local function getTarget()
local aggro = MAX_AGGRO.Value
local chosenTarget = nil
for player, target in pairs(targets) do
if player.Character.Humanoid.Health > 0 then
if (turret.PrimaryPart.Position - target).Magnitude < aggro then
aggro = (turret.PrimaryPart.Position - target).Magnitude
chosenTarget = player
end
end
end
return chosenTarget
end
local fire = coroutine.wrap(function()
while true do
firing = true
local bullet = turretBullet:Clone()
bullet.CFrame = barrel.WorldCFrame
bullet.Parent = turret
local speed = 200
local params = RaycastParams.new()
local iterations = 50
params.FilterDescendantsInstances = {turret}
local bulletConnection
bulletConnection = run.Heartbeat:Connect(function(deltaTime)
local position, nextPosition = bullet.Position, bullet.CFrame.LookVector * deltaTime * speed
local result = workspace:Raycast(position,nextPosition,params)
if result then
bulletConnection:Disconnect()
bullet:Destroy()
local human = result.Instance.Parent:FindFirstChildWhichIsA("Humanoid")
if human and human.Health > 0 then
human:TakeDamage(math.random(0.5 * DAMAGE.Value, 1.5 * DAMAGE.Value))
end
else
bullet.Position = position + nextPosition
end
iterations -= 1
if iterations < 0 then
bulletConnection:Disconnect()
bullet:Destroy()
end
end)
task.wait(1 / FIRE_RATE.Value)
firing = false
end
end)
posEvent.OnServerEvent:Connect(function(player, position)
targets[player] = position
end)
run.Heartbeat:Connect(function()
local target = getTarget()
if target and target.Character.Humanoid.Health > 0 and (targets[target] - HRP.Position).Magnitude <= MAX_AGGRO.Value then
aim.CFrame = CFrame.lookAt(COM.WorldPosition, targets[target] + target.Character.PrimaryPart.Velocity/(3.9 - (targets[target] - HRP.Position).Magnitude/30))
if not firing then
fire()
end
else
coroutine.yield(fire)
end
print("running", target)
end)
Let me know if there’s anything else I need to provide. I can’t imagine this being an extremely complex problem, I just don’t have a very good understanding of coroutines.