How am I able to prevent this from happening?

So, whenever I throw a tomato at a wall, the wall ends up looking like this:

and I wanted to ask, how can I prevent this from happening? Is there a way to like add the decal to one part of the wall?

Code:


local Tool = script.Parent
local enabled = true
local THROW_FORCE = math.random(40, 180)
local MIN_THROW_DELAY = 1

local DECAL_ID = "rbxassetid://13510998982"

local IMPACT_SOUND_ID = "rbxassetid://8595980577"

local canThrow = true

local players = game:GetService("Players")

local MouseUpdate = Instance.new("RemoteEvent")
MouseUpdate.Name = "MouseUpdate"
MouseUpdate.Parent = Tool

local currentMousePosition = Vector3.new(0,0,0) --Default position

MouseUpdate.OnServerEvent:Connect(function(player, mousePos)
	currentMousePosition = mousePos
end)

local function onActivated()
	if not enabled or not canThrow then
		return
	end

	enabled = false
	canThrow = false

	local character = Tool.Parent
	local humanoid = character:FindFirstChild("Humanoid")
	if not humanoid then return end

	local torso = character:FindFirstChild("Torso") or character:FindFirstChild("HumanoidRootPart")
	if not torso then return end

	local handle = Tool.Handle:Clone()
	handle.Anchored = false
	handle.CanCollide = true
	handle.Parent = workspace
	handle.CFrame = Tool.Handle.CFrame

	-- Apply throwing force using the *latest* mouse position
	local throwDirection = (currentMousePosition - handle.Position).Unit
	handle:ApplyImpulse(throwDirection * THROW_FORCE * handle:GetMass())
	handle:SetNetworkOwner(nil)

	Tool.Enabled = false

	if Tool.Handle:FindFirstChild("BiteSound") then
		Tool.Handle.BiteSound:Play()
	end

	local function applyImpactEffects(hit)
		if hit and hit:IsA("BasePart") then -- Checks if the hit object is a Part, MeshPart, or UnionOperation

			-- Check if the hit part is not an ancestor of the character throwing the tool
			local character = Tool.Parent
			local isAncestor = false
			local current = hit
			while current do
				if current == character then
					isAncestor = true
					break
				end
				current = current.Parent
			end

			if not isAncestor then
				print(hit)

				local decal = Instance.new("Decal")
				decal.Texture = DECAL_ID
				decal.Face = Enum.NormalId.Front
				decal.Parent = hit
				game.Debris:AddItem(decal, 5)

				-- Particle effect
				local emitter = Instance.new("ParticleEmitter")
				emitter.Parent = hit
				emitter.Rate = 10
				emitter.Lifetime = NumberRange.new(0.5, 1)
				game.Debris:AddItem(emitter, 1)

				-- Impact sound (on hit object)
				local sound = Instance.new("Sound")
				sound.SoundId = IMPACT_SOUND_ID
				sound.Parent = hit
				sound.Volume = 0.5
				sound:Play()

				-- Impact sound (only if a humanoid root part exists and is a descendant of the hit part)
				if hit.Parent and hit.Parent:FindFirstChild("HumanoidRootPart") then
					local sound2 = Instance.new("Sound")
					sound2.SoundId = IMPACT_SOUND_ID
					sound2.Parent = hit.Parent.HumanoidRootPart
					sound2.Volume = 0.5
					sound2:Play()
				end
			end
		end


	end
	local connection = handle.Touched:Connect(applyImpactEffects)

	-- Fix: Compare the number of players with a number, not the players service itself
	if #players:GetPlayers() >= 6 then
		game.Debris:AddItem(handle, 3)
	else
		game.Debris:AddItem(handle, 3000)
	end

	wait(0.5)

	local player = game.Players:GetPlayerFromCharacter(Tool.Parent)

	if player then
		local backpack = player.Backpack

		if backpack then
			if Tool then
				Tool:Destroy()
			end
		end
	end

	wait(MIN_THROW_DELAY)

	if connection then connection:Disconnect() end
	Tool.Enabled = true
	enabled = true
	canThrow = true

end

local function onEquipped()
	if Tool.Handle:FindFirstChild("EquipSound") then
		Tool.Handle.EquipSound:Play()
	end
end

Tool.Activated:Connect(onActivated)
Tool.Equipped:Connect(onEquipped)

1 Like

You could probably make a part with the decal appear where the tomato gets thrown. Bonus is that you can use Debris to make it disappear after a certain amount of time too.

1 Like

throwable object without raycasts? :fearful: :fearful:

anyways, like what @HYDR0GEN1 said, heres how i made my bulletholes in my zombie game, which work similar to what you want (except with raycasts)

	-- Create bullet hole part
	local bulletHole = Instance.new("Part")
	bulletHole.Size = Vector3.new(0.5, 0.5, 0.001) -- Small size to simulate a bullet hole
	bulletHole.CFrame = CFrame.new(hitPosition, hitPosition + hitNormal) -- Align the part with the surface normal
	bulletHole.Anchored = true
	bulletHole.CanCollide = false
	bulletHole.CanQuery = false
	bulletHole.Transparency = 1
	bulletHole.Parent = folder

Looks like the decal is fitting itself to the part, and in this case that is that whole wall. Maybe create a smaller transparent part that the decal is in for the hit.

Lol, I don’t really know what raycasts where. Thanks for the tip!

Okay, it works now but one more issue, I can see the decal through players like myself

image


local Tool = script.Parent
local enabled = true
local THROW_FORCE = math.random(40, 180)
local MIN_THROW_DELAY = 1

local DECAL_ID = "rbxassetid://2884584"
local IMPACT_SOUND_ID = "rbxassetid://8595980577"

local canThrow = true
local players = game:GetService("Players")

local MouseUpdate = Instance.new("RemoteEvent")
MouseUpdate.Name = "MouseUpdate"
MouseUpdate.Parent = Tool

local currentMousePosition = Vector3.new(0, 0, 0)

MouseUpdate.OnServerEvent:Connect(function(player, mousePos)
	currentMousePosition = mousePos
end)

-- Function to create a SurfaceGui decal at hit position + normal
local function applyImpactEffects(hitPosition, hitNormal, character)
	local sizeX = math.random(2, 4) -- Random size between 2 and 4
	local sizeY = math.random(2, 4) -- Random size between 2 and 4
	local part = Instance.new("Part")
	part.Size = Vector3.new(sizeX, sizeY, 0.1)  -- Make the part thin
	part.Anchored = true
	part.CanCollide = false
	part.Transparency = 1
	part.CFrame = CFrame.new(hitPosition, hitPosition + hitNormal)
	part.Parent = workspace

	local surfaceGui = Instance.new("SurfaceGui")
	surfaceGui.Face = Enum.NormalId.Front
	surfaceGui.AlwaysOnTop = true
	surfaceGui.SizingMode = Enum.SurfaceGuiSizingMode.PixelsPerStud
	surfaceGui.PixelsPerStud = 50
	surfaceGui.Adornee = part
	surfaceGui.Parent = part

	local image = Instance.new("ImageLabel")
	image.Image = DECAL_ID
	image.Size = UDim2.new(1, 0, 1, 0)
	image.BackgroundTransparency = 1
	image.Parent = surfaceGui

	-- Impact sound
	local sound = Instance.new("Sound")
	sound.SoundId = IMPACT_SOUND_ID
	sound.Volume = 0.5
	sound.Parent = part
	sound:Play()

	-- Optional: Particle effect
	local emitter = Instance.new("ParticleEmitter")
	emitter.Rate = 20
	emitter.Lifetime = NumberRange.new(0.3, 0.6)
	emitter.Speed = NumberRange.new(2, 4)
	emitter.SpreadAngle = Vector2.new(360, 360)
	emitter.Parent = part
	game.Debris:AddItem(emitter, 1)

	-- Disable collision with the character who threw it
	if character then
		local PhysicsService = game:GetService("PhysicsService")
		local partCollisionGroupName = tostring(part) -- Unique collision group name

		-- Register the collision group
		PhysicsService:RegisterCollisionGroup(partCollisionGroupName)

		-- Set the collision group to not collide with itself
		PhysicsService:CollisionGroupSetCollidable(partCollisionGroupName, partCollisionGroupName, false)

		-- Function to recursively set collision group for parts and their descendants
		local function setCollisionGroupRecursive(obj, groupName)
			if obj:IsA("BasePart") then
				obj.CollisionGroup = groupName
			end
			for _, child in ipairs(obj:GetChildren()) do
				setCollisionGroupRecursive(child, groupName)
			end
		end

		-- Set collision group for the part
		setCollisionGroupRecursive(part, partCollisionGroupName)

		-- Set collision group for all parts in the character
		for _, partInCharacter in ipairs(character:GetChildren()) do
			setCollisionGroupRecursive(partInCharacter, partCollisionGroupName)
		end
	end
	game.Debris:AddItem(part, 10)
end

local function onActivated()
	if not enabled or not canThrow then return end
	enabled = false
	canThrow = false

	local character = Tool.Parent
	local humanoid = character:FindFirstChild("Humanoid")
	if not humanoid then return end

	local torso = character:FindFirstChild("Torso") or character:FindFirstChild("HumanoidRootPart")
	if not torso then return end

	local handle = Tool.Handle:Clone()
	handle.Anchored = false
	handle.CanCollide = true
	handle.Parent = workspace
	handle.CFrame = Tool.Handle.CFrame

	local throwDirection = (currentMousePosition - handle.Position).Unit
	handle:ApplyImpulse(throwDirection * THROW_FORCE * handle:GetMass())
	handle:SetNetworkOwner(nil)

	if Tool.Handle:FindFirstChild("BiteSound") then
		Tool.Handle.BiteSound:Play()
	end

	-- Perform raycast from handle's position in the direction of throw
	local raycastParams = RaycastParams.new()
	raycastParams.FilterDescendantsInstances = {character, handle}
	raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
	raycastParams.IgnoreWater = true


	local raycastResult = workspace:Raycast(handle.Position, throwDirection * 100, raycastParams)
	if raycastResult then
		applyImpactEffects(raycastResult.Position, raycastResult.Normal, character)
	end

	-- Clean up the thrown handle
	if #players:GetPlayers() >= 6 then
		game.Debris:AddItem(handle, 3)
	else
		game.Debris:AddItem(handle, 3000)
	end

	wait(0.5)

	local player = players:GetPlayerFromCharacter(character)
	if player then
		local backpack = player:FindFirstChild("Backpack")
		if backpack then
			Tool:Destroy()
		end
	end

	wait(MIN_THROW_DELAY)
	Tool.Enabled = true
	enabled = true
	canThrow = true
end

local function onEquipped()
	if Tool.Handle:FindFirstChild("EquipSound") then
		Tool.Handle.EquipSound:Play()
	end
end

Tool.Activated:Connect(onActivated)
Tool.Equipped:Connect(onEquipped)

You need to set AlwaysOnTop to false.

2 Likes

Thanks, it officially works now!!!

2 Likes