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

Hey there, thank you for this really neat module! Just wondering, is there possibly a way for us to model the projectile motion? So users would be able to know what the projectile look like (before even shooting) with factors such as gravity and the rest accounted for. (similar to EgoMoose’s Modeling a projectile's motion thread)

There might not be a specific feature for this, but could you suggest a workaround to replicate the same effect using the current module?

I’d recommend writing your own implementation to do this as a companion module to FC. This isn’t designed to run trajectories instantly as that’s outside of its scope of why it was created.

As for how? The thread you linked works as it is - physics is physics, so FC uses the same type of math. In theory, they would be able to work without even acknowledging each other’s existence.

1 Like

Does calling FastCast.new() for every single bullet cause memory leaks?

No, granted you don’t keep a reference to them.

I’ll use the example in the API docs to explain why it causes a problem (I assume you are curious?), it’s not memory leaks. Making a new caster every time you fire the gun is like taking the time to build a brand new gun at your workbench, then only fire it once, and then take the time to dismantle it. Every. single. time.

While it does not cause memory leaks, it does cause performance problems.

I don’t know what you’re trying to ask here. I assume you mean to ask why you can’t pause/resume your casts in the LengthChanged event. You can pause there, that will work, but you need to remember what pausing does: Pausing causes the ray to not process anymore. This means that LengthChanged will no longer fire. That’s why you can’t resume it again from that function - it’s not actually running in the first place because you paused the ray.

If you need code that can start/stop rays based on a condition, you will need some table to keep track of each individual ActiveCast instance, and an external loop. It’s not exactly the easiest thing to set up, so you’ll need to make sure you trust your scripting abilities enough to do so.

1 Like

Is there any plans to incorporate the new Parallel Lua feature into this module? It seems that multi-threading has potential for a huge speed improvement, especially with raycasting type tasks. FastCast is already fast but more speed never hurts! :wink:

1 Like

Yep! Its on the to do list. It might be a bit though.

3 Likes

This is amazing! Why didn’t I find this sooner…

1 Like

This is cool modoule. Love it. But i found a problem.
I used cast:Terminate() in RayPierce function
and i got an error


any way to solve it?

I’m trying to get the module to work on mobile. My plan is to have a button that players can tap when they want to fire. I’m trying to get the bullets to be fired straight forward no matter what direction the player is facing(as if the cursor clicked the center of the screen). In the Client script in the Gun example model, I tried to replace Mouse.Hit.Position with Mouse.Origin.Position, but the bullets are being fired toward the screen instead of away. Any idea how to fix this?

Hmm… Docs may be out of date in that they mention it’s OK to call in all functions. Terminating inside of a returning callback (RayPierce specifically) is an unsafe operation for that exact reason. If the wiki doesn’t say that, I’ll update it later. Generally speaking, Terminate should only ever be used if you can’t control the cast’s behavior from the system itself e.g. an outside influence needs to tell a bullet to stop by force under a condition where it normally couldn’t stop. Avoid using it in event handlers.

Edit: For the sake of compliance with this limit, I might actually cause FC to throw an error if Terminate is called in an event handler in a future version. Right now my focus is on releasing major version 4, which will implement parallel Luau and rebuild some of the module’s code because bits and pieces of it are falling apart due to my less than ideal understanding of strong types at the time I made major version 3.

What you should be doing is returning false to tell the system that the bullet should stop there, and then just let the cast terminate itself naturally. If you’re trying to avoid a RayHit call for some reason, you can just add an entry to ActiveCast.UserData that you use to keep track of a cast that you want to skip a RayHit call for.

For example, instead of:

function CanPierce(cast, ...)
    -- ...
    cast:Terminate()
end

do:

function CanPierce(cast, ...)
    -- ...
    cast.UserData.PierceTermination = true -- This can really be whatever.
    return false -- cannot pierce
end

function OnRayHit(cast, ...)
    if (cast.UserData.PierceTermination == true) then return end -- Abort if you set that.
end

That’s happening because Mouse.Origin is the Camera’s CFrame. When the system calculates the direction, it expects the given value to be a goal point, so it’s basically the same as aiming for your camera if you use Mouse.Origin in the default code.

You need to find a point some distance in front of the camera. I recommend raycasting out of the camera and figuring out the spot in the world where their camera hits.

1 Like

Is the module supposed to move the cosmetic bullets?

I am asking this because I am trying to make a gun, but the bullet doesnt move:

https://gyazo.com/74c4ab8e3a9a5ca2a020c0ef7b93462c

Script:

local player = game.Players.LocalPlayer

local mouse = player:GetMouse()

local DEBUG = true							-- Whether or not to use debugging features of FastCast, such as cast visualization.
local BULLET_SPEED = 600					-- Studs/second - the speed of the bullet
local BULLET_MAXDIST = 1000						-- The furthest distance the bullet can travel 
local BULLET_GRAVITY = Vector3.new(0, -20, 0)		-- The amount of gravity applied to the bullet in world space (so yes, you can have sideways gravity)
local MIN_BULLET_SPREAD_ANGLE = 1					-- THIS VALUE IS VERY SENSITIVE. Try to keep changes to it small. The least accurate the bullet can be. This angle value is in degrees. A value of 0 means straight forward. Generally you want to keep this at 0 so there's at least some chance of a 100% accurate shot.
local MAX_BULLET_SPREAD_ANGLE = 4					-- THIS VALUE IS VERY SENSITIVE. Try to keep changes to it small. The most accurate the bullet can be. This angle value is in degrees. A value of 0 means straight forward. This cannot be less than the value above. A value of 90 will allow the gun to shoot sideways at most, and a value of 180 will allow the gun to shoot backwards at most. Exceeding 180 will not add any more angular varience.
local FIRE_DELAY = 0								-- The amount of time that must pass after firing the gun before we can fire again.
local BULLETS_PER_SHOT = 1							-- The amount of bullets to fire every shot. Make this greater than 1 for a shotgun effect.
local PIERCE_DEMO = true							-- True if the pierce demo should be used. See the CanRayPierce function for more info.

local castModule = require(script.Parent:WaitForChild("FastCastRedux"))
local partModule = require(script.Parent:WaitForChild("PartCache"))

castModule.DebugLogging = true
castModule.VisualizeCasts = false

local CosmeticBulletsFolder = workspace:FindFirstChild("CosmeticBulletsFolder") or Instance.new("Folder", workspace)
CosmeticBulletsFolder.Name = "CosmeticBulletsFolder"

local activeCast = castModule.new()

local CosmeticBullet = Instance.new("Part")
CosmeticBullet.Material = Enum.Material.Plastic
CosmeticBullet.Color = Color3.fromRGB(255, 248, 16)
CosmeticBullet.CanCollide = false
CosmeticBullet.Anchored = true
CosmeticBullet.Size = Vector3.new(0.2, 0.2, 2.4)

local CastParams = RaycastParams.new()
CastParams.IgnoreWater = true
CastParams.FilterType = Enum.RaycastFilterType.Blacklist
CastParams.FilterDescendantsInstances = {script.Parent}

local moduleBulletCache = partModule.new(CosmeticBullet, 100, CosmeticBulletsFolder)

local CastBehavior = castModule.newBehavior()
CastBehavior.RaycastParams = CastParams
CastBehavior.MaxDistance = BULLET_MAXDIST
CastBehavior.HighFidelityBehavior = castModule.HighFidelityBehavior.Default

CastBehavior.CosmeticBulletProvider = moduleBulletCache

CastBehavior.CosmeticBulletContainer = CosmeticBulletsFolder
CastBehavior.Acceleration = BULLET_GRAVITY
CastBehavior.AutoIgnoreContainer = false

script.Parent.Activated:Connect(function()
	activeCast:Fire(
		script.Parent.Handle.GunFirePoint.WorldPosition,
		mouse.Hit.Position - script.Parent.Handle.Position,
		BULLET_SPEED,
		CastBehavior
	)
end)

No, not on its own. You need to do that on your own by CFraming it in the LengthChanged event like you would any other bullet. That’s part of why the parameter is passed in to the function (see the docs for more)

2 Likes

Ah, I didn’t think about raycasting. Any idea as to how I would shoot the ray straight ahead at the center of the screen?

For some reason when I walk backwards the bullet starts shooting far from the origin:

https://gyazo.com/1cb4e7a4c4b75542a60d0599f51bc0ef
https://gyazo.com/4d500b8398e1a9473047e37cc2913cad

Works well when walking forward.

activeCast:Fire(
		script.Parent.Handle.BulletOrigin.WorldPosition,
		mousePos - script.Parent.Handle.BulletOrigin.WorldPosition,
		BULLET_SPEED,
		CastBehavior
	)

It’s just a visual thing. If you make the bullet inherit the player’s velocity, it will go away, but it will also affect the trajectory.

2 Likes

Did the same as in the example gun. Still doing the same tho.

local myMovementSpeed = character.HumanoidRootPart.Velocity
	local modifiedBulletSpeed = (direction * BULLET_SPEED) + myMovementSpeed
	
	activeCast:Fire(
		script.Parent.Handle.BulletOrigin.WorldPosition,
		direction,
		modifiedBulletSpeed,
		CastBehavior
	)

Edit:

This only happens when using a shoulder camera system, the shoulder cam uses CFrame.fromMatrix() on the humanoid root part to rotate the character every frame. Bug fixed when using shiftlock tho.

Edit2:

Shouldn’t this in the example gun be changed to CFrame.lookAt()?

image

1 Like

So I don’t know if anyone else is having the same issue, but holy CRAP. Studio hangs any time I insert this script into studio. Any idea on what’s up? It seems to be something involving Luau, but I’m not familiar enough with the module to figure out why when you open the scripts, Studio just refuses to cooperate. Here’s a video of opening the scripts on a 24 core processor, courtesy of Boatbomber. I love the work here- and I was excited to use it / contribute, but holy CRAP, studio just doesn’t like it!!

2 Likes

Yeah this is definitely something with Luau. I get it too and it’s because of importing strong types as far as I can tell.

2 Likes

I’ve got a question. For some reason my bow won’t fire in the proper direction. The gray part is the position my arrow should hit, but for some reason it just shoots straight up?

I’m not really sure what I’m doing wrong here. This is basically my code:

local TargetPosition = Mouse.Hit.P --FYI this is all on the server. I'm just using this so you can easily see what TargetPosition is.
local Velocity = 1000
--
local FastCastBehavior = FastCast.newBehavior()
--Edit FastCastBehavior:
FastCastBehavior.RaycastParams = BowRaycastParams --BowRaycastParams.TargetBlackList = {Character, Bow}.
FastCastBehavior.MaxDistance = 10000
FastCastBehavior.Acceleration = Vector3.new(0,-50,0) --Simulating gravity hopefully.

Caster:Fire(Arrow.Position, TargetPosition, Velocity, FastCastBehavior)

I must be missing something.

1 Like