Touching inconsistency

Main Problem

I want to achieve creating parts that play music whenever you touch them. Currently it works, however there are some inconsistencies.

The issues are:

  • Sometimes, whenever leaving and entering the hitbox, the music will not play.
  • Whenever jumping, it will call the TouchEnded event, which will stop the music

I have been considering Region3's, however, you will need to detect them through while loops or RunService.Heartbeat. This will be performance heavy whenever having multiple loops.

Code

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

local resources = ReplicatedStorage.Resources
local musicHandler = require(resources.MusicHandler)
local touchUtil = require(resources.TouchUtil)

local CharacterHitbox = {}
local currentSoundPlaying

function CharacterHitbox.DetectedMusicHitbox(hitbox)
    local oldCurrentSoundPlaying = currentSoundPlaying

    local success, err = pcall(function()
	    currentSoundPlaying = musicHandler.GetAudio(hitbox.Name)
    end)

    if not success and oldCurrentSoundPlaying then
	    print("no success")
	    oldCurrentSoundPlaying:Stop()
	    oldCurrentSoundPlaying = currentSoundPlaying
	    currentSoundPlaying = nil
    end


    if oldCurrentSoundPlaying == currentSoundPlaying then
	    print("the same audio")
	    return
    end

    if currentSoundPlaying then
	    print("play!")
	    currentSoundPlaying:Play()
    end
end

 function CharacterHitbox.Init()
    local player = Players.LocalPlayer
    local character = player.Character or player.CharacterAdded:Wait()

    local rootPart = character:WaitForChild("UpperTorso") or character:WaitForChild("Torso")
	
    local function isPointInPart(point, part)
	    local relPos = part.CFrame:PointToObjectSpace(point)
	    local multiplier = 0.5
	    return math.abs(relPos.X) < part.Size.X * multiplier and math.abs(relPos.Y) <   part.Size.Y * multiplier and math.abs(relPos.Z) < part.Size.Z * multiplier
    end
	
    rootPart.Touched:Connect(
	    touchUtil.TouchedDebounce(1, function(hit)
		    local pointInPart = isPointInPart(rootPart.Position, hit)
		
		    print(isPointInPart(rootPart.Position, hit))
		
		    if CollectionService:HasTag(hit, "MusicHitbox") then
			    if isPointInPart(rootPart.Position, hit) then
				    CharacterHitbox.DetectedMusicHitbox(hit)
			    elseif not isPointInPart(rootPart.Position, hit) then
				    CharacterHitbox.DetectedMusicHitbox(nil)
			    end	
		    end
	    end)
    )
end

return CharacterHitbox

Clarifications

  • TouchUtil is a class that handles Touched events and TouchEnded events. All of this works.
  • MusicHandler is a module that plays, pauses, and stops music. That also works, too.
  • MusicHitboxHandler.Init is called. You should not worry about that
1 Like

I would like to mention that TouchEnded is very unreliable and has been discussed all around there is a thread somewhere about the same thing you are trying to achieve, let me find it for you

2 Likes

Here

It does mention the use of Region3 and I understand your concern of Performance,

Instead of Region3 use Magnitude instead and don’t use multiple loops

What I would do is Simply check when the player is moving then check if the Player is near an Area enough, once you know they are in an area you don’t have to check for every area only check for the same area until they leave that area then find which new area they are closets to.

Or you could use Touched and Magnitude instead or with Region3, many ways of achieving the same thing but you gotta be smart about it.



1 Like

This is unnecessary. The fastest way to check if a position is inside a box is to do this:

function isPointInPart(point, part)
    local relPos = part.CFrame:PointToObjectSpace(point)
    return math.abs(relPos.X) < part.Size.X * 0.5 and math.abs(relPos.Y) < part.Size.Y * 0.5 and math.abs(relPos.Z) < part.Size.Z * 0.5
end
7 Likes

Hey I’ve thought of that too but I’m not particularly familiar with Vector Math so I couldn’t give him the Formula but I like your method a lot and I strongly recommend using it!

2 Likes

Interesting. I’ll give it a try and see if it works well.

I actually want to ask a question. Do I call this function in the Touched event?
EDIT: I called the function in the Touched event. However, the music stopping whenever the character jumps is still a problem

Could make a check in the function for whether the music should end to see whether the player is jumping via Humanoid.Jump’s Boolean value. Though, you might want to pair that alongside checking whether their position is still within your box in case they like jumping to get in and out.

1 Like

You call this in a loop actually. I would probably do it something like this:

while true do
	local isInPart = false
	for _, limb in ipairs(character:GetChildren()) do
		if limb:IsA("BasePart") and isPointInPart(limb.Position, hitbox) then
			isInPart = true
			break
		end
	end
	if isInPart and not music.Playing then
		music:Play()
	elseif not isInPart and music.Playing then
		music:Stop()
	end
	-- wait function here
end

A bunch of optimizations can be done here to run this function less frequently or on less parts, but that will be your adventure.

Edit: fixed it, now it shouldnt turn on and off a bunch for no reason.

1 Like

Thank you for your response, however, stated in the post, it will lag the game. This is the same method as using Region3’s, which I want to avoid.

You could also try checking the distance between the part and the player… for example:

if (((math.abs(Player.Character.PrimaryPart.Position.X) - math.abs(Part.Position.X)) <= Part.Size.X/2) and (((math.abs(Player.Character.PrimaryPart.Position.Y) - math.abs(Part.Position.Y)) <= 5) and (((math.abs(Player.Character.PrimaryPart.Position.Z) - math.abs (Part.Position.Z)) <= Part.Size.Z/2) then

This will check if the character’s position is above (technically below too) the part. The variables ‘Part’ and ‘Player’ will need to be set while using this. Using the GetPropertyChangedSignal event would work well with this to reduce lag.

Edit: you may want to increase the required distance required or the player might end up having to stand directly in the center of the part.

1 Like

I have remade the code. It works so much better than the older one, however, there is still one problem.

The isPointInPart function seems to give a false positive whenever the player goes outside of the hitbox. Whenever the character walks back in, now the TouchEnded part would fire. However, the Touched part would fire since they go back inside the part.

(I changed the original post to the new code)

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

local resources = ReplicatedStorage.Resources
local musicHandler = require(resources.MusicHandler)
local touchUtil = require(resources.TouchUtil)

local CharacterHitbox = {}
local currentSoundPlaying

function CharacterHitbox.DetectedMusicHitbox(hitbox)
    local oldCurrentSoundPlaying = currentSoundPlaying

    local success, err = pcall(function()
	    currentSoundPlaying = musicHandler.GetAudio(hitbox.Name)
    end)

    if not success and oldCurrentSoundPlaying then
	    print("no success")
	    oldCurrentSoundPlaying:Stop()
	    oldCurrentSoundPlaying = currentSoundPlaying
	    currentSoundPlaying = nil
    end


    if oldCurrentSoundPlaying == currentSoundPlaying then
	    print("the same audio")
	    return
    end

    if currentSoundPlaying then
	    print("play!")
	    currentSoundPlaying:Play()
    end
end

 function CharacterHitbox.Init()
    local player = Players.LocalPlayer
    local character = player.Character or player.CharacterAdded:Wait()

    local rootPart = character:WaitForChild("UpperTorso") or character:WaitForChild("Torso")
	
    local function isPointInPart(point, part)
	    local relPos = part.CFrame:PointToObjectSpace(point)
	    local multiplier = 0.5
	    return math.abs(relPos.X) < part.Size.X * multiplier and math.abs(relPos.Y) <   part.Size.Y * multiplier and math.abs(relPos.Z) < part.Size.Z * multiplier
    end
	
    rootPart.Touched:Connect(
	    touchUtil.TouchedDebounce(1, function(hit)
		    local pointInPart = isPointInPart(rootPart.Position, hit)
		
		    print(isPointInPart(rootPart.Position, hit))
		
		    if CollectionService:HasTag(hit, "MusicHitbox") then
			    if isPointInPart(rootPart.Position, hit) then
				    CharacterHitbox.DetectedMusicHitbox(hit)
			    elseif not isPointInPart(rootPart.Position, hit) then
				    CharacterHitbox.DetectedMusicHitbox(nil)
			    end	
		    end
	    end)
    )
end

return CharacterHitbox

Another solution which would require a lot less code would be having a transparent, noncollidable part over the other part. This new part would sense when the player touches it and would have the height the player jumps to. Of course, Part.Touched does not work with noncollidable parts, so you would have to use Part:getTouchingParts().

1 Like

I would use a transparent noncollidable part within the section that you want the player to hear the music. Keep the .Touched and .TouchEnded, and make sure you add debounce.

The .Touched will work with non-collidable parts, just make sure it’s anchored.

Mark this as a solution if it helps!

1 Like

Thank you for your response, but I have tried this before. I am circumventing this problem because TouchEnded is unreliable.

If you’re problem is related to the Touched and TouchEnded events, @nooneisback wrote this script a month back that, basically, works as a pseudo Touched and TouchEnded event.

You can edit it to make it so it works for multiple parts (Personally, I did this by checking which out of a group of parts was the closest to the player and then setting it so it would check if the player was touching the part, though that’s probably not the most efficient way, it works)

If you want an explanation as to what this script actually does, the below contains just that.

1 Like

This works more like :GetTouchingParts(), but you can add a custom event to the frame binding.

2 Likes