A moving flashlight!
(Please note that this only works on R6 avatars, and they must be in first person.)
Hello, my fellow scripters!
Today, we are going to be talking about a flashlight, but not just any regular one. A moving one!
I’m not talking about a PointLight
glued to your camera, I mean a flashlight tool that points to your mouse direction… mostly.
I’ve seen many articles about this issue, so I decided to add a working solution that lots of people want, so I feel important. >:)
What most of those people request is a flashlight that points to where your camera is looking at. So that’s what we’re going to do, but the correct way.
Let’s start by adding a flashlight with a simple light script already implemented inside. The sound is the on/off sound that plays every time you toggle your flashlight, and the mesh which makes the tool’s Handle look like… a flashlight.
With this 4-line Server script, making the on/off switch functional:
script.Parent.Activated:Connect(function()
script.Parent.Handle.Light.Enabled = not script.Parent.Handle.Light.Enabled
script.Parent.Handle.Sound:Play()
end)
Now, how do we make this flashlight go up and down? Well, we’re not using sockets, or any other kind of complicated math. This will be possible by getting the Motor6Ds.
In every single player’s Torso, there are multiple Motor6Ds inside, and we’re going to be modifying one: the Right Shoulder
, aka your right arm’s shoulder (The arm which holds that tool). We can very simply change the .C0
’s orientation to make it look where your camera looks. So what do we change it to?
We need to add a local script and an event to communicate with the server. The client will be responsible for checking the player’s CurrentCamera
, then sends that information to the server, so that script can then set the arm’s orientation.
So, create a LocalScript named “Client”, and a RemoteEvent called “Event”, looking like this:
Now, we need to make the client-side check for the player’s camera, so the server-side can appropriately set the arm’s orientation:
— CLIENT SCRIPT —
Step 1:
local camera = workspace.CurrentCamera
This gets the player’s local camera, basically what the player sees.
Step 2:
local camera = workspace.CurrentCamera
camera:GetPropertyChangedSignal("CFrame"):Connect(function()
end)
This function runs every time the camera’s position or orientation changes. This saves lots of performance because if the player isn’t moving their camera, then it’ll be updating the arm’s orientation for no reason, in the server-side script. Having a for loop is not recommended unless there really is no other option.
Step 3:
local camera = workspace.CurrentCamera
camera:GetPropertyChangedSignal("CFrame"):Connect(function()
script.Parent.Event:FireServer()
end)
Now, the event inside our Tool gets fired every time that function gets fired. We need to pass on information to the server-side however.
Step 4:
local camera = workspace.CurrentCamera
camera:GetPropertyChangedSignal("CFrame"):Connect(function()
script.Parent.Event:FireServer(camera.CFrame)
end)
So now we’re getting the camera’s CFrame (position and orientation). There’s only one value in this that’s very important, so let’s search deeper into this property.
Step 5:
local camera = workspace.CurrentCamera
camera:GetPropertyChangedSignal("CFrame"):Connect(function()
script.Parent.Event:FireServer(camera.CFrame.LookVector)
end)
Now we’re looking at the orientation of the player’s camera. This will be useful since the server needs to know where we’re looking at, so it can then set the arm’s orientation.
(I probably said “arm’s orientation”, like, 6 times now.)
Step 6:
local camera = workspace.CurrentCamera
camera:GetPropertyChangedSignal("CFrame"):Connect(function()
script.Parent.Event:FireServer(camera.CFrame.LookVector.Y)
end)
Lastly, for this client script, we get the Y axis of the orientation. Since the player will be in first person, the X and Z angles won’t really matter, since the flashlight will naturally rotate with you, but it doesn’t go with you when you look up and down. So that’s what we’re currently doing, if you already didn’t know.
How this works is that Y axis’s value goes between 1 and -1. If you directly at the middle, the value will be 0, if you look up, it’ll be 1, if you look down, it’ll be -1, anywhere between will make the value go between. I hope you can understand that.
Now let’s go to the server script. This one will be longer, and much more difficult, but I’ll be with you every step of the way. Don’t worry!
— SERVER SCRIPT —
Step 1:
script.Parent.Activated:Connect(function()
script.Parent.Handle.Light.Enabled = not script.Parent.Handle.Light.Enabled
script.Parent.Handle.Sound:Play()
end)
script.Parent.Event.OnServerEvent:Connect(function(player,axis)
end)
Just after those 4 lines, we will make a function that runs every time that RemoteEvent is fired from the client script, giving us the player that fired it, and the “axis” variable, which is our Y axis orientation. So now we’re going to move the player’s arm.
Step 2 (Optional):
script.Parent.Activated:Connect(function()
script.Parent.Handle.Light.Enabled = not script.Parent.Handle.Light.Enabled
script.Parent.Handle.Sound:Play()
end)
script.Parent.Event.OnServerEvent:Connect(function(player,axis)
if axis > 1 or axis < -1 then player:Kick("Maliciously using RemoteEvents.") return end
end)
This is just an anti-cheat feature. You don’t have to add this, but this can be useful if your game is prone to exploiters.
Step 3:
script.Parent.Activated:Connect(function()
script.Parent.Handle.Light.Enabled = not script.Parent.Handle.Light.Enabled
script.Parent.Handle.Sound:Play()
end)
script.Parent.Event.OnServerEvent:Connect(function(player,axis)
if axis > 1 or axis < -1 then player:Kick("Maliciously using RemoteEvents.") return end
if player.Character then
end
end)
The script checks if the character exists before actually editing the arm’s orientation, to prevent any errors. Of course, you can use a pcall()
, but i’m not like other people. Or maybe most people use my method. Wait, do they? I’m gunna need to find out. ahem Anyway, moving on.
Step 4:
script.Parent.Activated:Connect(function()
script.Parent.Handle.Light.Enabled = not script.Parent.Handle.Light.Enabled
script.Parent.Handle.Sound:Play()
end)
script.Parent.Event.OnServerEvent:Connect(function(player,axis)
if axis > 1 or axis < -1 then player:Kick("Maliciously using RemoteEvents.") return end
if player.Character and player.Character:FindFirstChild(script.Parent.Name) then
end
end)
The script is now also checking if the Tool is equipped or not. You’re not going to be moving your arm without even holding your flashlight, you’ll just look like you’re waving your hand to someone.
Step 5:
script.Parent.Activated:Connect(function()
script.Parent.Handle.Light.Enabled = not script.Parent.Handle.Light.Enabled
script.Parent.Handle.Sound:Play()
end)
script.Parent.Event.OnServerEvent:Connect(function(player,axis)
if axis > 1 or axis < -1 then player:Kick("Maliciously using RemoteEvents.") return end
if player.Character and player.Character:FindFirstChild(script.Parent.Name) then
player.Character.Torso["Right Shoulder"]
end
end)
Now, we’re going to be looking for that right shoulder. This is basically a connection between your torso and right shoulder, you can change the position and orientation, (which is what we wanna do), and some other things which are irrelevant.
Step 6:
script.Parent.Activated:Connect(function()
script.Parent.Handle.Light.Enabled = not script.Parent.Handle.Light.Enabled
script.Parent.Handle.Sound:Play()
end)
script.Parent.Event.OnServerEvent:Connect(function(player,axis)
if axis > 1 or axis < -1 then player:Kick("Maliciously using RemoteEvents.") return end
if player.Character and player.Character:FindFirstChild(script.Parent.Name) then
player.Character.Torso["Right Shoulder"].C0
end
end)
Now we’re going to access the actual arm’s position and orientation, this .C0
is a CFrame, once again, a position and an orientation. This is getting tiring; saying things over and over.
Step 7:
script.Parent.Activated:Connect(function()
script.Parent.Handle.Light.Enabled = not script.Parent.Handle.Light.Enabled
script.Parent.Handle.Sound:Play()
end)
script.Parent.Event.OnServerEvent:Connect(function(player,axis)
if axis > 1 or axis < -1 then player:Kick("Maliciously using RemoteEvents.") return end
if player.Character and player.Character:FindFirstChild(script.Parent.Name) then
player.Character.Torso["Right Shoulder"].C0 = CFrame.new(player.Character.Torso["Right Shoulder"].C0.Position) * CFrame.Angles(0,math.rad(90),math.rad(axis*60))
end
end)
This post is taking me 1 hour already, so I’ll make this a long jump. What we’re doing now is creating a new CFrame, setting it’s position to what it already is, not modifying it, then the orientation… oh boy, what did I do?
The original orientation for the shoulder is (0,90,0)
, and if we change the Z axis to 60, it’ll move up, -60 and it’ll go down. So we’re using the axis and multiplying it by 60, so where we look will then make the arm go either up or down.
Step 8:
script.Parent.Activated:Connect(function()
script.Parent.Handle.Light.Enabled = not script.Parent.Handle.Light.Enabled
script.Parent.Handle.Sound:Play()
end)
script.Parent.Event.OnServerEvent:Connect(function(player,axis)
if axis > 1 or axis < -1 then player:Kick("Maliciously using RemoteEvents.") return end
if player.Character and player.Character:FindFirstChild(script.Parent.Name) then
player.Character.Torso["Right Shoulder"].C0 = CFrame.new(player.Character.Torso["Right Shoulder"].C0.Position) * CFrame.Angles(0,math.rad(90),math.rad(axis*60))
else
player.Character.Torso["Right Shoulder"].C0 = CFrame.new(player.Character.Torso["Right Shoulder"].C0.Position) * CFrame.Angles(0,math.rad(90),0)
end
end)
Just one more thing, if you unequip the flashlight your arm will stay where it was, either up or down. So we need to set it back to what it originally was.
— CONCLUSION —
So now we’ve finished. I have just told you step-by-step (mostly) to how to make a working moving flashlight. I tested this myself, and the flashlight does get a bit hidden if you look directly up or down, but at least the arm moves wherever your camera looks!?
I’ll be honest, I don’t think my tutorial was 100% clear or even correct, because I did research this, and I had to do about 40 attempts to get this right. The reason I did this is because there was nothing to help me, and I brute-forced my peanut brain to get this correct, and I eventually did, and I didn’t want anyone else to experience this mental breakdown, so I made this post.
And no, I’m not going to add the aftermath of the server and client scripts. You’re reading through this.
This took me 2 hours to type all out, so I hope this article is useful to someone. Please. Please make use of this. ;-;
OK, bye!
-DiamondDrencher1