Raycasting to find nearest part [Un-Solved]

Hello, So I’ve been working on a web swinging type game and I wanted to know how to make auto-aim Using RopeConstraints I’ve already done this before but with springs and it works perfectly but I’ve already made a new script that generates a part above the character’s head and allows the rope to attach to it i was wondering how i would change this by using this reference script:

local player = game.Players.LocalPlayer
local BuildingFolder = workspace.buildings
local Radius = 100

local OP = OverlapParams.new()
OP.FilterDescendantsInstances = {BuildingFolder}
OP.FilterType = Enum.RaycastFilterType.Whitelist
OP.MaxParts = math.huge

local ClosestDistance = Radius
local ClosestBuilding = nil

for i, v in pairs(workspace: GetPartBoundsInRadius(player.Character.HumanoidRootPart.Position, Radius, OP)) do
		if (v.Position - player.Character.HumanoidRootPart.Position).Magnitude < ClosestDistance and v.Parent == BuildingFolder then
			ClosestDistance = (v.Position - player.Character.HumanoidRootPart.Position).Magnitude
			ClosestBuilding = v
		end
	end

	print(ClosestBuilding)

Which works very nicely so instead of just printing the nearest part to the player how would I make it show A target for the player that the player would be able to swing on I’ve been told this could be done but Each time I try to implant or add it to the script it just breaks so I have two scripts a local script Parented to “StarterCharacterScripts” and a server script parented to “ServerScriptService” and an event in ReplicatedStorage There’s also a folder in the workspace Named “Objects” which the part the the rope attaches to is spawned above the players head

LocalScript:

local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local HapticService = game:GetService("HapticService")
local TweenService = game:GetService("TweenService")

local isHapticSupported = HapticService:IsVibrationSupported(Enum.UserInputType.Gamepad1)

local swingEvent = ReplicatedStorage:WaitForChild("Swing")

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()

local camera = workspace.CurrentCamera

local cameraShaker = require(ReplicatedStorage:WaitForChild("CameraShaker"))

local swingAnimation = character:WaitForChild("Humanoid"):LoadAnimation(script:WaitForChild("Swing"))

local animationsFolder = script:WaitForChild("Animations")

local cooldown = false

local currentWeb = nil

local speed = 70

local swinging = false
local inAir = false

local largeSupported = false
local smallSupported = false

local cameraTweenIn = TweenService:Create(camera, TweenInfo.new(0.3), {FieldOfView =  110})
local cameraTweenOut = TweenService:Create(camera, TweenInfo.new(0.5), {FieldOfView =  70})

local mobileUI = player.PlayerGui:WaitForChild("Mobile")

local camShake = cameraShaker.new(Enum.RenderPriority.Camera.Value, (function(shakeCFrame)
	
	camera.CFrame = camera.CFrame * shakeCFrame
end))

camShake:Start()

character:FindFirstChild("Humanoid").StateChanged:Connect(function(oldState, newState)
	
	character:FindFirstChild("Humanoid"):SetStateEnabled(Enum.HumanoidStateType.Ragdoll, false)
	character:FindFirstChild("Humanoid"):SetStateEnabled(Enum.HumanoidStateType.FallingDown, false)
end)

if isHapticSupported then
	
	largeSupported = HapticService:IsMotorSupported(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Large)
	smallSupported = HapticService:IsMotorSupported(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Small)
end

local function PlayTrick(anim)
	
	local trickAnimation = character:WaitForChild("Humanoid"):LoadAnimation(animationsFolder:FindFirstChild(anim))
	
	trickAnimation:Play()
end

local function Swing()
		
	cooldown = true

	swingEvent:FireServer("Shoot")

	if not character:WaitForChild("HumanoidRootPart"):FindFirstChild("SwingForce") then

		local force = Instance.new("BodyForce", character.HumanoidRootPart)
		force.Name = "SwingForce"

		force.Force = camera.CFrame.LookVector * Vector3.new(speed, 0, speed)
	end

	camShake:Shake(cameraShaker.Presets.Bump)

	swingAnimation:Play()

	cameraTweenIn:Play()

	HapticService:SetMotor(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Large, 1)
	
	wait(0.15)

	HapticService:SetMotor(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Large, 0)
end

local function Release()
	
	swingEvent:FireServer("Release")
	
	if character:WaitForChild("HumanoidRootPart"):FindFirstChild("SwingForce") then
		
		character:WaitForChild("HumanoidRootPart"):FindFirstChild("SwingForce"):Destroy()
	end
	
	swingAnimation:Stop()

	cameraTweenOut:Play()
	
	local randomTrick = math.random(1, #animationsFolder:GetChildren())
	
	PlayTrick(randomTrick)
	
	HapticService:SetMotor(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Large, 1)

	wait(0.15)

	HapticService:SetMotor(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Large, 0)
	
	wait(0.2)
	
	cooldown = false
end

UserInputService.InputBegan:Connect(function(key)
	
	if key.KeyCode == Enum.KeyCode.E or key.KeyCode == Enum.KeyCode.ButtonR2 then
		
		if not currentWeb and not cooldown then
			
			if character:FindFirstChild("Humanoid").FloorMaterial == Enum.Material.Air then
				
				Swing()
			end
		end
	end
end)

UserInputService.InputEnded:Connect(function(key)

	if key.KeyCode == Enum.KeyCode.E or key.KeyCode == Enum.KeyCode.ButtonR2 then
		
		if currentWeb and cooldown then
			
			Release()
		end
	end
end)

mobileUI.Swing.MouseButton1Down:Connect(function()
	
	if not currentWeb and not cooldown then

		if character:FindFirstChild("Humanoid").FloorMaterial == Enum.Material.Air then

			Swing()
		end
	end
end)

mobileUI.Swing.MouseButton1Up:Connect(function()
	
	if currentWeb and cooldown then

		Release()
	end
end)

swingEvent.OnClientEvent:Connect(function(web)
	
	currentWeb = web
end)

game:GetService("RunService").Heartbeat:Connect(function()
	
	if currentWeb then
		
		HapticService:SetMotor(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Small, 0.5)
	else
		HapticService:SetMotor(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Small, 0)
	end
end)

Server Script:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local swingEvent = ReplicatedStorage:WaitForChild("Swing")

game.Players.PlayerAdded:Connect(function(plr)
	
	plr.CharacterAdded:Connect(function(character)
		
		local rightAttach = Instance.new("Attachment", character.RightHand)
		rightAttach.Name = "WebAttachment"
	end)
end)

swingEvent.OnServerEvent:Connect(function(player, ins)
	
	if ins == "Shoot" then
		
		local part = Instance.new("Part", workspace.Objects)
		
		part.Name = player.Name.."_SwingPart"
		
		part.CFrame = player.Character.HumanoidRootPart.CFrame * CFrame.new(0, 50, -60)
		
		part.CanCollide = false
		part.Anchored = true
		part.Transparency = 1
		
		local web = Instance.new("RopeConstraint")
		
		web.WinchEnabled = true
		web.WinchForce = 10000
		web.WinchResponsiveness = 34
		web.WinchSpeed = 2
		web.WinchTarget = 2.9
		web.Restitution = .38
		web.Length = 600
		
		local swingAttachment = Instance.new("Attachment", part)
		
		swingAttachment.WorldPosition = part.Position
		
		local webDistance = (player.Character.HumanoidRootPart.Position - part.Position).Magnitude + 2
		
		web.Length = webDistance
		
		web.Visible = true
		
		web.Color = BrickColor.new("Institutional white")
		
		web.Attachment0 = player.Character.RightHand:FindFirstChild("WebAttachment")
		web.Attachment1 = swingAttachment
		
		web.Parent = player.Character.HumanoidRootPart
		
		ReplicatedStorage:WaitForChild("Swing"):FireClient(player, web)
		
		local connection
		connection = game:GetService("RunService").Heartbeat:Connect(function()
			
			local newLength = (player.Character.HumanoidRootPart.Position - web.Attachment1.WorldPosition).Magnitude + 2
			
			if web.Length > newLength then 
				
				web.Length = newLength
			end
			
			web.Destroying:Connect(function()
				
				connection:Disconnect()
			end)
		end)
		
	elseif ins == "Release" then
		
		if player.Character.HumanoidRootPart:FindFirstChild("RopeConstraint"):FindFirstChild("Attachment1") then
			
			player.Character.HumanoidRootPart:FindFirstChild("RopeConstraint"):FindFirstChild("Attachment1"):Destroy()
		end
		
		if player.Character.HumanoidRootPart:FindFirstChild("RopeConstraint") then

			player.Character.HumanoidRootPart:FindFirstChild("RopeConstraint"):Destroy()
		end
		
		if workspace.Objects:FindFirstChild(player.Name.."_SwingPart") then
			
			workspace.Objects:FindFirstChild(player.Name.."_SwingPart"):Destroy()
		end
		
		ReplicatedStorage:WaitForChild("Swing"):FireClient(player, nil)
	end
end)

1 Like

You might be better off using something along the lines of this, and raycast to detect if the part can be seen:

local nearestPart = nil

for _, i in workspace:GetChildren() do
    if i:IsA("Part") then -- You will probably need to change this
        if nearestPart then
            if (game.Players.LocalPlayer.Character.HumanoidRootPart.Position - i.Position).Magnitude < (game.Players.LocalPlayer.Character.HumanoidRootPart.Position - nearestPart.Position).Magnitude then
            -- if raycast sees it then
                nearestPart = i
            -- end
            end
        end
        else
            -- if raycast sees it then
                nearestPart = i
            -- end
    end
end
2 Likes

Yep using a position check first is easier,

To make it more efficient rather than searching through the entire workspace you can categorize them in an octree as a node with a position value attached…

Octree:RadiusSearch(position, radius)

Thanks for the reply, but it just does the same thing I used as a reference And it also gives me an error.


I added two parts to the workspace just for an example. What I mean by using raycasting to find the nearest part to the player and attaching a rope constraint is that I’ve already done this by using springs, which @5uphi helped me make in some previous posts. Here’s the script for the springs

local runService = game:GetService("RunService")
local userInputService = game:GetService("UserInputService")
local character = script.Parent
local rootPart = character:WaitForChild("HumanoidRootPart")
local camera = workspace.CurrentCamera
local targetValue = game.ReplicatedStorage:WaitForChild("Target")
local selectedValue = game.ReplicatedStorage:WaitForChild("Selected")
local attachmentParts = workspace:WaitForChild("Attachments"):GetChildren()
local maxSwingRange = 100

-- every frame look for the best target and set game.ReplicatedStorage.Target
runService.Heartbeat:Connect(function(deltaTime)
	local selectedPart = nil
	local selectedMagnitude = math.huge
	for i, child in ipairs(attachmentParts) do
		local magnitude = (child.Position - rootPart.Position).Magnitude
		if magnitude > selectedMagnitude then continue end
		if magnitude > maxSwingRange then continue end
		if selectedValue.Value == child then continue end
		local viewVector, inView = camera:WorldToViewportPoint(child.Position)
		if inView == false then continue end
		selectedPart = child
		selectedMagnitude = magnitude
	end
	targetValue.Value = selectedPart
end)

-- when MouseButton1 set game.ReplicatedStorage.Selected to game.ReplicatedStorage.Target
userInputService.InputBegan:Connect(function(i,t)
	if t then return end
	if i.KeyCode == Enum.KeyCode.E   then
		if targetValue.Value == nil then return end
		selectedValue.Value = targetValue.Value

	end
end)

And it also has some values in ReplicatedStorage as well as a billboard gui script located in starterplayerscripts

local playerGui = game.Players.LocalPlayer:WaitForChild("PlayerGui")
local billboardGui = playerGui:WaitForChild("BillboardGui")
local targetValue = game.ReplicatedStorage:WaitForChild("Target")

-- when game.ReplicatedStorage.Target changes set the billboardGui Adornee
targetValue.Changed:Connect(function(value)
	billboardGui.Adornee = value
end)

I tried using this as a reference but would keep getting errors and it would just break the scripts If you can use the reference and change the script shown above and make it work, it would be nice. And I completely understand if you’re not interested. I appreciate your efforts and time.

this is what it should look like using springs the mechanics are fully functional but this time i want to use ropes instead of springs

That’s because you didn’t add this part:

If you use meshes then you will have to add this but with meshpart / whatever you use. (Or use Objects:GetChildren() instead)
Because obviously, you can’t get the position of a folder. As the error clearly states.

1 Like

Thanks also I found a solution by just using an event and adding some other stuff Have a nice day!