local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
From returned result, you can get the the part that was hit (and check if it is player’s part) as well as the exact position. Now you can create a new Surface GUI (perhaps clone it) on that spot and use for example Debris to remove it after some time.
local bulletHoleImageIdsBasedOnMaterial = {
[Enum.Material.Wood] = "rbxassetid//:123412312"; -- put Ids here
[Enum.Material.Plastic] = "rbxassetid//:1234123132";
[Enum.Material.Metal] = "rbxassetid//:1234123152";
-- put rest of materials here with their corresponding ID
}
local rayResult = workspace:Raycast() -- ray here
if rayResult then
local bulletPart = Instance.new("Part")
bulletPart.Transparency = 1
bulletPart.Anchored = true
bulletPart.CanCollide = false
bulletPart.Size = Vector3.new(0.35, 0.35, 0.05) -- you might have to change the axis, i didnt test this
bulletPart.CFrame = CFrame.lookAt(rayResult.Position, rayResult.Position + rayResult.Normal)
local bulletImage = Instance.new("Decal")
bulletImage.Texture = bulletHoleImageIdsBasedOnMaterial[rayResult.Instance.Material] or ""
bulletImage.Parent = bulletPart
bulletImage.Face = Enum.NormalId.Front
bulletPart.Parent = workspace -- best if you add it to a folder you use for debris
end
Even though this should all work, you might need to adjust which face it is since I did not test any of this
No, I think RaycastResult.Position is to be used here (because it stores location of the point where intersection occured).
So when you have the CFrame stored, you can for example clone the “bullet hole” from the ReplicatedStorage and parent it to the hit part, which is also detected when hit by a ray and is thus stored in RaycastResult too. The last step is positioning the hole according to the CFrame.
You have a least two options when creating bullet holes. You can either use surface GUIs or parts with applied texture using decals.