Oooh, something that’s an interesting subject!
It’s always fun to make tracking projectiles, and honestly I like how they work out! Although @KultDeeri’s suggestion may work out, it’s not good performance wise as you’re calling numerous amounts of Tweens which results in a lot of network to be taken in
The way how I usually create my projectiles, are mostly physics based as that’s probably the simplest you can get with them & they’re easy to workaround with once you get to know how they work! As @TheZanderius stated, Physics I wouldn’t say the best, but definitely are a great example to show how to create a Tracking Missile/Part! Keep in mind though that there are a few things that we’ll need to use in order to accomplish this, such as:
-
Learning how the Constraint Objects work, like LinearVelocity & AngularVelocity
-
Understanding a bit of Lua Programming to know how to constantly update where our Target is going using RunService, and CFrames
-
Using a simplified .Touched Event (For this example) to activate collisions when to make the Missile explode
But don’t worry, once we finish we should have a good result of illegal use of explosives & get sent to jail making our Rocket lock onto a specific target, then the rest should be easy from there!
Step 1: Creating our Instances
Now, our first step is creating our Instances here What we want to have are 2 things here:
- A Target that we want to lock onto (You can easily get one through the Rig Builder inside the Model tab)
- The Missile that’ll end all warfare
Dave: “I’m not ready to die!”
After we have those set up, we’ll have to add the following Instances to our Missile here:
-
LinearVelocity
to assign the velocity for the Part to properly move
-
AngularVelocity
to prevent the Part’s Rotation from constantly orientating
-
Attachment
to hook onto our LinearVelocity
that I’ll explain later
- Our
Script
that’ll handle all the magic
- (Optional) Add a
Sound
that’ll play upon impact, and when ended destroys the Missile
If we’ve done all these correctly, then it should look like this!
Now, before we move onto Step 2 here…We have to actually assign our LinearVelocity
instance to our Attachment
that we just created, as it requires 1 Attachment to hook onto cause otherwise our Part will just straight up fall in the V O I D
Make sure to click your LinearVelocity
object, check on its Properties, and click our Attachment
object! (Do the same with your AngularVelocity
as well, otherwise your Missile will move like a pendulum)
Set both of the MaxForce
& MaxTorque
properties of the Velocity Objects to math.huge
:
- LinearVelocity
- AngularVelocity
And if we’re done it and these properties show up, we can now move onto Step 2!
Step 2: The Scripting
Now here’s where things are gonna get very interesting, the scripting aspect is the most important part here as we want to accomplish 3 main things here:
- Checking for hit collisions via
.Touched
- Constantly looping so that the Missile changes direction when the Target moves
- Completely ruin Dave’s life (Aka exploding him)
Now, we’re only gonna use 1 service which is our RunService
that allows us to keep track with ROBLOX’s frame-by-frame loops, instead of rather just using our standardized “while true do” wait()/task.wait()
globals
local RS = game:GetService("RunService")
We’ll also want to go ahead and create a couple of custom properties for our part, so that we’re able to reference them easier later on within our script:
local MaxSpeed = 25 -- This is how fast the Velocity of the Part should go
local Exploded = false -- We will use this later within our RunService loop
local MainTarget = workspace:WaitForChild("Dummy") -- Dave is not going to see the light of day ever again
local Part = script.Parent -- Just a simple shortcut to reference our Missile here
local PartConnection -- This will be explained later on
Now onto our hit collision here; We want to make sure that we’re only checking for Models that should have Humanoid objects within them, so what the first thing we’d want to do is create a local function
that we’ll connect with a .Touched
Event here:
local function Touched(Hit)
end
- The first parameter of a .Touched
Event is the Hit Part that collided with it (Examples being the Right Arm of a Character, or a Car Tire from the Model of an Old Ford)
- This can throw back errors if not handled properly, such as no frequent checks
Now, since we want to check for the Target Model (As .Touched
gives only the Hit Part back), we will have to reference the Hit Part’s Parent to be able to obtain it that way:
local Target = Hit.Parent
if not Target then return end
And if say, there isn’t an exact Parent within the Part then we can go ahead and return
our function which prevents it from running any more line of code :L
Next, we’ll check for the Humanoid
of the Model:
local Hum = Target:FindFirstChildOfClass("Humanoid")
if Hum ~= nil then
PartConnection:Disconnect()
Exploded = true
for _, Part in pairs(Target:GetDescendants()) do
if Part:IsA("BasePart") then
Part:BreakJoints()
end
end
local Explosion = Instance.new("Explosion")
Explosion.Position = Part.Position
Explosion.Parent = workspace
Part.Explode:Play()
Part.Anchored = true
Part.Transparency = 1
Part.Explode.Ended:Wait()
Part:Destroy()
end
Now, this may look like a lot BUT we’ll go ahead and slowly discuss what each thing does here:
-
if Hum ~= nil
is just simply checking if the Humanoid we’re looking for inside our Model, is valid & able to be obtained
-
PartConnection:Disconnect()
is kinda like a debounce, except when you call this it only fires once and can never be used again (You can read more about it here if you’re interested)
-
We’re setting Exploded = true
to indicate that our Missile has exploded
-
for _, Part in pairs
, now here we’re obtaining every singular object within our Target using the GetDescendants()
method, and we can loop through every individual part to check if it’s a valid BasePart
object to be able to call BreakJoints()
on it (Or in simple terms, Model go bye bye)
-
We’re also creating an Explosion to add an effect to said “Missile”, to make it look more cool
-
Part.Explode
is referring to our Missile, so we’re obtaining our Explode
Sound and Playing that
-
Part.Anchored
& Part.Transparency
are both properties
-
We also call Part.Explode.Ended:Wait()
to yield until the Sound has ended, and then we can fully remove our Part
And lastly, we have to connect our Event
PartConnection = Part.Touched:Connect(Touched)
Overall, our Touched
function should look like this now:
local Part = script.Parent
local PartConnection
local function Touched(Hit)
local Target = Hit.Parent
if not Target then return end
local Hum = Target:FindFirstChildOfClass("Humanoid")
if Hum ~= nil then
PartConnection:Disconnect()
Exploded = true
for _, Part in pairs(Target:GetDescendants()) do
if Part:IsA("BasePart") then
Part:BreakJoints()
end
end
local Explosion = Instance.new("Explosion")
Explosion.Position = Part.Position
Explosion.Parent = workspace
Part.Explode:Play()
Part.Anchored = true
Part.Transparency = 1
Part.Explode.Ended:Wait()
Part:Destroy()
end
end
PartConnection = Part.Touched:Connect(Touched)
It’s looking good so far! But wait, how are gonna constantly update it? Well, remember that Exploded
variable we made a while back? We’re gonna use that within a while true do
loop, except replace the “loop” with our own variable “Exploded” instead
while not Exploded do
Part.CFrame = CFrame.new(Part.Position, MainTarget.HumanoidRootPart.Position)
Part.LinearVelocity.VectorVelocity = Part.CFrame.LookVector * MaxSpeed
RS.Stepped:Wait()
end
Now, since at the start we defined local Exploded = false
, we want to include the not
operator (Which is basically the opposite, so not Exploded == true
)
A couple things more here that shouldn’t be too hard to explain:
-
Part.CFrame
is changing its CFrame
(Orientational & Positional Values) so that it’s able to properly look at our Target, whilst keeping its position as well! The main idea here, is that in our example we’re going 2 parameters for this constructor here:
-
The first parameter is our Position we want our Missile to be, possibly in the center like Vector3.new(0, 0, 0)
or even up in the sky! (Vector3.new(0, 500, 0
)
-
The second parameter is the Position the Part should look at, now this one is important as the Part will face forward (You can add a Decal to your Part and see what Front Face your Missile is at), but it’ll still stay at our Position from our first parameter here
-
Part.LinearVelocity.VectorVelocity = Part.CFrame.LookVector
would move our Part in the current direction it’s facing
-
- Keep in mind!
LookVector
will always have a max of 1 in this scenario for each axis, so if we want to increase our speed we’ll have to multiply by our MaxSpeed
variable (Which is 25)
-
Each RunService Event is different on how it runs, RS.Stepped:Wait()
would fire for each frame prior to the Physics Simulation, so that’s what we’ll use for this example
Hopefully though, our full result should look like this:
local RS = game:GetService("RunService")
local MaxSpeed = 25
local Exploded = false
local MainTarget = workspace:WaitForChild("Dummy")
local Part = script.Parent
local PartConnection
local function Touched(Hit)
local Target = Hit.Parent
if not Target then return end
local Hum = Target:FindFirstChildOfClass("Humanoid")
if Hum ~= nil then
PartConnection:Disconnect()
Exploded = true
for _, Part in pairs(Target:GetDescendants()) do
if Part:IsA("BasePart") then
Part:BreakJoints()
end
end
local Explosion = Instance.new("Explosion")
Explosion.Position = Part.Position
Explosion.Parent = workspace
Part.Explode:Play()
Part.Anchored = true
Part.Transparency = 1
Part.Explode.Ended:Wait()
Part:Destroy()
end
end
PartConnection = Part.Touched:Connect(Touched)
while not Exploded do
Part.CFrame = CFrame.new(Part.Position, MainTarget.HumanoidRootPart.Position)
Part.LinearVelocity.VectorVelocity = Part.CFrame.LookVector * MaxSpeed
RS.Stepped:Wait()
end
And now, it’s time to move onto the exciting part! The Testing
Step 3: The Testing
Now, we have our Script alongside our Instances ready to fire up! If you’ve done everything correctly, then our Missile should move & target Dave; our test dummy!
We’ll do 2 tests: One without moving Dave, and one with moving Dave
Here’s Test #1:
As you can see here, our Missile has completely obliterated poor Dave & all his Body Parts have scattered everywhere so Test #1 was a success!
Now, let’s try Test #2 with moving Dave! (Spoiler Alert: Dave FREAKING DIES)
But as you can see from these 2 tests, both were successful! And it should hopefully give you a better insight on how to creating tracking parts!
If you do have any questions, please feel free to ask if you need to!