Hey all.
First off I’d like to apologize for how long this took. I had some other stuff to do, but honestly there was a lot of free time where I could have worked on this, but didn’t. But that was then, and this is now, and now, I present to you a solution!
Tool Solution
Before we begin, I’d like to implement one last suggestion by @Hest_z. Let’s try out their solution before we take a look at mine.
All we need to do is change the tool’s local script into a normal script and set the Character value when the ball is touched.
local t = 1;
local Character=nil -- Should be nil
local bball_tool = script.Parent
local bball_handle = script.Parent:WaitForChild("Handle")
local hoop = workspace.Net.Hoop
bball_tool.Equipped:Connect(function()
Character=bball_tool.Parent -- once equip get the character
end)
bball_tool.Activated:Connect(function()
local g = Vector3.new(0, -game.Workspace.Gravity, 0);
local x0 = Character.HumanoidRootPart.CFrame * Vector3.new(0, 2, -2)
-- calculate the v0 needed to reach mouse.Hit.p
local v0 = (hoop.Position - x0 - 0.5*g*t*t)/t;
-- have the ball travel that path
bball_tool.Parent = game.Workspace
bball_handle.AssemblyLinearVelocity = v0;
bball_handle.CFrame = CFrame.new(x0);
end)
Note that I moved the target part hoop to a model Net.
Look at that! We can successfully throw the tool itself and retrieve it after. Everyone say, “thank you @Hest_z!”
Custom Welding Solution
For those of you in a rush, please go ahead and implement @Hest_z’s solution and keep up the good work. But if you have the time, I would like to present my custom welding solution to those who are curious.
As I discussed earlier, my goal is to weld a part to the player and write a scrip that will destroy the grip before launching the part.
Because I won’t be using a Tool (which is stored in StarterPack, thus making it client-sided), and because the ball is a server-wide entity, I’ll need to separate the input of the player from the output of the ball being launched. We can do this using a localscript for the input, a script for the ball’s behavior, and a remoteEvent that will tie the two together.
Let’s set up our workspace.
Here we have our ball and a script named
BBallHandler that will do all the physics stuff.

This will be the script that handles input.

And this will be our RemoteEvent.
Handling Input
When we get the player input (let’s use the E key), we want the script to send a signal to the ball to launch itself. This is how we do that:
local UIS = game:GetService("UserInputService")
local remoteEvent = game.ReplicatedStorage.RemoteEvent
-- player input
UIS.InputBegan:Connect(function(input, gameProcessed)
-- only trigger input if player isn't typing in chat
if gameProcessed then return end
-- tell BBallHandler to throw ball when player presses the 'E' key
if input.KeyCode == Enum.KeyCode.E then
remoteEvent:FireServer()
end
end)
For those who are unaware, the line if gameProcessed then return end basically prevents us from calling this function if the player types the letter ‘e’ in the chat. So if someone wants to type “electricity” or something with the ball, they won’t unintentionally shoot it.
When you use remoteEvent:FireServer(), you tell the RemoteEventobject to fire a signal to be received by any server script that is listening, which, in our case, will be BallHandler.
Welding the ball to the player
Meanwhile, in our BBallHandler script, we can set up all our necessary variables for now.
local bball = script.Parent
local Players = game:GetService("Players")
local t = 1
local hoop = workspace.Net.Hoop
local remoteEvent = game.ReplicatedStorage.RemoteEvent
local motor6d = nil
As suggested by @satqx1, we will be using a Motor6D to weld the ball to the player’s right arm. motor6d will eventually hold that Motor6D, but for now we will set it as nil because we don’t have that information right away.
We also have a variable remoteEvent that references the RemoteEvent object that we put in ReplicatedStorage. We will use this later to receive the signal sent from InputHandler to launch the ball.
So… how do we weld an object using Motor6D?
Documentation defines Motor6D as an object that “joins two BaseParts (Part0 and Part1) together in an animatable way.” This is nearly identical to a Weld, which “hold[s] two objects together in a relative position, regardless of whether they’re touching.” In most cases, either is fine, but Motor6D works for this project specifically because it can be used for animations.
As stated, Motor6D needs two parts to work:
Part0, which is the first part that the joint connects. This will be the ball.
Part1, the second part that the joint connects. This will be the the player’s right hand.
But how do we tell our motor6D variable which part we want to apply to Part0 and Part1? Well, Roblox already has a built in function called Touched that will fire a signal when an object is, well, touched. So, in BBallHandler, we can put
bball.Touched:Connect(function(hit)
end)
Here, I put a parameter into the function so that we can get whatever the ball is touching. You can write something like print(tostring(hit)) you’ll be able to see everything in the output box. This is great and all, but we’ve only solved one part of the touching problem. The value hit is indiscriminate, so now we need to actually differentiate a player from every other object. Fortunately for us, Roblox already has a tool for this: GetPlayerFromCharacter. This function allows us access the player’s Character and all the data we need from it.
For our case, we’ll use Players:GetPlayerFromCharacter(hit.Parent) to see if hit is a player. And because this function will return nil if no player exists, then we can add this as:
bball.Touched:Connect(function(hit)
if Players:GetPlayerFromCharacter(hit.Parent) then
end
end)
This way we can have the ball check if a player is touching it when a touch event occurs. Awesome! Now we have a way to set Part0 and Part1 to the right objects 100% of the time.
motor6d = Instance.new("Motor6D")
motor6d.Part0 = bball
motor6d.Part1 = hit.Parent:WaitForChild("Right Arm")
motor6d.C0 = motor6d.C1 * CFrame.new(0, 2, 0)
motor6d.Parent = bball
Here we set motor6d as a new instance of Motor6D, parent Part0 to the ball, and then parent Part1 to the "Right Arm" child of the player (hit.Parent). We can also alter the CFrame of the ball by using the C0 property. In our case, we set it to the location of the player’s hand by getting the CFrame of Part1 through the C1 property, then offsetting the y-axis by 2 studs. Lastly we parent motor6d to the ball.
One last thing before we’re done with this part; we need to add a debounce so that the player doesn’t accidentally fire off multiple touched events. We want to check if a value is false, set it to true, wait a moment, and then set it to false again. In this case our value will be the “Touched” attribute. We can use bball:GetAttribute("Touched") to check if “Touched” is false, and then we can set it to true/false using bball:SetAttribute("Touched", boolean)
All in all, we’re left with this as our final solution to the welding.
bball.Touched:Connect(function(hit)
if Players:GetPlayerFromCharacter(hit.Parent) then
if not bball:GetAttribute("Touched") then
bball:SetAttribute("Touched", true)
motor6d = Instance.new("Motor6D")
motor6d.Part0 = bball
motor6d.Part1 = hit.Parent:WaitForChild("Right Arm")
motor6d.C0 = motor6d.C1 * CFrame.new(0, 2, 0)
motor6d.Parent = bball
task.wait(1)
bball:SetAttribute("Touched", false)
end
end
end)
Sweet! Now we’ll move on to actually firing the ball.
Throwing the Ball
Earlier we decided that we needed a local script to handle input, and we worked out a small program that will fire a RemoteEvent when we press ‘E.’ However, while the signal is sent, nothing in our game actually cares about that signal. This is where local remoteEvent = ReplicatedStorage.RemoteEvent definied in BBallHandler comes into play. We’ll use the OnServerEvent function to execute a function when we call FireServer() from our local script.
remoteEvent.OnServerEvent:Connect(function(player)
end)
FireServer has an implicit parameter player. An implicit parameter a type of value that’s passed automatically by the system/runtime rather than explicitly by the programmer. This is especially useful for us because we can get the player who activated the FireServer function through this parameter, which we need to calculate the initial position for the velocity of the ball.
local character = player.Character or player.CharacterAdded:Wait()
local HRP : Part = character:WaitForChild("HumanoidRootPart")
-- calculate initial position and velocity
local g = Vector3.new(0, -workspace.Gravity, 0)
local x0 = HRP.CFrame * Vector3.new(0, 2, -2)
local v0 = (hoop.Position - x0 - 0.5*g*t*t)/t;
Now all that’s left is to throw the ball. Before we throw the ball, we want to destroy the Motor6D so we, the player, don’t get flung. We’ll also disable CanTouch until the ball is thrown, after which we’ll enable CanTouch again.
-- throw ball
bball.CanTouch = false
motor6d:Destroy()
bball.CFrame = CFrame.new(x0)
bball.AssemblyLinearVelocity = v0
task.wait(1)
bball.CanTouch = true
motor6d = nil
We set motor6d back to nil so we can’t access motor6d again until we touch the ball. Speaking of which, we want to make sure that the ball can only be thrown if the ball is being held by a player. We’ll check if Part1, which should be the player, exists. If it doesn’t, then the ball shouldn’t be activated if we press the ‘E’ key.
if motor6d.Part1 == player.Character:WaitForChild("Right Arm") then
So with this check in mind, the part of the script that throws the ball should look like:
-- throw ball
remoteEvent.OnServerEvent:Connect(function(player)
if motor6d.Part1 == player.Character:WaitForChild("Right Arm") then
local character = player.Character or player.CharacterAdded:Wait()
local HRP : Part = character:WaitForChild("HumanoidRootPart")
-- calculate initial position and velocity
local g = Vector3.new(0, -workspace.Gravity, 0)
local x0 = HRP.CFrame * Vector3.new(0, 2, -2)
local v0 = (hoop.Position - x0 - 0.5*g*t*t)/t;
-- throw ball
bball.CanTouch = false
motor6d:Destroy()
bball.CFrame = CFrame.new(x0)
bball.AssemblyLinearVelocity = v0
task.wait(1)
bball.CanTouch = true
motor6d = nil
end
end)
Great! We have now solved the problem of welding the ball to the player, as well as throwing the ball when the player presses a button.
InputHandler
local UIS = game:GetService("UserInputService")
local remoteEvent = game.ReplicatedStorage.RemoteEvent
-- player input
UIS.InputBegan:Connect(function(input, gameProcessed)
-- only trigger input if player isn't typing in chat
if gameProcessed then return end
-- tell BBallHandler to throw ball when player presses the 'E' key
if input.KeyCode == Enum.KeyCode.E then
remoteEvent:FireServer()
end
end)
BBallHandler
-- variables
local bball = script.Parent
local Players = game:GetService("Players")
local t = 1
local hoop = workspace.Net.Hoop
local remoteEvent = game.ReplicatedStorage.RemoteEvent
local motor6d = nil
-- functions
-- weld ball to player's hand
bball.Touched:Connect(function(hit)
if Players:GetPlayerFromCharacter(hit.Parent) then
if not bball:GetAttribute("Touched") then
bball:SetAttribute("Touched", true)
motor6d = Instance.new("Motor6D")
motor6d.Part0 = bball
motor6d.Part1 = hit.Parent:WaitForChild("Right Arm")
motor6d.C0 = motor6d.C1 * CFrame.new(0, 2, 0)
motor6d.Parent = bball
print("Now equipping: " .. tostring(motor6d))
task.wait(1)
bball:SetAttribute("Touched", false)
end
end
end)
-- throw ball
remoteEvent.OnServerEvent:Connect(function(player)
if motor6d.Part1 == player.Character:WaitForChild("Right Arm") then
local character = player.Character or player.CharacterAdded:Wait()
local HRP : Part = character:WaitForChild("HumanoidRootPart")
-- calculate initial position and velocity
local g = Vector3.new(0, -workspace.Gravity, 0)
local x0 = HRP.CFrame * Vector3.new(0, 2, -2)
local v0 = (hoop.Position - x0 - 0.5*g*t*t)/t;
-- throw ball
bball.CanTouch = false
motor6d:Destroy()
bball.CFrame = CFrame.new(x0)
bball.AssemblyLinearVelocity = v0
task.wait(1)
bball.CanTouch = true
motor6d = nil
end
end)
Alright, let’s test this out. I’ll showcase it with two players just to make sure that no data carries over from Player1 to Player2 or anything like that.
Hooray! We did it! We’ve successfully recreated shooting a virtual basketball!
I’d like to thank everyone who responded to this post and to you, the reader, for making it this far. I hope I haven’t been too verbose; I really enjoy tutorials that break down the thought process behind everything instead of just throwing a code on the screen. I hope that you’ve gotten something out of this forum and my rambling.
Have a great day, and good luck on your project!