How do I make an exit thing?

I was going to say exit wound, but it’s more like an exit wound for basic parts.
I have a very basic bullet hole for parts, but how can I find the opposite side of the part?

pls ignore the gun im not good at building



How would I get the other side kinda like this?

Can you show your script on what your using for the gun?

yeah its a lot, but here is the shooting part:

mouse.Button1Down:Connect(function()
	if canShoot == true then
		canShoot = false
		local landed = false
		
		local bullet = replicatedStorage.Round556.Bullet:Clone()
		local case = replicatedStorage.Round556.Casing:Clone()
		local sound = replicatedStorage.Round556.Sound:Clone()
		bullet.Parent = rounds
		case.Parent = rounds
		sound.Parent = rounds
		
		bullet.CFrame = barrelattachment.WorldCFrame
		case.CFrame = chamberattachment.WorldCFrame
		case.Velocity = gunBase.Velocity
		case.Velocity = chamberattachment.WorldCFrame.RightVector * 25
		case.RotVelocity = chamberattachment.WorldCFrame.UpVector * -50
		local casetouch = true
		case.Touched:Connect(function()
			if casetouch == true then
				casetouch = false
				case.ShellCasing:Play()
				wait(0.5)
				casetouch = true
			end
		end)
		
		local position1 = barrelattachment.WorldPosition + (bullet.CFrame.LookVector * distance)
		local time1 = 25
		local shootTweenInfo = TweenInfo.new(time1,Enum.EasingStyle.Linear,Enum.EasingDirection.Out,0,false,0)
		local shootTween = tweenService:Create(bullet,shootTweenInfo,{CFrame = CFrame.new(position1)})
		shootTween:Play()
		
		bullet.Hitbox.Touched:Connect(function(part1)
			shootTween:Cancel()
			bullet.Anchored = true
			bullet.Hitbox.CanTouch = false
			bullet.Velocity = Vector3.new(0,0,0)
			print(bullet.Hitbox.Name.." touched "..part1.Name)
			spawn(function()
				bullet.Transparency = 1
				wait(3)
				bullet:Destroy()
			end)
			
			local params32 = RaycastParams.new()
			params32.FilterDescendantsInstances = {char}
			params32.FilterType = Enum.RaycastFilterType.Blacklist
			local ray12 = workspace:Raycast(bullet.CFrame.Position - bullet.CFrame.LookVector * 0.25, bullet.CFrame.LookVector * 10, params32)
			if ray12 and ray12.Instance then print(ray12.Instance) end
			if ray12 then
				local pos  = ray12.Position
				local inst = ray12.Instance
				local dist = ray12.Distance
				local mat  = ray12.Material
				local norm = ray12.Normal
				local hole = replicatedStorage.Round556.Bullet_Hole:Clone()
				hole.Parent = workspace.BulletHoles
				hole.CFrame = CFrame.new(pos,pos+norm)
				bullet.CFrame = CFrame.new(pos)
				if ray12.Instance then
					hole.Anchored = false
					hole.WeldConstraint.Part1 = inst
					hole.WeldConstraint.Part1.Destroying:Connect(function()
						hole:Destroy()
					end)
				else
					hole.Anchored = true
					hole.WeldConstraint:Destroy()
				end
				local shrink = tweenService:Create(hole,TweenInfo.new(0.5,Enum.EasingStyle.Quint,Enum.EasingDirection.In,0,false,10),{Size = Vector3.new(0,0,hole.Size.Z)})
				shrink:Play()
				shrink.Completed:Connect(function() hole:Destroy() end)
			end
		end)

yeah I used tween service to make the bullet go, i didnt know any other way pls dont make fun of me

1 Like

Why would I make fun of you lol, not your fault. So, if I am correct this script is what makes the bullet hole whenever it hits a wall and you want it to wall bang (go through the wall)?

yeah, it does the whole cloning, setting up CFrames, makes the case go whoosh, sets up the tween, checks if the long, skinny hitbox touches anything (it aint that bad since tweens are real smooth), does a bit of doohicky stuff with raycasts, and yeah thats basically it

Well I would say, instead of doing doohicky stuff with workspace:Raycast() do Ray.new() even if it is depracated it is still very useful. It is a ray that goes through a wall but you can detect parts with workspace:FindPartOnRay()

Example:

local ray = Ray.new(bullet.CFrame.Position - bullet.CFrame.LookVector * 0.25,bullet.CFrame.LookVector * 10)

if ray then
    local HitPart, HitPosition = workspace:FindPartOnRay(ray) --// For ignoring do :FindPartOnRayWithIgnoreList(Ray, {Table})

    --// DO the funny stuff with HitPart and HitPosition. And due to the fact Ray.new doesn't stop on a part like usual workspace:RayCast() it can be used for Wall Bangs
end

This should work.

so how would i get the other end using this?

You can detect a part touch on the other side aswell with FindPartOnRay due to the fact it goes through walls

I forgot to mention, I only get one part and position

For the second Raycast parameter try using LookVector * 100 and tell me. I tried this and it worked for me.

yeah i changed that thinking the same thing but it still works the same as before

local ray = Ray.new(script.Parent.CFrame.Position,script.Parent.CFrame.LookVector * 100)

if ray then
	local HitPart, HitPosition = workspace:FindPartOnRay(ray) --// For ignoring do :FindPartOnRayWithIgnoreList(Ray, {Table})
	print(HitPart,HitPosition)
	--// DO the funny stuff with HitPart and HitPosition. And due to the fact Ray.new doesn't stop on a part like usual workspace:RayCast() it can be used for Wall Bangs
end

Try putting it in a while loop (don’t forget to add a task.wait()

hundreds of same thing
Screenshot (681)

while task.wait() do
	local ray = Ray.new(script.Parent.CFrame.Position,script.Parent.CFrame.LookVector * 100)

	if ray then
		local HitPart, HitPosition = workspace:FindPartOnRay(ray) --// For ignoring do :FindPartOnRayWithIgnoreList(Ray, {Table})
		print(HitPart,HitPosition)
		--// DO the funny stuff with HitPart and HitPosition. And due to the fact Ray.new doesn't stop on a part like usual workspace:RayCast() it can be used for Wall Bangs
	end
end

Hm, I think they may have updated it :sob: I got to go for now I will be back probably tomorrow, good luck!

The only solution I can think of using workspace:Raycast is raycasting n-times until the limit is reached or no result, and add each result to the blacklist.
One downside is you can’t use a whitelist.
code:

function raycast(origin: Vector3, direction: Vector3, max: number?): (number, {[number]: RaycastResult})
    max = max or 100
    
    local result
    local results = {}
    local blacklist = {}
    local count = 0

    local endPoint = origin + direction
    
    while count < max do
        local rayParams = RaycastParams.new()
        rayParams.FilterDescendantsInstances = blacklist
        rayParams.FilterType = Enum.RaycastFilterType.Blacklist
        
        result = workspace:Raycast(origin, endPoint - origin, rayParams)
        
        if result then
            table.insert(results, result)
            table.insert(blacklist, result.Instance)

            origin = result.Position

            count += 1
        else
            break
        end
    end
    
    return count, results
end

edit: a sollution without using a blacklist but with using the CanQuery and CanCollide property to ignore allready found parts instead.
Code:

function raycast(origin: Vector3, direction: Vector3, params: RaycastParams?, max: number?): (number, {[number]: RaycastResult})
    max = max or 100
    
    local result
    local results = {}
    local parts = {}
    local count = 0

    local endPoint = origin + direction
    
    while count < max do        
        result = workspace:Raycast(origin, endPoint - origin, params)
        
        if result then
            table.insert(results, result)
            
            table.insert(parts, {
                part = result.Instance,
                canCollide = result.Instance.CanCollide
            })
            
            result.Instance.CanCollide = false
            result.Instance.CanQuery = false

            origin = result.Position
            
            count += 1
        else
            break
        end
    end
    
    for _, data in ipairs(parts) do
        data.part.CanQuery = true
        data.part.CanCollide = canCollide
    end
    
    return count, results
end

What you need to do is shoot a ray from both sides like this

here is a demo project
ExitThing.rbxl (35.2 KB)


and this is the script inside the project

local userInputService = game:GetService("UserInputService")

-- this function will simply create a part with a position and color
local function CreatePart(position, color)
	local part = Instance.new("Part")
	part.Position = position
	part.Size = Vector3.one
	part.Color = color
	part.Shape = Enum.PartType.Ball
	part.TopSurface = Enum.SurfaceType.Smooth
	part.BottomSurface = Enum.SurfaceType.Smooth
	part.Anchored = true
	part.Parent = workspace
end

userInputService.InputBegan:Connect(function(input, processed)
	if processed == true then return end
	if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end

	-- get the mouses click position in 3d space and the direction of the click
	local mouseLocation = userInputService:GetMouseLocation()
	local ray = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
	
	-- shoot a ray from the mouses position in the direction of the click for 1000 studs
	local results1 = workspace:Raycast(ray.Origin, ray.Direction * 1000)

	-- if the ray does not hit anything exit the function and do nothing
	if results1 == nil then return end
	
	-- get a random color so its easier to match the part on the other side
	local color = Color3.new(math.random(), math.random(), math.random())
	
	-- create a part at the ray hit position with the random color
	CreatePart(results1.Position, color)
	
	-- now we whitelist the part that the ray hit so that the ray can only hit the same part when we shoot a ray backwards
	local raycastParams = RaycastParams.new()
	raycastParams.FilterType = Enum.RaycastFilterType.Whitelist
	raycastParams.FilterDescendantsInstances = {results1.Instance}
	raycastParams.IgnoreWater = true
	
	-- shoot a ray backwards
	local results2 = workspace:Raycast(ray.Origin + ray.Direction * 1000, -ray.Direction * 1000, raycastParams)

	-- create a second part using the backwards rays position and use the same color as the first part
	CreatePart(results2.Position, color)
end)

1 Like

was just about to propose the same solution:

local function opositeHit(origin: Vector3, direction: Vector3, result: RaycastResult): Vector3
	local params = RaycastParams.new()
	params.FilterType = Enum.RaycastFilterType.Whitelist
	params.FilterDescendantsInstances = { result.Instance }

	local newOrigin = origin + direction
	local newDirection = result.Position - newOrigin
	local result = workspace:Raycast(newOrigin, newDirection, params)

	return result and result.Position or nil
end

Only the way you create the opposite ray is kinda weird?
It should go from destination to origin - destination
destination = origin + direction

Red is original ray
Green your way
Blue is mine
image

1 Like

Was you talking to me?

Red is original ray
Green is my ray
Blue is your ray
Purple is hit position
image

you will get the same result for both mine or your ray

So @5uphi’s works pretty well, but how do I use yours @S3nt1ne3l? I tried to put it in a script in a part, but the ray was nil, and so the instance and position were errors.

local function opositeHit(origin: Vector3, direction: Vector3, result: RaycastResult): Vector3
	local params = RaycastParams.new()
	params.FilterType = Enum.RaycastFilterType.Whitelist
	params.FilterDescendantsInstances = { result.Instance }

	local newOrigin = origin + direction
	local newDirection = result.Position - newOrigin
	local result = workspace:Raycast(newOrigin, newDirection, params)

	return result and result.Position or nil
end

opositeHit(script.Parent.Position,script.Parent.CFrame.LookVector*100)

the function takes a third argument, the result of the previous casted ray. the previous result is needed for the hit position and hit part. And I changed it a bit and now you can use the hit part as a third argument too.

local function oppositeHit(origin: Vector3, direction: Vector3, result: RaycastResult|Instance): Vector3
	local params = RaycastParams.new()
	params.FilterType = Enum.RaycastFilterType.Whitelist
	params.FilterDescendantsInstances = { typeof(result) == "Instance" and result or result.Instance }

	local newOrigin = origin + direction
	local newDirection = origin - newOrigin
	local result = workspace:Raycast(newOrigin, newDirection, params)

	return result and result.Position or nil
end

local origin = script.Parent.Position
local direction = script.Parent.CFrame.LookVector*100
local result = workspace:Raycast(origin, direction)

local oppositeResult = result and oppositeHit(origin, direction, result)

-- or

local oppositeResult = result and oppositeHit(origin, direction, result.Instance)
1 Like