Fastcast projectile guidance target vector becomes NaN?

If this is any vague, sorry.

  1. What do you want to achieve?
    I want the projectile to continue forward when it loses the target.

  2. What is the issue?
    Projectile doesn’t go straight; targetVector becomes NaN

  3. What solutions have you tried so far?
    I have tried using Roblox’s AI assistant and researching, but I’ve come up with no true solutions.
    The AI gave me this, it fixes the whole script breaking which I was having problems with before, though the projectile just veers off into a different direction instead of straight forward like it’s meant to.

Roblox AI revision (didn’t work)

targetVector = CFrame.new(CF.Position, CF.Position * 1000).lookVector.Unit

Guidance Module

local computeGuidance = function(cast)
	local CF = cast.RayInfo.CosmeticBulletObject.CFrame

	local dist = (CF.Position - cast.UserData.targetPos).Magnitude

	local targetVector
	
	local velocity
	velocity = velocity or cast:GetVelocity()
	
	if cast.UserData.target == nil then
		targetVector = CFrame.new(CF.Position, CF.Position * 1000).lookVector.Unit
		print(targetVector)
		
	elseif cast.UserData.target ~= nil then
		local targetVelocity

		if cast.UserData.target:IsA("Attachment") then
			targetVelocity = cast.UserData.target.Velocity.Value
		else
			targetVelocity = cast.UserData.target.Velocity
		end

		local combined = targetVelocity - velocity

		local timeToImpact = (cast.UserData.targetPos - CF.Position).Magnitude / combined.Magnitude

		local aimAt = cast.UserData.targetPos + (targetVelocity * timeToImpact)

		targetVector = CFrame.new(CF.Position, aimAt).lookVector.Unit
	end
	
	local targElev = 0;
	local targRot = 0;
	local turretNormal = CF.LookVector
	local angleFromNormal = turretNormal:Dot(targetVector);
	targElev = math.pi/2 - math.acos(angleFromNormal);
	targRot= math.pi/2 - math.acos(angleFromNormal);
	local targetProjection = (targetVector - (angleFromNormal*turretNormal)).unit;	
	local forwardVector = ((CF - CF.Position) * CFrame.new(0, -1, 0)).p
	targRot = math.acos(forwardVector:Dot(targetProjection));
	local vectorCross = forwardVector:Cross(targetProjection);
	if (vectorCross:Dot(turretNormal) < 0) then
		targRot = -targRot;
	end
	
	local vertical = -math.deg(filterNAN(targElev))
	local horizontal = -math.deg(filterNAN(targRot))

	local xVel = CF:vectorToWorldSpace(Vector3.new(1,0,0)):Dot(velocity)
	local yVel = CF:vectorToWorldSpace(Vector3.new(0,1,0)):Dot(velocity)
	
	local xForce = CF.RightVector * -xVel * cast.StateInfo.Delta * getDragFactor(cast)
	local yForce = CF.UpVector * -yVel * cast.StateInfo.Delta * getDragFactor(cast)
	
	velocity = velocity + (CF.LookVector * getAcceleration(cast) * cast.StateInfo.Delta) + xForce + yForce
	
	local lossMultiplier = 1 - (cast.UserData.properties.speedLoss * cast.StateInfo.Delta)
	velocity = velocity * lossMultiplier
	
	local tm = getMaxTurn(cast)
	local verticalTurn = math.clamp(vertical * 3, -tm, tm) * cast.StateInfo.Delta
	local horizontalTurn = math.clamp(horizontal * 3, -tm, tm) * cast.StateInfo.Delta
	local turnLoss = math.abs(verticalTurn) + math.abs(horizontalTurn) / 4
	local gravityLoss = velocity.Unit * CF.LookVector.Y * cast.StateInfo.Delta * -65.4
	velocity = velocity * (1 - turnLoss / 100) + gravityLoss
	
	--CF = (CF + (velocity)) * CFrame.Angles(math.rad(verticalTurn), math.rad(-horizontalTurn), 0)
	
	--cast:SetVelocity(CF.Position)
	
	local DesiredVector = targetVector --(cast.UserData.targetPos - CF.Position).Unit
	local CurrentVector = velocity.Unit
	local AngularDifference = math.acos(DesiredVector:Dot(CurrentVector))
	local OrthoVector = CurrentVector:Cross(DesiredVector).Unit
	local AngularCorrection = math.min(AngularDifference, cast.StateInfo.Delta)

	cast:SetVelocity(CFrame.fromAxisAngle(OrthoVector, AngularCorrection):vectorToWorldSpace(velocity))
	
	cast.UserData.T += cast.StateInfo.Delta
	local willDetonate = ((cast.UserData.T > 2 and velocity.Magnitude < 150) or (dist < 5))
	if cast.UserData.T > 1 and dist > cast.UserData.lastDist then
		willDetonate = true
	end
	cast.UserData.lastDist = dist
	
	cast.UserData.willDetonate = willDetonate
end

Video

2 Likes

Wouldn’t targetVector just be CF.LookVector if you want to keep it going straight?

2 Likes

If the targetVector happens to be equal to Vector3.zero, then attempting to turn it into a unit vector will result in NaN, so use an if statement to check if the targetVector is equal to Vector3.zero before converting it to a unit vector


@bonebreaker114 @7z99 has a good point: if you want the raycast to fire in a straight line according to the direction the gun is facing, using this formula to calculate the target vector is recommended:

targetVector = CF.Position + CF.LookVector * 1000 -- Equivalent to saying 1000 studs in front of the projectile's current position
2 Likes

No, as this just breaks it since targetVector has to be a set coordinate; I’m attempting to set targetVector 1000 studs in the direction of the projectile CFrame.

1 Like

This breaks the code; even putting it into a CFrame.new(formula).Position.

image

1 Like

If your code expects a unit vector for the direction that the gun is facing, then why not use the CF.LookVector directly? I don’t understand why you’re multiplying the look vector with a distance, as doing so will convert it to a regular vector

2 Likes

I’ve honestly no clue, but CF.LookVector just causes it to not cast at all, it just breaks on the first cast.

This code isn’t 100% mine since I’m attempting to port an existing guidance system onto another projectile system that’s less outdated.

2 Likes

Is targetVector supposed to be a direction or a coordinate in 3D space?

1 Like

For the gun to shoot perfectly straight, you’ll need to do something like this:

local distance = 1000
local origin = barrel.Position -- Use .WorldPosition if using an Attachment
local direction = barrel.CFrame.LookVector -- Use .WorldCFrame.LookVector if using an Attachment

local raycastResult = workspace:Raycast(origin, direction * distance)
print(raycastResult)

@bonebreaker114 Note: if raycastResult is equal to nil, you can use origin + direction * distance to calculate the projectile’s target, otherwise use the raycastResult’s position

local targetPosition = if raycastResult then raycastResult.Position else origin + direction * distance
1 Like

It’s meant to be a coordinate/vector3 that gets pushed a 1000 studs forward (in front of the projectile) each update when there’s no target until it either times out or finds a target.

1 Like

Yeah I’m now lost.

What happens if you orthonormalize your CFrame before doing anything to it

local CF = cast.RayInfo.CosmeticBulletObject.CFrame:Orthonormalize()

Still wants to bend off into different directions instead of going straight, I’m lost too since I don’t know too much about this side of programming; still trying to get the hang of everything.

I forgot to specify to be setting targetVector to be CF.LookVector before trying that, were you doing that as well?

Setting targetVector to just be CF.LookVector just causes the cast to break (even with orthnormalizing).

The code below is the closest I’ve gotten to it working properly, but it just bends off into other directions (see video for example).

CFrame.new(segmentOrigin, segmentDirection * 1000).lookVector.Unit

Using a function such as this one to handle moving the projectile should work:

local TweenService = game:GetService("TweenService")

local DISTANCE = 1000
local TWEEN_INFO = TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.In, 0, false, 0)

local function calculateProjectile(projectile: BasePart)
	local raycastParams = RaycastParams.new()
	raycastParams.FilterDescendantsInstances = {projectile}
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude

	local thread = task.spawn(function()
		while true do
			local origin = projectile.Position
			local direction = projectile.CFrame.LookVector

			local raycastResult = workspace:Raycast(origin, direction * DISTANCE, raycastParams)
			local targetPosition

			if raycastResult then
				-- The projectile has hit something
				targetPosition = raycastResult.Position
			else
				targetPosition = origin + direction * DISTANCE
			end

			local tween = TweenService:Create(projectile, TWEEN_INFO, {Position = targetPosition})
			tween:Play()
			tween.Completed:Wait()
			if raycastResult then break end -- Must break the loop once the projectile has hit something
		end
	end)

	task.delay(4, function() -- After 4 seconds have passed, stop the loop and destroy the projectile
		task.cancel(thread)
		projectile:Destroy()
	end)
end

@bonebreaker114 I’ve made an important edit to the code above: I forgot to create RaycastParams for the projectile, which can create issues if it has CanQuery set to true

I’m trying not to deviate from using FastCast.

Unfortunately, I’m not familiar with using FastCast, as I prefer writing code myself whenever possible, but if you continue to encounter issues with your code, then I recommend that you’ll consider my suggestion

I’ll still check FastCast’s docs to see if I can make any changes to the code in-order to make it work with it, though :slight_smile::+1:


@bonebreaker114 I’m back from doing research + testing FastCast, and by default, the caster:Fire function will shoot the ray straight unless you add a new behavior to edit the projectile’s acceleration

The current script that you’re using seems to be complicating things much more than they really need to be, since FastCast already has useful functionality built in to it

Fixed it by adding

AngularDifference > 0

Looked over your initial reply by accident, should’ve read deeper

1 Like

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