Raycast returns inaccurate position

The blood part ceases to exist in an inaccurate position very far away from the blood trail part. This is my first attempt at messing with raycasts. I am trying to achieve a part that exists right wheret he blood trail finishes.

local raycastResult = workspace:Raycast(BloodTrail.Position, (TouchedHit.Position - BloodTrail.Position).Unit * 500, rayParams) -- updated method
bloodpart.CFrame = CFrame.new(raycastResult.Position, raycastResult.Position) * CFrame.Angles(math.rad(90),0,0)

What I’m looking for: Blood Splatter help
What I’m getting:

1 Like

Try swapping this:
bloodpart.CFrame = CFrame.new(raycastResult.Position, raycastResult.Position)
… with this:
bloodpart.CFrame = CFrame.new(raycastResult.Position, raycastResult.Position + raycastResult.Normal)

If you add the normal, you’ll get a proper look CFrame. I don’t advise setting the CFrame’s own position as the point for it to look at.

As far as positioning goes, there’s not enough info here to guarantee an answer. BloodTrail needs to be defined in this case. If you tell me exactly where BloodTrail.Position is at in the video (or provide a new video with better visuals) I think I can come up with an answer for that.

1 Like
BloodTrail.Position = Position -- this is a given functino argument
BloodTrail.Velocity = Vector3.new(math.random(v1.Value,v2.Value),math.random(v1.Value,v2.Value) + 30,math.random(v1.Value,v2.Value))

Also I did try normal but the blood part would sometimes exist inside the red part which i dont really want

1 Like

Are you using a .Touched event to initiate the raycast? I’m saying this because I saw “TouchedHit.Position - BloodTrail.Position” for the direction of the raycast

there are two things wrong with this approach
The first being mathematical, the TouchedHit.Position refers to the center of the hit part, not the position where it was hit, so you end up casting a ray that is pretty much sideways, and the raycast result position is far from the position where the bloodtrail hit the part.

The second thing is that you shouldn’t be using .Touched along with raycast, just pick one, unless you insist on getting the normal vector of the hit part.
If you do use .Touched, simply put the blood splatter at the position of the blood trail the moment the .Touched event fires
For more precise collision detection for projectiles such as the blood trail, I recommend using raycast to calculate the ballistics, and if you don’t want to do the math, use FastCastRedux (you can find the module in the toolbox)
Whenever the cast hits something, place the splatter at the result position and u could do some fancy CFrame stuff with the normal vector

Yes I am.

That’s what I did also, but the part spawns inside the touched part, which I don’t want, and the solution to this was to make the blood part appear above the part but I don’t know the math formula for it.

Well I don’t use .Touched event that much because it’s pretty unreliable, especially at higher speeds
For example, I’m scripting flight physics and aircraft go several hundred studs per second, and bullets thousands. I almost exclusively use raycast for collision detection, and for the blood trail, which is a ballistic projectile, I would recommend simulating its trajectory using raycast instead of built-in physics.

The .Touched event sometimes completely misses the collision and fails to fire or delays firing, which might be why the blood splatter spawns inside the hit part
But if it worked ideally, spawning the blood splatter at the blood trail position the moment the event fires should work fine, so long as you account for the size of the blood splatter so it’s not clipped halfway into the hitpart

I did use to account for the size but only for the raycast yet the part would still exist either far away from the blood trail or inside the hit part

Yes that’s because the direction of the raycast is incorrect which results in the wrong position
You could try putting the origin of the raycast back a bit to where the blood trail might have been, and then cast it towards the blood trail’s current position (should be touching the hit part because this happens in the .Touched event)

local origin = BloodTrail.Position - BloodTrail.AssemblyLinearVelocity.Unit
--there is a short interval of time during which something can move into position immediately after the bloodtrail passes and just happen to block the raycast
-- you could do BloodTrail.Position - BloodTrail.AssemblyLinearVelocity.Unit * .1 to make it even less likley

--I also noticed that you're using the Velocity property which is deprecated,
--and assemblylinearvelocity is not exactly its replacement,
--you'll have to read up on that it's too long to explain
local direction = BloodTrail.AssemblyLinearVelocity.Unit * 2 --a bit of extra length in case it barely misses the hitpart
--the bloodtrail should be in the filterdescendantsinstances table
local result = workspace:Raycast(origin, direction, params)

if result.Instance ~= TouchedHit:: BasePart then
    --this should not happen but can happen
    warn("Ray hit instance, ", result.Instance, ", is not the same as Touched event hit part!")
end

bloodpart.CFrame = CFrame.new(result.Position, result.Position + result.Normal)
--makes the lookvector of the bloodpart parallel to the surface's normal
--idk what dimensions of the bloodpart actually are so change this accordingly

However, I still highly recommend raycast projectile simulation, it is superior in literally every way. It would allow you to get the precise point of contact between the blood trail and whatever part it hit, it will not fail to detect a collision unlike with roblox physics, and way faster, which will eventually be a huge deal as more processes are added to the game

1 Like

This solved it, thank you for the tips and everything! this helped me learn about raycasts more!
image
About the velocity property, what should I change it to? Since it almost always fires in the same direction.

for your purposes, using “AssemblyLinearVelocity” is the exact same thing as Velocity, except with more letters to type

but if the bloodtrail involved multiple baseparts welded together, it’s a different story
welding puts baseparts into a common “assembly”, and AssemblyLinearVelocity actually refers to linear velocity at the center of mass of the assembly

the true replacement for BasePart.Velocity is actually BasePart:GetVelocityAtPosition(BasePart.Position)
But unless you’re making something that demands extremely precise physics calculations that involve fast rotating assemblies, the differences are negligible

but yeah if you want your code to be future proof, use AssemblyLinearVelocity

1 Like

Okay, thank you! I did some adjustements to the function and it works great! though, for some reason if Time is 1, the blood part won’t spawn.

for i = 1, Time do
		local BloodTrail = __REPLIC.Effects.BloodTrail:Clone() -- this is a part with trail
		BloodTrail.Name = "BL"
		BloodTrail.Position = Position
		BloodTrail.AssemblyLinearVelocity = Vector3.new(math.random(10,15),math.random(10,15) + math.random(25,30), math.random(10,15))
		BloodTrail.Parent = workspace
		C = BloodTrail.Touched:Connect(function(hit)
			if hit.Anchored and not tostring(hit):find("BL") then
				BloodTrail.Anchored = true
				warn("Touched: ".. hit.Name)
				local origin = BloodTrail.Position - BloodTrail.AssemblyLinearVelocity.Unit
				local direction = BloodTrail.AssemblyLinearVelocity.Unit * 2 --a bit of extra length in case it barely misses the hitpart
				local rayParams = RaycastParams.new()
				rayParams.IgnoreWater = true	
				local result = workspace:Raycast(origin, direction, rayParams)
				if result then
					local BloodPart = __REPLIC.Effects.BloodPool:Clone()
					BloodPart.Name = "BL2"
					local R = math.random(2,2)
					Tween:Create(BloodPart, TweenInfo.new(1), { Size = Vector3.new(R, R,0.5) }):Play()
					BloodPart.Anchored = true
					BloodPart.Color = Color3.fromRGB(76, 0, 1)
					BloodPart.CanCollide = false
					BloodPart.CanTouch = false
					BloodPart.CFrame = CFrame.new(result.Position, result.Position + result.Normal)
					BloodPart.Parent = workspace
					task.delay(4,function()
						Tween:Create(BloodPart, TweenInfo.new(4), {Transparency = 1}):Play()
					end)
					Debris:AddItem(BloodPart, 9)
				end
			end
			C:Disconnect()
		end)
		Debris:AddItem(BloodTrail, 4)
	end
for i = 1, Time do

this actually means

local i = 1
while i < Time do
   i += 1
end

loop doesn’t not run at all if i starts with the value equal to Time

in quite literally every other programming language, the convention is to have i = 0
but for some reason tables in lua starts index at 1

I still do for i = 0 tho, it just makes way more sense to me

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.