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

Having a little play around with this library, it’s honestly amazing. I’m so glad I stumbled across this today, thank you! :slight_smile:

5 Likes

Has anyone used FastCast for a high population (40+), high rate of fire (10/s) game? What numbers did you have and were there performance issues? It seems to have a lot of server side work so I’m concerned that building on top this will have scaling issues.

2 Likes

I made a video of a stress test for the module a while ago. I think it’ll work out for ya.

22 Likes

Hey! Great to see you update the module I have been using an old version for a long time and I’m excited to test the new version, I’ll give feedback on how it performs as soon as I implement it to my game.


I see in the video above that when you exit from First Person Camera View it doesn’t shoot at the cursor but rather below is that intentional? how can I avoid/fix it?

2 Likes

Yes this seems to be a problem I had even without bullet drop.

4 Likes

I think that’s an issue with my hit detector in the LocalScript to get mouse position (I’m changing it to use the Mouse class in a newer version). It’s not an issue with the module itself, but rather the gun script driving the module.

I’m about to have V6 out which addresses some user requests. The example gun will be updated accordingly.

EDIT: I’ve just released FastCast V6!


https://www.roblox.com/library/1802664961/FastCast-Hitscan-with-Bullet-Physics
https://www.roblox.com/library/2009153526/FastCast-Example-Laser-Gun

4 Likes

https://gyazo.com/68fcb54d04540046ef3c8601a5254e57

https://gyazo.com/24f8977ce40ae98818291f859341b6fd

https://gyazo.com/36df6388b240fcb7d24bc47fa3809f64

Still having issues with the bullet not hitting where the cursor is at.

https://gyazo.com/14c79c7194684317413891c38ded1cfc

This game is very accurate and the physic’s lag isn’t there


I made a game to test the module

Lazer Tag.rbxl (340.4 KB)

7 Likes

This is likely your own script causing the error. Make sure you are absolutely 100% certain your gun script is correct! Given that I can test the module without experiencing these errors in both studio and online mode, the error is more than likely not related to FastCast but instead your own gun script. Luckily, I have some plausible causes for most of the issues you may be experiencing.

For the first and third gif (bullet not going to your cursor), are you using the latest version of the example gun? The old version had some faulty aiming and it should be resolved in the new version which was released yesterday. The new version uses Mouse.Hit rather than the camera ray method.

For the second gif, you will need to parent the cosmetic bullets to a special container and set Caster.IgnoreDescendantsInstance to that container. The issue you are seeing here is the caster hitting cosmetic bullets for different rays. CosmeticBulletObject only prevents the ray from hitting its own bullet, not other bullets fired from the same caster.

If you’re still having issues or can otherwise prove that the module is the cause for these errors, please message me so we can get it squared away ASAP :grinning:

5 Likes

It’s your sample script I didn’t change anything related to the gun script, I just added a team filter.

Yes, I took it from Toolbox today.

2 Likes

I’ve found the problem. The example gun should fire at your cursor. I did Mouse.Hit.lookVector which is the direction from your camera to Mouse.Hit.p. Now it goes from the handle’s WorldFirePoint attachment like it should.

My mistake - I hadn’t realized this would happen. The example should function properly now.

2 Likes

Yes this is now working very well, Thank You!


I will give more feedback on how it performs when I get more people to test it.

3 Likes

I just ask a little question
Is there a good way to ignore characters’ accesories when cast ray? I mean I tried my own way but did not work :cry:

edit: nvm i figured out

6 Likes

Heya folks!

I’ve just pushed version 7.0.0 to address some recent requests. It is now possible to apply acceleration (not just velocity!) to individual bullets! This is great for things like sniper rifles where the wind affects the curvature of individual bullets as they fly, more specifically where the wind may change in realtime.

Have a look at the new API for more details. https://github.com/XanTheDragon/FastCastAPIDocs/wiki/API#version-700

2 Likes

Hey i’m trying to use this for my projectiles in the game, but i’m running into a problem where the arrow isn’t moving, I also get no errors, here’s the script section
.

		local Attack = game.Lighting.Parts.SpiritArrow:Clone()
		Attack.Name = "Attack"
		Attack.Anchored = true
		Attack.CanCollide = false
		Attack.TopSurface = 0
		Attack.BottomSurface = 0
		Attack.BrickColor = Player.AbilityColor.Value
		Attack.Reflectance = 0
		Attack.CFrame = CFrame.new(char.RightHand.Position+Vector3.new(0,5,0), Vector3.new(MouseEvents.MousePos.Value.X,MouseEvents.MousePos.Value.Y,MouseEvents.MousePos.Value.Z))
		Attack.Parent = workspace	

local Distance = (Vector3.new(MouseEvents.MousePos.Value.X,MouseEvents.MousePos.Value.Y,MouseEvents.MousePos.Value.Z)*1000)
local Speed = (Vector3.new(MouseEvents.MousePos.Value.X,MouseEvents.MousePos.Value.Y,MouseEvents.MousePos.Value.Z) * 60) 
	
		Caster:Fire(char.RightHand.Position+Vector3.new(0,5,0), Distance, Speed, Attack)

Here’s what’s happening in a gif (I put arrow spawning in the air on purpose, and again I get no errors in output)

Did you remember to CFrame the arrow on the LengthChanged event?

Caster.LengthChanged:Connect(function (CastOrigin, SegmentOrigin, SegmentDirection, Length, CosmeticBulletObject)
	CosmeticBulletObject.CFrame = CFrame.new(SegmentOrigin, SegmentOrigin + SegmentDirection)
end)

I suggest checking out the example code (https://www.roblox.com/library/2009153526/FastCast-Example-Laser-Gun) for more usage.

Sorry for bumping an old thread but, how come the bullet Hit detection for me doesn’t really work?
here’s the GIF here:

and here’s the script portion:

 function OnRayHit(HitPart, HitPoint, Normal, Material, CosmeticBulletObject)
--This function will be connected to the Caster's "RayHit" event.
CosmeticBulletObject:Destroy() --Destroy the cosmetic bullet.
if HitPart and HitPart.Parent then --Test if we hit something
	local Humanoid = HitPart.Parent:FindFirstChildOfClass("Humanoid") --Is there a humanoid?
	if Humanoid then
		Humanoid:TakeDamage(10) --Damage.
	end
	MakeParticleFX(HitPoint, Normal) --Particle FX
end
end

function OnRayUpdated(CastOrigin, SegmentOrigin, SegmentDirection, Length, 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.
local BulletLength = CosmeticBulletObject.Size.Z / 2 --This is used to move the bullet to the right spot based on a CFrame offset
CosmeticBulletObject.CFrame = CFrame.new(SegmentOrigin, SegmentOrigin + SegmentDirection) * CFrame.new(0, 0, -(Length - BulletLength))

end

the hit detection is literally the same as the test weapon you have, but here’s the fire function, if there’s a problem with that.

local function shoot(Player, Direction)
--//Initialize
--if not canShoot(player, true) then return end
--shootSound:Play()

if tool.Parent:IsA("Backpack") then return end --Can't fire if it's not equipped.
--Note: Above isn't in the event as it will prevent the CanFire value from being set as needed.

if tool.Parent:IsA("Backpack") then return end --Can't fire if it's not equipped.
--Note: Above isn't in the event as it will prevent the CanFire value from being set as needed.

--We need to make sure the bullet inherits the velocity of the gun as it fires, just like in real life.
local HumanoidRootPart = tool.Parent:WaitForChild("HumanoidRootPart", 1)	--Add a timeout to this.
local MyMovementSpeed = HumanoidRootPart.Velocity							--To do: It may be better to get this value on the clientside since the server will see this value differently due to ping and such.
local ModifiedBulletSpeed = (Direction * BULLET_SPEED) + MyMovementSpeed	--We multiply our direction unit by the bullet speed. This creates a Vector3 version of the bullet's velocity at the given speed. We then add MyMovementSpeed to add our body's motion to the velocity.

--Prepare a new cosmetic bullet
local Bullet = CosmeticBullet:Clone()
Bullet.CFrame = CFrame.new(FirePointObject.WorldPosition, FirePointObject.WorldPosition + Direction)
Bullet.Parent = workspace

Caster:Fire(FirePointObject.WorldPosition, Direction * BULLET_MAXDIST, ModifiedBulletSpeed, Bullet)
--We need to make sure the bullet inherits the velocity of the gun as it fires, just like in real life.
local HumanoidRootPart = tool.Parent:WaitForChild("HumanoidRootPart", 1)	--Add a timeout to this.
local MyMovementSpeed = HumanoidRootPart.Velocity							--To do: It may be better to get this value on the clientside since the server will see this value differently due to ping and such.
local ModifiedBulletSpeed = (Direction * BULLET_SPEED) + MyMovementSpeed	--We multiply our direction unit by the bullet speed. This creates a Vector3 version of the bullet's velocity at the given speed. We then add MyMovementSpeed to add our body's motion to the velocity.
--Prepare a new cosmetic bullet
local Bullet = CosmeticBullet:Clone()
Bullet.CFrame = CFrame.new(FirePointObject.WorldPosition, FirePointObject.WorldPosition + Direction)
Bullet.Parent = workspace

Caster.LengthChanged:Connect(function (CastOrigin, SegmentOrigin, SegmentDirection, Length, CosmeticBulletObject)
CosmeticBulletObject.CFrame = CFrame.new(SegmentOrigin, SegmentOrigin + SegmentDirection)end)

Caster:Fire(FirePointObject.WorldPosition, Direction * BULLET_MAXDIST, ModifiedBulletSpeed, Bullet)

end
3 Likes

First of all, one major issue I see is that you fire the bullet twice in the fire function, and the connect the LengthChanged event every single time after you fire both shots. It looks almost like you copied and pasted that entire chunk of code so it runs twice. This isn’t proper behavior. The template script I supplied had a correct layout, though for the sake of redundancy, this layout should include the following:

  1. The creation of a caster and optionally a template cosmetic bullet at the start of your script
  2. Some function to handle creating a bullet, e.g. shoot in your case. This function should only clone the cosmetic bullet (if necessary), calculate velocity (if necessary), and fire the caster with the needed parameters. It should not connect any caster events.
  3. Event connections to LengthChanged and RayHit at the bottom of the script or wherever you see fit given your organizational preferences.

To elaborate on #2 and #3 – You should connect to LengthChanged and RayHit outside of the shoot function. Think of it like any other event, like Tool.Activated; you usually connect to this only once at the end of your script. This same ideology also applies to LengthChanged and RayHit – connect to them once and handle the data whenever they fire.

From what I can tell in the behavior of the bullets in the gif, you do not handle RayHit correctly. If you indeed copied the code directly from my template, which you claim you did, the undesired behavior is likely caused by your shoot function and its odd behavior. That, or you didn’t use Caster.RayHit:Connect(OnRayHit)

Try cleaning up the script a bit and ensuring that everything necessary is set up. If you’re still having issues, please send me a message and I’ll help you get it sorted out :grinning:

Thank you so much for helping out! turns out the only thing needed was the Caster.RayHit:Connect(OnRayHit), and I’m sorry about my rookie mistakes, this was my first time using fastcast and I love how it works, I will definitely sort out through my code, but all I can say is that it definitely works. Thank you for responding in the thread!

edit once again, but it seems that my code is indeed, horrible! due to how the bullets were working, it multiplies the bullet every single time it is shot, and it deals more damage! I will look into why i made such a stupid mistake, and will edit my comment as to why this happened, lol.

edit edit: was because i put rayhit and lengthchanged inside the shoot function, sorry my bad.

3 Likes

You can probably replace the Signal library in this with this alternative. It’s faster than that Signal library.

local Signal = { }
Signal.__index = Signal
Signal.ClassName = "Signal"

local setmetatable = setmetatable
local running = coroutine.running

function Signal.new() return setmetatable({ }, Signal) end

function Signal:Fire(...)
	for Index = 1, #self do
		local Thread = coroutine.create(self[Index])
		coroutine.resume(Thread, ...)
	end
end

function Signal:Wait()
	local Thread = running()

	local function Yield(...)
		self:Disconnect(Yield)
		coroutine.resume(Thread, ...)
	end

	self[#self + 1] = Yield
	return coroutine.yield()
end

function Signal:Connect(Function)
	self[#self + 1] = Function
end

function Signal:Disconnect(Function)
	local Length = #self

	for Index = 1, Length do
		if Function == self[Index] then
			self[Index] = self[Length]
			self[Length] = nil
			break
		end
	end
end

function Signal:Destroy()
	for Index = 1, #self do
		self[Index] = nil
	end
end

return Signal

It’s the fastest event library there is, using it can make FastCast more like FastestCast :stuck_out_tongue: . It’s around 88% faster than the Signal you have currently.
image

I published a gist of the updated code.

8 Likes

Definitely some interesting work. I’ll be replacing the signal module, you’ll be credited of course.

In terms of your edits to the main module, I think I’ll only implement the changes that implement the new Signal module as soon, caching things like Vector3.new, math.sqrt, etc. will have no benefits in performance. Edit: It seems you’ve replied to this, so you’re aware of it already (Also see the second tweet, https://twitter.com/zeuxcg/status/1120508108445450241). Given that the performance increases from those changes at the moment are relatively negligible (very few use cases would require something faster than what’s already going on – I don’t think anyone needs to fire 500+ bullets per frame), I plan to continue using the older methods in anticipation of the new Lua VM, even if it’s some distance from release.

1 Like