Heya there!
There has been just one issue that I seemingly cannot solve.
Sometimes, the bullet ray shoots at the sky (past the zombie)? I’m guessing this is because of the muzzle being inside a zombie head. The video below showcases this: red part is muzzle position, green part is mouse position.
Here’s the module code that I use for the gun.
local setup = {}
setup.__index = setup
local PS = game:GetService("Players")
local RS = game:GetService("ReplicatedStorage")
local DBS = game:GetService("Debris")
local caster = require(script.Parent.Caster)
local partCache = require(script.Parent.Cache)
local damageDisplay = require(script.Parent.Parent.DamageDisplay)
local fx = RS.Events.FX
local effect = {
blood = "Blood",
hit = "Hit"
}
type params = {[string]: any}
local function VisualizeRay(dir: Vector3, origin: Vector3)
local mid = origin + dir / 2
local part = Instance.new("Part") do
part.Parent = workspace
part.Anchored = true
part.CFrame = CFrame.new(mid, origin)
part.Size = Vector3.new(.1, .1, dir.Magnitude)
part.Material = Enum.Material.Neon
part.BrickColor = BrickColor.Red()
part.CanCollide = false
part.CanQuery = false
part.CanTouch = false
task.delay(.2, function()
part:Destroy()
end)
end
end
local function VisualizePos(pos: Vector3, color: BrickColor)
local part = Instance.new("Part") do
part.Parent = workspace
part.Anchored = true
part.Position = pos
part.Size = Vector3.new(.5,.5,.5)
part.Material = Enum.Material.Neon
part.BrickColor = color
part.CanCollide = false
part.CanQuery = false
part.CanTouch = false
--task.delay(.2, function()
-- part:Destroy()
--end)
end
end
local function LoadBulletHole(parent: BasePart, pos: Vector3, normal: Vector3, hum: Humanoid)
local hole = Instance.new("Part") do
hole.CanCollide = false
hole.CanQuery = false
hole.CanTouch = false
hole.Transparency = 1
hole.Size = Vector3.new(.5, .5, .05)
hole.Parent = parent
hole.CFrame = CFrame.lookAt(pos, pos + normal)
end
local image = if hum then script.BloodHole:Clone() else script.PartHole:Clone() do
image.Parent = hole
end
local weld = Instance.new("WeldConstraint") do
weld.Parent = parent
weld.Part0 = parent
weld.Part1 = hole
end
DBS:AddItem(hole, 15)
end
function setup.new(params)
local self = {}
self.Tool = params.Tool
self.Owner = if self.Tool.Parent:IsA("Backpack") then self.Tool.Parent.Parent else PS:GetPlayerFromCharacter(self.Tool.Parent)
self.Character = self.Owner.Character or self.Owner.CharactedAdded:Wait()
self.Damage = params.Damage
self.AttackType = params.AttackType
self.Bullet = Instance.new("Part")
self.Multiplier = params.Multiplier
self.DistanceBeforeMultiply = params.DistanceBeforeMultiply
self.MinSpread = params.MinSpread
self.MaxSpread = params.MaxSpread
for i,v in pairs(params.BulletInfo) do
if i == "Trail" and v:IsA("Trail") then
local at0 = Instance.new("Attachment") do
at0.Parent = self.Bullet
at0.WorldPosition = (self.Bullet.CFrame * CFrame.new(0, 0, (self.Bullet.Size.Z / 2))).Position
end
local at1 = Instance.new("Attachment") do
at1.Parent = self.Bullet
at1.WorldPosition = (self.Bullet.CFrame * CFrame.new(0, 0, -(self.Bullet.Size.Z / 2))).Position
end
local clone = v:Clone() do
clone.Parent = self.Bullet
clone.Enabled = false
clone.Attachment0 = at0
clone.Attachment1 = at1
end
else
if self.Bullet[i] then
self.Bullet[i] = v
end
end
end
self.MaxDistance = params.MaxDistance
self.Speed = params.Speed
self.Gravity = params.Gravity
self.RaycastParameters = params.RaycastParameters
self.BulletParent = params.BulletParent
self.PartCache = partCache.new(self.Bullet, 100, self.BulletParent)
self.Hitbox = caster.new()
self.Behavior = caster.newBehavior() do
self.Behavior.RaycastParams = self.RaycastParameters
self.Behavior.HighFidelityBehavior = caster.HighFidelityBehavior.Default
self.Behavior.MaxDistance = self.MaxDistance
self.Behavior.Acceleration = self.Gravity
self.Behavior.CosmeticBulletContainer = self.BulletParent
self.Behavior.CosmeticBulletProvider = self.PartCache
self.Behavior.AutoIgnoreContainer = false
end
return setmetatable(self, setup)
end
function setup:Fire(mousePos, endPos)
local distBeforeMult = self.DistanceBeforeMultiply
local player = self.Owner
local hitbox = self.Hitbox
local speed = self.Speed
local damage = self.Damage
local gravity = self.Gravity
local multiplier = self.Multiplier
local behavior = self.Behavior
local cache = self.PartCache
local params = self.RaycastParameters
local attackType = self.AttackType
local maxSpread = self.MaxSpread
local minSpread = self.MinSpread
local direction = (mousePos - endPos).Unit
local debounce = {}
local connection = {}
VisualizePos(endPos, BrickColor.Red())
VisualizePos(mousePos, BrickColor.Green())
connection[1] = hitbox.LengthChanged:Connect(function(caster, origin, direction, length, velocity, bullet)
if not bullet then
return
end
if bullet:FindFirstChildWhichIsA("Trail") then
bullet:FindFirstChildWhichIsA("Trail").Enabled = true
end
local bulletLength = bullet.Size.Z / 2
local baseCFrame = CFrame.new(origin, origin + direction)
bullet.CFrame = baseCFrame * CFrame.new(0, 0, -(length - bulletLength))
end)
connection[2] = hitbox.RayHit:Connect(function(caster, ray, velocity, bullet)
local hitPart = ray.Instance
local hitPoint = ray.Position
local normal = ray.Normal
local model = ray.Instance:FindFirstAncestorOfClass("Model")
local hum = model and model:FindFirstChildOfClass("Humanoid")
fx:FireAllClients(if hum then effect.blood else effect.hit, hitPart, hitPoint)
LoadBulletHole(hitPart, hitPoint, ray.Normal, hum)
if hum and not PS:GetPlayerFromCharacter(model) then
if debounce[hum] then
return
end
--if attackType == "Destroying" then
-- local blacklist = {"Torso", "HumanoidRootPart"}
-- local rand = Random.new():NextInteger(1,65)
-- if rand <= 25 and hitPart and not table.find(blacklist, hitPart.Name) then
-- hitPart.Transparency = 1
-- task.delay(.4, function()
-- hitPart:Destroy()
-- end)
-- end
--end
local currDamage = if multiplier[hitPart.Name] then damage * multiplier[hitPart.Name] else damage
if player:DistanceFromCharacter(hitPoint) < distBeforeMult then
currDamage *= 1.3
end
debounce[hum] = true
hum:TakeDamage(currDamage)
damageDisplay.new(currDamage, hitPart)
end
end)
connection[3] = hitbox.CastTerminating:Connect(function(caster)
local cosmeticBullet = caster.RayInfo.CosmeticBulletObject
if cosmeticBullet then
if behavior.CosmeticBulletProvider then
pcall(function()
if cosmeticBullet:FindFirstChildWhichIsA("Trail") then
cosmeticBullet:FindFirstChildWhichIsA("Trail").Enabled = false
end
behavior.CosmeticBulletProvider:ReturnPart(cosmeticBullet)
for i,v in ipairs(connection) do
v:Disconnect()
end
end)
end
end
end)
pcall(function()
local dirCF = CFrame.lookAt(Vector3.new(), direction)
local spreadDir = CFrame.fromOrientation(0, 0, math.random(0, math.pi * 2))
local spreadAngle = CFrame.fromOrientation(math.rad(math.random(minSpread, maxSpread)), 0, 0)
local direction = (dirCF * spreadDir * spreadAngle).LookVector
local modifSpeed = (direction * speed)
hitbox:Fire(endPos, direction, modifSpeed, behavior)
end)
end
return setup
I’m not really sure how I can solve this. Any help is appreciated!
EDIT: I think I solved it. I just moved the muzzle point backwards by a few studs (it’s now practically in the player’s shoulder).