Thrown knife not sticking properly to hit object

Hi there, Devforum. I’ve recently programmed a knife throwing system using attachments on the knife as raycasting points, but the problem is that when the knife lands, it won’t stay in a correct way.

How knifes from other games land:
image
image

How my knifes land:

Knife’s visuals code sample:

function throw(character,startPos,direction,maxRange,gravity,velocity)
	local lastPos = startPos
	local range = tonumber(maxRange)
	local startTime = tick()
	local spinTween = nil
	local rayhit = false

	local projectile = game:GetService("ReplicatedStorage").knifeProjectile:Clone()
	projectile.Parent = gameSettings.ProjectilesFolder
	projectile.Anchored = true
	projectile.CanCollide = false

	local blacklist = {character,workspace.ProjectilesFolder}
	local rayFilter = RaycastParams.new()
	rayFilter.FilterType = Enum.RaycastFilterType.Exclude

	while range >0 do
		if range <=0 then break end

		local timeLength = (tick()-startTime)

		local currentPos = startPos + (direction*timeLength)
		currentPos = applyGravity(currentPos,gravity,timeLength)

		local distance = (lastPos-currentPos).Magnitude
		range -= distance

		if projectile then
			local projectileOffset = CFrame.new(0, 0, -(lastPos-currentPos).magnitude/2)
			projectile.CFrame = (CFrame.new(lastPos,currentPos)*projectileOffset)
		end

		for _, point in pairs(projectile:GetChildren()) do
			if point:IsA("Attachment") and point.Name == "hitPoint" then
				local createdRay = workspace:Raycast(lastPos,point.WorldPosition-lastPos,rayFilter)
				if createdRay == nil then
					createdRay = workspace:Raycast(point.WorldPosition,lastPos-point.WorldPosition,rayFilter)
				end

				if createdRay and range >0 and rayhit == false then
					local hit = createdRay.Instance
					rayhit = true

					if spinTween then 
						spinTween:Cancel() 
						spinTween = nil 
						print("cancelling tween")
					end
					local projectileOffset = CFrame.new(0, 0, -(currentPos-createdRay.Position).magnitude/2)

					projectile.CFrame = (CFrame.new(currentPos,createdRay.Position)*projectileOffset)*CFrame.Angles(-90,0,0)
					projectile.Anchored = false
					projectile.CanCollide = false

					local weld = Instance.new("WeldConstraint",projectile)
					weld.Enabled = true
					weld.Part0 = projectile          
					weld.Part1 = hit

					task.wait(game:GetService("Players").RespawnTime/1.5)
					break
				end				
			end
			lastPos = currentPos
			local t = tick()
			repeat task.wait() until tick()-t >= 1/60
		end
		local t = tick()
		repeat task.wait() until tick()-t >= 1/60
		if projectile then
			projectile:Destroy()	
		end
	end
end

Any help is appreciated!

oh boy, i sure do hope I dont see any beautiful projectile scripts in her-OH MY GOODNESS

It so good to look at! Its just missing that hidden beauty like, idk, using the hit’s CFRAME and adding it to the knife’s to modify the knife’s orientation

(it would probably look like

--Set the knife pos to current pos, and have it face the thrower/player
projectile.CFrame = CFrame.new(currentPos,Character.Position)
--Optional relative rotations
local SwaggyRotator = CFrame.Angles(
math.rad(math.random(LOWEST ADJUSTMENT AMOUNT,MAX ADJUSTMENT AMOUNT)),--X offset
0,--Y offset
0)--Z offset
--[[Disclaimer; this wont work because im GUESSING that X is the
 orientation the will cause the knife to tilt back or forth towards the player.
It could be Y or Z! Definitely try both out and see which one causes the
knife to tile forwards and backwards!]]--
projectile.CFrame:ToWorldSpace(SwaggyRotator)

:smiley: )

(PS. you have CFrame.Angles(90,0,0) in your script. THIS WILL NOT WORK. CFrame.Angles ONLY accepts RADIANS! IT IS VERY WHINEY LIKE THAT!!!)

2 Likes

Maybe I’m stupid but I don’t see what’s wrong??

I used the code sample you’ve posted, and it probably did something, but not enough. Any ideas/tips on how to make sure that the knife ALWAYS has the right rotation and position?

my code sample:

local projectileOffset = CFrame.new(0, 0, -(lastPos-createdRay.Position).magnitude/2)
local rotateScale = CFrame.Angles(
	-math.random(90,105),--x offset
	0,--y offset
	0)--z offset

projectile.CFrame = (CFrame.new(lastPos,createdRay.Position)*projectileOffset)
projectile.CFrame:ToWorldSpace(rotateScale)
projectile.Anchored = false
projectile.CanCollide = false

local weld = Instance.new("WeldConstraint",projectile)
weld.Enabled = true
weld.Part0 = projectile          
weld.Part1 = hit

Hm. its way to random for the very small offsets youve given it.

What id do is print the orientation before and after the modification
And make the angle at which it can rotate be an easy 30 or 60 degrees. Because you have it either completely straight up, to a LITTLE bit towards the player. so it shouldnt be noticeable.

The script detects the last position (lastpos) and makes a new position (currentpos), so sometimes the knife goes trough the wall or ground, so it doesn’t matter about the orientation, its about how to make sure that the knife lands as it should be.

The only idea in my head is using another raycast when the knife lands in order to check if the knife is in a object, and if so, then the script will make sure that the knife is ontop of the part and correct the orientation.

Just do RunService.Heartbeat:Wait() here since it waits for the next frame i.e 1/60th of a second.

Try this for the position of the knife.

projectile.CFrame = (CFrame.new(currentPos, createdRay.Position):ToWorldSpace(CFrame.new(0, createdRay.Instance.Position.Y + (createdRay.Instance.Size.Y / 2), 0))

I’ve used the exact code sample you’ve send, and this is how it currently looks like:

Ahh I see, try this

projectile.CFrame = CFrame.new(createdRay.Instance.CFrame:ToWorldSpace(CFrame.new(0, createdRay.Instance.Position.Y + (createdRay.Instance.Size.Y / 2), 0)), createdRay.Position)

It shows an error which says that the first argument is supposed to be a Vector3, but it’s a CFrame.
image