Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

Projectiles using Raycasting, looping to detect on hit?

I have created a system that takes the user input to fire a ray; This ray is then fired across the server and then firesallclients to produce a projectile which tweens to the end of the ray - or to the max distance the ray is allowed to travel..

I have attempted to now cause damage to a player if the projectile hits the player upon collision.. But the RayCast is causing the damage to happen before the projectile even hits the player..

Any ideas?

Local:

local UIS = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local Spells = game.ReplicatedStorage.Spells
local WandClicks = game.ReplicatedStorage.WandClicks
local Debris = game:GetService("Debris")


local Tool = script.Parent
local plr = game.Players.LocalPlayer
local Camera = workspace.CurrentCamera

local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
repeat wait() until plr:HasAppearanceLoaded()
raycastParams.FilterDescendantsInstances = plr.Character:GetDescendants()


local function getPosition(mouseXY)
	local x,y  = mouseXY.X,mouseXY.Y
	local ray = Camera:ViewportPointToRay(x,y)
	local result = workspace:Raycast(ray.Origin, ray.Direction*1000,raycastParams)
	if result then
		return result.Position
	else
		return ray.Origin+(ray.Direction*500)
	end
end

Tool.Equipped:Connect(function(Mouse)
	UIS.InputBegan:Connect(function(input,gp)
		if not gp then
			if input.UserInputType == Enum.UserInputType.MouseButton1 then
				print("send")
				WandClicks:FireServer(getPosition(UIS:GetMouseLocation()), script.Parent)
			end
		end
	end)
end)

Spells.OnClientEvent:Connect(function(Spell,SentFrom,SentTo)
	local SpellCloned = Spell:Clone()
	SpellCloned.CFrame = CFrame.new(SentFrom)
	
	local tweenInfo = TweenInfo.new(2*(SentFrom-SentTo).Magnitude/100)
	local goal = {
		["CFrame"] = CFrame.new(SentTo)
	}
	local tween = TweenService:Create(SpellCloned, tweenInfo, goal)
	SpellCloned.Parent = workspace
	
	tween:Play()
	
	
	tween.Completed:Connect(function(playbackState)
		if playbackState == Enum.PlaybackState.Completed then
			local HitEffect = game.ReplicatedStorage.SpellB
			local HClone = HitEffect:Clone()
			HClone.Position = SpellCloned.Position 
			SpellCloned:Destroy()
			HClone.Parent = workspace
			Debris:AddItem(HClone, 1)
		end
	end)
end)


Server:

--local x = game.ReplicatedStorage.Spell1:Clone()
--x.CFrame = CFrame.new(pos)
--x.Parent = workspace

local ReplicatedStorage = game.ReplicatedStorage
local Event = ReplicatedStorage.Spells
local Spell = ReplicatedStorage.Spell

ReplicatedStorage.WandClicks.OnServerEvent:Connect(function(player, mouseLocation, tool)
	local rayOrigin = Vector3.new(tool.Handle.Position.X, tool.Handle.Position.Y+2, tool.Handle.Position.Z)
	local SentFrom = rayOrigin
	
	local raycastParams = RaycastParams.new()
	raycastParams.FilterDescendantsInstances = player.Character:GetDescendants()
	raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
	
	local rayDirection = (mouseLocation-rayOrigin).Unit
	local distance, result, maxdistance = 0, nil, 200
---
	local count = 0
	repeat 
		
		if distance < maxdistance then
			
			result = workspace:Raycast(rayOrigin, rayDirection * 20, raycastParams)
			rayOrigin += (rayDirection * 20)
			distance += ((rayDirection * 20)).Magnitude
			
			if result then
				Event:FireAllClients(Spell,SentFrom,result.Position)
				if result.Instance.Parent:findFirstChild("Humanoid") then
					result.Instance.Parent:findFirstChild("Humanoid"):TakeDamage(50)
				else
					print("I hit something that wasn't a player - Whoops")
				end
				
				break
				
			end
			
		else
			
			Event:FireAllClients(Spell,SentFrom,rayOrigin)
			print("I hit nothing :( ")
			break
			
		end
		count += 1
		wait()
	until count >= 20
---	
end)


Thank you!

3 answers

✓ Accepted answer

Why don't you let the server script control how fast the tween plays, so that you can just fire all clients and have the server script wait that amount of time, and then damage the humanoid.

I mean, sure, players with high ping could experience issues, but that's just something you're going to have to trust the players on, that they won't have high expectations if their ping is over 200.

So slight modification to each script: local:

Spells.OnClientEvent:Connect(function(Spell,SentFrom,SentTo, Velocity)
    local SpellCloned = Spell:Clone()
    SpellCloned.CFrame = CFrame.new(SentFrom)

    local tweenInfo = TweenInfo.new(Velocity) --get velocity from server
    local goal = {
        ["CFrame"] = CFrame.new(SentTo)
    }
    local tween = TweenService:Create(SpellCloned, tweenInfo, goal)
    SpellCloned.Parent = workspace

    tween:Play()


    tween.Completed:Connect(function(playbackState)
        if playbackState == Enum.PlaybackState.Completed then
            local HitEffect = game.ReplicatedStorage.SpellB
            local HClone = HitEffect:Clone()
            HClone.Position = SpellCloned.Position 
            SpellCloned:Destroy()
            HClone.Parent = workspace
            Debris:AddItem(HClone, 1)
        end
    end)
end)

server:

ReplicatedStorage.WandClicks.OnServerEvent:Connect(function(player, mouseLocation, tool)
    local rayOrigin = Vector3.new(tool.Handle.Position.X, tool.Handle.Position.Y+2, tool.Handle.Position.Z)
    local SentFrom = rayOrigin

    local raycastParams = RaycastParams.new()
    raycastParams.FilterDescendantsInstances = player.Character:GetDescendants()
    raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

    local rayDirection = (mouseLocation-rayOrigin).Unit
    local distance, result, maxdistance = 0, nil, 200
---
    local count = 0
    repeat 

        if distance < maxdistance then

            result = workspace:Raycast(rayOrigin, rayDirection * 20, raycastParams)
            rayOrigin += (rayDirection * 20)
            distance += ((rayDirection * 20)).Magnitude

            if result then
                local vel = 2*(SentFrom-result.Position).Magnitude/100 --calculate velocity
                Event:FireAllClients(Spell,SentFrom,result.Position, vel) 
		wait(vel)--wait the amount of time it'd take before doing damage
                if result.Instance.Parent:findFirstChild("Humanoid") then
                    result.Instance.Parent:findFirstChild("Humanoid"):TakeDamage(50)
                else
                    print("I hit something that wasn't a player - Whoops")
                end

                break

            end

        else

            Event:FireAllClients(Spell,SentFrom,rayOrigin, 2*(SentFrom-rayOrigin).Magnitude/100)
            print("I hit nothing :( ")

            break

        end
        count += 1
        wait()
    until count >= 20
--- 
end)

Maybe try using this script, It is on an explosion.

ball = script.Parent
damage = 50

function onTouched(hit)


	local humanoid = hit.Parent:findFirstChild("Humanoid")
	if humanoid~=nil then
		tagHumanoid(humanoid)
		humanoid.Health = humanoid.Health - damage
		wait(2)
		untagHumanoid(humanoid)
		connection:disconnect()
	else
		damage = damage / 2
		if damage < 2 then
			connection:disconnect()
			ball.Parent = nil
		end
	end

	if math.random(1,3) == 2 then
		explosion = Instance.new("Explosion")
		explosion.BlastRadius = 12
		explosion.BlastPressure = 100000000
		explosion.Position = script.Parent.Position
		explosion.Parent = game.Workspace
		connection:disconnect()
		ball.Parent = nil
	end

end

function tagHumanoid(humanoid)
	
	local tag = ball:findFirstChild("creator")
	if tag ~= nil then
		local new_tag = tag:clone()
		new_tag.Parent = humanoid
	end
end


function untagHumanoid(humanoid)
	if humanoid ~= nil then
		local tag = humanoid:findFirstChild("creator")
		if tag ~= nil then
			tag.Parent = nil
		end
	end
end

connection = ball.Touched:connect(onTouched)

r = game:service("RunService")
t, s = r.Stepped:wait()
d = t + 5.0 - s
while t < d do
	t = r.Stepped:wait()
end

ball.Parent = nil

Or this way.

player = game.Players.LocalPlayer 
Tool = script.Parent
Ammo = 10
Mouse = player:GetMouse()
function Shoot()
if Ammo > 0 then	
local B = Instance.new("Part",workspace)
game.Debris:AddItem(B, 2)
B.formFactor = "Custom"	
B.Size = Vector3.new(3, 0.2, 0.2)
B.BrickColor = BrickColor.new("New Yeller")
B.CanCollide = false
B.CFrame = Tool.Main.CFrame
B.CFrame = CFrame.new(B.Position,Mouse.hit.p)
local v = Instance.new("BodyVelocity", B)
v.Velocity = B.CFrame.lookVector *90
v.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
	end
end

Tool.Activated:connect(Shoot)

1 comment

0

Thank you for your answer; However, It has no use of RayCasting or FireAllClients() - It supports nothing of my question.

I've been working on raycasting projectiles for a couple days now, and I'm not exactly sure what you're trying to achieve and why the tweens in your projectile are necessary but, I can say when it comes to projectiles I had and idea the other day encouraged by one of my good friends. You can make the ray inside the projectile easily. There are lots of code that I don't understand why you're going the extra mile, when you can just simplify it. Such as the one below,

tool.Handle.Position.X, tool.Handle.Position.Y+2, tool.Handle.Position.Z

When possible you can do this.

local rayOrigin = Handle.Position + Vector3.new(0, 2, 0)

Anyways, I made a successful damaging projectile the other day and I'm hoping it can put any type of inspiration into you without entirely spoon-feeding.

--//Variables\\--
local RayPart = Projectile

--//Raycast Parameters\\--
local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {Projectile, Player.Character}
rayParams.FilterType = Enum.RaycastFilterType.Blacklist
    

--//Function\\--
    
    while Casted do
        wait()
        local RayPartPosition = RayPart.Position - (Vector3.new(0,2,0))
        local hit, position = workspace:Raycast(RayPartPosition, Projectile.CFrame.lookVector*70, rayParams)
        if hit then
            if hit.Instance.Parent:FindFirstChild('Humanoid') or hit.Instance.Parent.Parent:FindFirstChild('Humanoid') then
                local hum = hit.Instance.Parent:FindFirstChild('Humanoid')
                hum:TakeDamage(20)
                Casted = false

I would also say start to send more of your variables over through the parameters and stop doing as much as you can on the Client because it's very risky. I hope this helps!

5 comments

0

Also if it's hitting it before the projectile touches the person then your ray is too long. Look at how you're positioning it!

0

Tho this is a perfect example of using RayCasting - It doesn't quite explain the boundaries of pushing the projectile through a client and acitvating the on-hit, the only information passed to the client is the position in which it tweens, the server still controls the actual ray and thus leading to a safe profound method....

1

I don't see how this answers any part of the question, and in fact does exactly what Frost has already done, which has been explained to be the whole problem. And FYI, absolutely nothing that is shown as being on the client is risky, and is in fact a good idea.

1

And one last thing, If you do not understand the question, you leave a comment, get clarification, and then answer. You do not throw a bunch of stuff together in hopes that it answers the question before understanding the question.

0

The end of the script is risky where he tweens. Most things on the client are easily manipulated by a lot of things. Maybe take the time to read. There are three golden rules about networking. Never trust the client. Never give the client too much power. Everything that can securely be handled on the client should be handled on the client."

This discussion is preserved as it was. New answers, comments, and votes are closed.