How to Make a Raycasting Lasergun
This is an intermediate, scripting related tutorial.
This tutorial is going to show you the built in ROBLOX raycasting, and a practical application of using this: building a lasergun.
Raycasting is basically checking for collisions in a direction. ROBLOX raycasting is used with the
workspace:FindPartOnRay(Ray,Ignore)
method. It takes two arguments. The first argument is a ray. To construct a ray you use
Ray.new(Start,Offset)
Start is the point the ray checks for collisions from, offset is how far to check away from it. The second argument is an instance. The ray will ignore all the stuff inside the descendants of that instance.
Tutorial
Getting The Base
The first thing we want to do is get an actual laser gun for us to script! We could make a tool, insert a handle and a mesh and all that or we could just use the ROBLOX paintball gun. Let's start by grabbing that from the toolbox under tools & weapons. Next just take all the stuff out of the tool except the handle and mesh. Now we have a nice tool that we can easily script. Insert a script from insert > object > script and start editing.
Equipping And Clicking
Next up we need to code our base. In our script let's start with this:
local Tool = script.Parent local User
These will be our starter variables. We didn't set a value for User, as it can change when a new person picks it up. Now we need to make it so that it'll do something when we equip. Let's connect the equipped event for the tool to a function. Note: You don't have to explicitly create a function for this, you can just put an unnamed function inside the :connect perimeters, like this:
local Tool = script.Parent local User Tool.Equipped:connect(function(mouse) end)
Now we have our function connected to the event, and we have access to the mouse because that's what equipped sends in. Now we can do almost the same thing with the button1down event in the mouse, except it doesn't send anything:
local Tool = script.Parent local User Tool.Equipped:connect(function(mouse) mouse.Button1Down:connect(function() end) end)
When we click the left button on the mouse, our event will be fired!
Raycasting
Now we need to create a ray, but first we should get the character of the player so the ray won't collide with them. When a tool is picked up or equipped it moves its parent into their character, so we can just do this:
local Tool = script.Parent local User Tool.Equipped:connect(function(mouse) User = Tool.Parent mouse.Button1Down:connect(function() end) end)
Now we're ready! Rays take two arguments, a start and an offset. The start is basically where the ray is coming from and the offset is how far to check off of the start. Let's create a ray between the tool handle and the mouse:
local Tool = script.Parent local User Tool.Equipped:connect(function(mouse) User = Tool.Parent mouse.Button1Down:connect(function() local Ray = Ray.new(Tool.Handle.CFrame.p, (mouse.Hit.p - Tool.Handle.CFrame.p).unit*300) end) end)
Now we have our ray! We made the offset with the direction between the tool handle and the mouse click position, and multiplied it by 300 so that it won't have too long of a range. Next we should check for a collision with game.Workspace:FindPartOnRay(Ray,Ignore). We'll supply our ray, and for ignore we'll use User. It returns two values, the hit part and the position it hit at. If it didn't hit anything it returns nil and the end point of the ray. Here's our code now:
local Tool = script.Parent local User Tool.Equipped:connect(function(mouse) User = Tool.Parent mouse.Button1Down:connect(function() local Ray = Ray.new(Tool.Handle.CFrame.p, (mouse.Hit.p - Tool.Handle.CFrame.p).unit*300) local Hit, Position = game.Workspace:FindPartOnRay(Ray, User) end) end)
Now we have two variables we can use to do damage and draw a laser.
Doing Damage And Drawing Lines
Now we need to make it check if it hit something, see if there's a humanoid and do damage if they don't have a forcefield. You most likely know how to do the first two things if you've come this far, but to stop it from doing damage if they have a forcefield we'll use the :TakeDamage(Damage) method on the humanoid. Here we go:
local Tool = script.Parent local User Tool.Equipped:connect(function(mouse) User = Tool.Parent mouse.Button1Down:connect(function() local Ray = Ray.new(Tool.Handle.CFrame.p,(mouse.Hit.p-Tool.Handle.CFrame.p).unit*300) local Hit,Position = game.Workspace:FindPartOnRay(Ray,User) if Hit then if Hit.Parent:FindFirstChild("Humanoid") then Hit.Parent.Humanoid:TakeDamage(30) end end end) end)
Excellent! If you fire at something with a humanoid it will take 30 damage. It's kind of hard to visualize the path though, so let's draw a laser. To do this we'll create a nice skinny custom part and stretch it to the length between the points, then set its CFrame to the point in the middle of the points. Here we go:
local Tool = script.Parent local User Tool.Equipped:connect(function(mouse) User = Tool.Parent mouse.Button1Down:connect(function() local Ray = Ray.new(Tool.Handle.CFrame.p,(mouse.Hit.p-Tool.Handle.CFrame.p).unit*300) local Hit,Position = game.Workspace:FindPartOnRay(Ray,User) if Hit then if Hit.Parent:FindFirstChild("Humanoid") then Hit.Parent.Humanoid:TakeDamage(30) end end local RayPart = Instance.new("Part",User) RayPart.Name = "RayPart" RayPart.BrickColor = BrickColor.new("Bright red") RayPart.Transparency = 0.5 RayPart.Anchored = true RayPart.CanCollide = false RayPart.TopSurface = Enum.SurfaceType.Smooth RayPart.BottomSurface = Enum.SurfaceType.Smooth RayPart.formFactor = Enum.FormFactor.Custom local Distance = (Position-Tool.Handle.CFrame.p).magnitude RayPart.Size = Vector3.new(0.2,0.2,Distance) RayPart.CFrame = CFrame.new(Position,Tool.Handle.CFrame.p) * CFrame.new(0,0,-Distance/2) game.Debris:AddItem(RayPart,0.1) end) end)
That'll draw a nice semi visible red line between the two points. We're using magnitude to determine the distance, and then setting the CFrame with two arguments which will point the CFrame from the first argument to the second. Then we offset it by negative the distance between the points divided by two, so we end up in the middle. Adding it to the debris will make it delete after 0.1 the second argument number of seconds.
Final Product
local Tool = script.Parent --Set a variable for the tool. local User --Create a nil value for the character which we can't find yet. Tool.Equipped:connect(function(mouse) --When it's equipped fire a function. User = Tool.Parent --Set the User variable to the character of the person using the tool. mouse.Button1Down:connect(function() --When the left mouse button is clicked fire a function. local Ray = Ray.new(Tool.Handle.CFrame.p,(mouse.Hit.p-Tool.Handle.CFrame.p).unit*300) --Make a the ray. local Hit,Position = game.Workspace:FindPartOnRay(Ray,User) --Check for collisions along the ray. if Hit then --If it hits something. if Hit.Parent:FindFirstChild("Humanoid") then --If the thing the ray hit has a humanoid. Hit.Parent.Humanoid:TakeDamage(30) --Do 30 damage if they don't have a forcefield. end --End the humanoid check. end --End the hit check. local RayPart = Instance.new("Part",User) --Create a part in the user. RayPart.Name = "RayPart" --Set its name. RayPart.BrickColor = BrickColor.new("Bright red") --Set its color. RayPart.Transparency = 0.5 --Set its transparency. RayPart.Anchored = true --Set whether it will fall or not. RayPart.CanCollide = false --Set whether people can walk though it or not. RayPart.TopSurface = Enum.SurfaceType.Smooth --Make it smooth on the top. RayPart.BottomSurface = Enum.SurfaceType.Smooth --Make it smooth on the bottom. RayPart.formFactor = Enum.FormFactor.Custom --Make it so it can be small. local Distance = (Position-Tool.Handle.CFrame.p).magnitude --Find the distance between the click and the gun. RayPart.Size = Vector3.new(0.2,0.2,Distance) --Set its size to the distance. RayPart.CFrame = CFrame.new(Position,Tool.Handle.CFrame.p) * CFrame.new(0,0,-Distance/2) --Move it halfway. game.Debris:AddItem(RayPart,0.1) --Add it to the debris. end) end)