Making a combat game with ranged weapons? FastCast may be the module for you!

RayHit doesnt seem to be firing for me

local module = {}

local RS = game:GetService("ReplicatedStorage")

local Modules = RS:WaitForChild("Modules")

local FastCast = require(Modules:WaitForChild("FastCastRedux"))
local OnHit = require(script:WaitForChild("OnHit"))

local Caster = FastCast.new()
local Behaviour = FastCast.newBehavior()

local cBullet = Instance.new("Part")
cBullet.Size = Vector3.new(0.1,0.1,0.2)
cBullet.Anchored = true
cBullet.CanCollide = false
cBullet.Color = Color3.fromRGB(18, 18, 18)

local CastParams = RaycastParams.new()
CastParams.IgnoreWater = true
CastParams.FilterType = Enum.RaycastFilterType.Blacklist

Behaviour.CosmeticBulletTemplate = cBullet
Behaviour.CosmeticBulletContainer  = workspace
Behaviour.RaycastParams = CastParams
Behaviour.Acceleration = Vector3.new(0,-9.8,0)

Caster.LengthChanged:Connect(function(CasterThatFired, LastPos, RayDir, Displacement, SegmentVelocity, Bullet)
	Bullet.Position = LastPos + (RayDir * Displacement)
end)

Caster.RayHit:Connect(function(CasterThatFired, RayResult, SegmentVelocity, Bullet)
	local Hit = RayResult.Instance
	print("Hit")
	if Hit.Parent:FindFirstChild("Humanoid") then
		print("Hum")
		local Func = OnHit[Hit.Name]
		if Func then
			print("Func")
			Func(Hit, RayResult)
		end
	end
end)

Caster.CastTerminating:Connect(function(ActiveCast)
	ActiveCast.RayInfo.CosmeticBulletObject:Destroy()
end)

function module.OnFire(Tool)
	local Pistol = Tool.Pistol	
	Pistol.Pistol.Fired:Play()
	
	CastParams.FilterDescendantsInstances = {Tool}

	local Origin = Pistol.Tip.Position
	local Direction = -Pistol.Bolt.CFrame.LookVector

	Caster:Fire(
		Origin,
		Direction,
		150,
		Behaviour
	)

end

return module

doesnt print or error anything even if i shoot directly at a wall. The bullet is fired, its even visible. And it goes straight trough anything i shoot it at without firing the rayhit. Ive been trying to solve this for days now.

This is on the server in a module by the way.

1 Like

How do you get the current cosmetic bullet?
CastTerminating doesn’t return the bullet so I have no idea how to delete the bullet after its done because I don’t know how to access it.

Never mind found out how.

Do we have to create a Caster for each individual weapon? I’m trying to make it modular and have client-server a lot quicker. Previously I had client and server scripts similar to your example. I am now just using the client to handle the physical bullets/etc. While server just do invisible check for player collisions. And I am trying to create a single module that can handle all the guns shooting, instead of having every single gun having their own individual scripts.

local Caster = FastCast.new()

function Bullet.Fire(player, weapon, mousePosition, firePoint)
	local WeaponFolder = Weapons:FindFirstChild(weapon)
	if not WeaponFolder then return end
	
	local Config = WeaponFolder.Weapon:FindFirstChild("Config")
	if not Config then return end
	
	local SPEED = Config:GetAttribute("Speed")
	local GRAVITY = Config:GetAttribute("Gravity")
	
	BulletTemplate.BrickColor = player.TeamColor -- Set bullet color to team color
	
	-- Set up cast behavior
	local CastBehavior = FastCast.newBehavior()
	CastBehavior.RayCastParams = CastParams
	CastBehavior.Acceleration = Vector3.new(0, -GRAVITY, 0)
	CastBehavior.CosmeticBulletContainer = Bullets
	CastBehavior.CosmeticBulletTemplate = BulletTemplate
	
	-- Projectile math
	local Origin = firePoint
	local Direction = (mousePosition - Origin).Unit
	
	Caster:Fire(Origin, Direction, SPEED, CastBehavior) -- Fire shot
	
	Caster.LengthChanged:Connect(OnLengthChanged)
	Caster.RayHit:Connect(OnRayHit)
end

This way it can be a million times easier, than having to go through say 100+ guns and all their scripts. I could just tell it what gun they firing from, and get that guns specifics from there.

My problem I then run into what could be a problem from the server. “Shoot” is fired from the client when they click.

-- SERVER
local FastCast = require(Shared.FastCast)

local Caster = FastCast.new()

-- Default raycast parameters
local CastParams = RaycastParams.new()
CastParams.FilterType = Enum.RaycastFilterType.Blacklist
CastParams.IgnoreWater = true

local function OnRayHit(cast, result, velocity, bullet)
	local Hit = result.Instance
	
	local HitCharacter = Hit:FindFirstAncestorWhichIsA("Model")
	if HitCharacter and HitCharacter:FindFirstChild("Humanoid") then -- Character
		HitCharacter.Humanoid:TakeDamage(100)
	end
end

local function FireBullet(player, mousePosition, firePoint)
	-- Find and set variables later --
	local SPEED = 20
	local GRAVITY = 300
	---------------------------------------------
	-- Set up cast behavior
	local CastBehavior = FastCast.newBehavior()
	CastBehavior.RayCastParams = CastParams
	CastBehavior.Acceleration = Vector3.new(0, -GRAVITY, 0)
	
	local Origin = firePoint
	local Direction = (mousePosition - Origin).Unit
	
	Caster:Fire(Origin, Direction, SPEED, CastBehavior)
	
	for _, v in pairs(Players:GetPlayers()) do
		if v == player then continue end
		
		ReplicateBullet:FireClient(v, mousePosition, firePoint)
	end
end

Shoot.OnServerEvent:Connect(FireBullet)

Caster.RayHit:Connect(OnRayHit)

I am then unable to tell on the server who actually fired the specific shot, so players are able to thus shoot themselves :confused:

Did you ever find a fix for this?? I’m using the inbuilt filter but it doesn’t actually seem to filter the descendants correctly??

I have a folder called “Splatter” and it’s added to the FilterDescendantsInstances, however I still get the bullet hitting the descendants of the folder :confused:

-- Create caster
local Caster = FastCast.new()

--// Object hit
local function OnRayHit(cast, result, velocity, bullet)
	local Hit = result.Instance
	print(Hit.Parent) -- Prints Splatter
	
end

function Bullet.Fire(player, weapon, mousePosition, firePoint)
	local WeaponFolder = Weapons:FindFirstChild(weapon)
	if not WeaponFolder then return end
	
	local Config = WeaponFolder.Weapon:FindFirstChild("Config")
	if not Config then return end
	
	local SPEED = Config:GetAttribute("Speed")
	local GRAVITY = Config:GetAttribute("Gravity")
	
	BulletTemplate.BrickColor = player.TeamColor -- Set bullet color to team color
	
	CastParams.FilterDescendantsInstances = {
		player.Character,
		Splatter
	}
	
	-- Set up cast behavior
	local CastBehavior = FastCast.newBehavior()
	CastBehavior.RayCastParams = CastParams
	CastBehavior.Acceleration = Vector3.new(0, -GRAVITY, 0)
	CastBehavior.CosmeticBulletContainer = Bullets
	CastBehavior.CosmeticBulletProvider = BulletCache
	print(CastBehavior)
	-- Projectile math
	local Origin = firePoint
	local Direction = (mousePosition - Origin).Unit
	
	Caster:Fire(Origin, Direction, SPEED, CastBehavior) -- Fire shot
end

Caster.RayHit:Connect(OnRayHit)

Is there a way to make a homing type projectile
This is basicly what i have going on but the Raycast is not getting updated so it jerkes around when it finds the target

function OnRayUpdated(cast, segmentOrigin, segmentDirection, length, segmentVelocity, cosmeticBulletObject)
-- Whenever the caster steps forward by one unit, this function is called.
-- The bullet argument is the same object passed into the fire function.
if cosmeticBulletObject == nil then return end
local bulletLength = cosmeticBulletObject.Size.Z / 2 -- This is used to move the bullet to the right spot based on a CFrame offset
local baseCFrame = CFrame.new(segmentOrigin, segmentOrigin + segmentDirection)
cosmeticBulletObject.CFrame = baseCFrame * CFrame.new(0, 0, -(length - bulletLength))
local t = cast.UserData["Target"]
local c = cast.UserData["Player"]

if(t == nil) then
	for i,v in pairs(workspace:GetDescendants()) do
		if(v:findFirstChild("Humanoid") and math.abs((v.HumanoidRootPart.Position - cosmeticBulletObject.Position).Magnitude) <= 15) then
			cosmeticBulletObject.CFrame = CFrame.new(cosmeticBulletObject.Position,v.HumanoidRootPart.Position)
			print(v)
		end
	end
end

This is because the container is the workspace, And since fastcast castbehaviour has a setting called “AutoIgnoreContainer” (True by default) it will ignore every descendant of the workspace.

1 Like

does anyone have an idea how i stop the bullet mid process? like a time stop stopping every velocity

This is a very useful module, but I want the effects done on the client while the actual hit is done on the server. Is there any way I should go about achieving that?

Hey, very nice api, extremely useful, however, I have a question about the projectiles. How would I add an effect to the bullet after the shot is fired, such as a trail? I’ve tried looking at the documentation but can’t seem to find anything but that’s probably just me being stupid. I’ve also tried adding it in the Fire function yet it makes a weird visual.

No you do not.

Personally, I have one caster in the server to handle the damage, and one on the client to handle the physical bullet.

All the client has is one ModuleScript that detects when the player equips a tool with LocalToolEquipped and binds a function to input with BindAction.

Here’s some of the client code to give you a rough idea:

local function onShoot(ActionName, InputState, InputObject, Gun)
	if ActionName == "Shoot" and InputState == Enum.UserInputState.Begin then
		print("Shooting.")
		ShootRemote:FireServer(Gun, Mouse.Hit.Position)
	end
end

-- Connected through a .OnClientEvent event
local function onReplicateCalled(Gun, MousePosition)
	local StartPart = Gun.Barrel --Depends on the gun
	
	local Direction = (MousePosition - StartPart.Position).Unit
	FastCastBehaviour.Acceleration = Vector3.new(0, -Gun.Gravity.Value, 0)
	ClientCaster:Fire(StartPart.Position, Direction, Gun.Power.Value,  FastCastBehaviour)
end

--Connected through a .LocalToolEquipped event
local function onToolEquipped(Weapon)
	print("Tool equipped!")
	ContextActionService:BindAction("Shoot", function(N, I, IO)
		onShoot(N, I, IO, Weapon)
	end, false, Enum.UserInputType.MouseButton1)
end

I also proceed to unbind the input when the tool is unequipped, and I detect this with LocalToolUnequipped.

On the server:

--Connected through a .OnServerEvent event
local function onShoot(Player, Gun, MousePosition)
	local RaycastParameters = createRaycastParams(Player)
	local OriginPart = Gun.Flare
	
	local Direction = (MousePosition - OriginPart.Position).Unit
	
	local FastCastBehaviour = FastCastRedux.newBehavior()

    --You can add checks for particular guns which shoot differently (eg. bombs that can also damage the player for particular RaycastParameters)
	FastCastBehaviour.RaycastParams = RaycastParameters
	FastCastBehaviour.Acceleration = Vector3.new(0, -Gun.Gravity.Value, 0)

	UniversalCaster:Fire(OriginPart.Position, Direction, Gun.Power.Value,  FastCastBehaviour)
	
    -- Merely preference.
	ShootRemote:FireAllClients(Gun, MousePosition)
end

I have this function that is called every time the Shoot Remote is fired to the server.

That adds up to two casters in the entire game.
I believe this also answers @OminousVibes0’s question.

3 Likes


Would be useful if there is an attraction property

AttractedPosition = Vector3
-The instance the trajectory is trying to reach
AttractionStrength = 0 to 1
-The strength of reaching the desired position

1 Like

Do you have any links to where I can look more into client replication for simulated physics?

Is it possible to make a part (or just find the position) where the cast lands? I’m sorry if this is a dumb question.

yes. Its an argument of the raycastresult in the RayHit event of the caster.

1 Like

Not yet. I want to though, I will make that known.

This is just an example of what I did

I see, but I have a couple questions

Why fire the cast on the server, if you want to visualize it on the client? Doesn’t the caster:Fire() method also create a projectile because it requires the castbehavior parameter, which passes the cosmeticbulletprovider? In the module, the ‘SimBullet’ function creates a cosmeticbulletobject. You are calling that on the server. How would that work?

The cast on the server is used mainly for hit detection. Of course, you could have the client do hit detection if you wanted and have the server do a valid check. The server doesn’t create the cosmetic bullet it only creates the ray. The clients create the visual stuff. Anything visual-related, aside from the debug, is removed from the server. You do not need to pass ‘CastBehavior.CosmeticBulletProvider’ on the server. In fact, FastCast still works without it.

oh sweet! Thats what I was worried about when I was reading the fastcast module. I couldn’t find anywhere, where the module checks if the cosmeticbulletprovider/ cosmetic bullet exists

I did some testing, If you are shooting and die while shooting (With the debug gun and my own) The bullets get frozen in the air, and will not be removed. How do you solve this?