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

This is pretty awesome!

Does this handle player damage by itself? Is there a damage parameter?

It only handles the projectile arc/movement by itself - There are events for when you hit something and even when you pierce something, allowing for very easy additions like that.

2 Likes

When i do the cast userdata on the sending part it works but reciveing does not work:

caster.RayHit:Connect(function(cast, raycastResult, segmentVelocity, cosmeticBulletObject)
local hitPart = raycastResult.Instance
local hitPoint = raycastResult.Position
local normal = raycastResult.Normal
if hitPart ~= nil and hitPart.Parent ~= nil then -- Test if we hit something
	local humanoid = hitPart.Parent:FindFirstChildOfClass("Humanoid") -- Is there a humanoid?
	if humanoid then
		damage.Damage(humanoid,10,cast.UserData["Player"])
	end
end

i want the player to be sent to my damage module

@EtiTheSpirit firstly, huge thank you for this community contribution! I am beginning integration in my project, which has some perhaps special parameters that allowed me to uncover what I think may be a small issue.

At your leisure, please try a test with the following parameters:
CosmeticBullet.Size = Vector3.new(2,2,16)
BULLET_SPEED = 20
BULLET_GRAVITY.Y = 0

In my application I travel at high speeds and need a very long and wide laser beam for adequate visualization. The low BULLET_SPEED allows us to clearly see this particular issue.

Now, let’s start the simulation and turn the camera to the side so that we can see a profile of the beam’s trajectory. We should notice that the beam appears to originate behind the character.

With some fiddling, I tracked this down to a double-negative on line 246 of StarterPack\DebugGun\Server

I propose that we should update the source to change from this:
cosmeticBulletObject.CFrame = baseCFrame * CFrame.new(0, 0, -(length - bulletLength))

to this:
cosmeticBulletObject.CFrame = baseCFrame * CFrame.new(0, 0, -(length + bulletLength))

After this small change, I now have the correct appearance of a very long beam that originates from the gun’s muzzle instead of behind the character’s shoulder.

Thanks again, and I offer you a deep bow of respect, as an Apprentice to a Sensei, and as one who has studied your keystrokes. This module is a valuable contribution to our community, and I’m happy that I can do my part to help.

Best Regards,
FirstVertex

2 Likes

It’s me again lol. This time, I’m here with a possible enhancement for the Laser pistol to improve accuracy when fired while travelling at high speed.

Let’s start with a video from my upcoming game, WormHole Rider

As you can see, I need to travel at very high speeds, this is just a Class 2 Training Worm, even higher speeds are still in development and present special challenges. Speed portrayed in the HUD is mathematically accurate, based upon figures obtained here

Anyway, as you might imagine, when the character is travelling at such high velocity, we must take care to fire the projectile from the correct location, which can be substantially different between the client and the server.

Our Sensei @EtiTheSpirit has left us a breadcrumb to start us down the journey. On line 192 of Server script we find the comment:

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.

Indeed. Let us proceed to do precisely that. It turns out to be fairly straightforward. Tracing the code we see that a MouseEvent is fired from client to server with a payload of the mouse’s Hit. Let’s enhance this payload with a bit more information. On the sending side, in the script file Client we’ll change the RunService stepped handler to include a packet of data instead of just the Mouse.Hit.p. Here’s the necessary data points from my WormHole copy:

    local packet = {
        velocity = humanoidRootPart.Velocity.Magnitude,
        cframe = CFrame.new(FirePointObject.WorldCFrame.p, Mouse.Hit.p),
        ostick = tick(),            
    }
    MouseEvent:FireServer(packet)

Now let’s modify the receiving side in Server script as follows:

When receiving the event on line 261, let’s remove line 267 which computes a direction, since that is provided by the packet. And, instead of passing this computed direction to Fire() we pass the whole packet.

Now, let’s update Fire() to utilize the packet of information from the client. First, we’ll slightly modify the directionalCF initialization based on the packet’s CFrame’s LookVector:

local directionalCF = CFrame.new(Vector3.new(), packet.cframe.LookVector)

Let’s remove the sketch that our Sensei left for us on lines 191-193 and we will compute a modifiedBulletSpeed using the packet’s velocity as follows:

local modifiedBulletSpeed = (direction * (BULLET_SPEED + packet.velocity))

The velocity we received from the client was accurate at the time the weapon was fired. Now, let’s predict where the client has moved to, during the time that has elapsed since the packet was transmitted. To do that, we’ll need to diff the server’s tick with the one from the packet, and we’ll actually need to double it, since approximately this amount of time will again elapse on the return trip when server sync’s the projectile’s position back to client. Here’s my computation for the interpolated position using this doubled ping difference:

local pingOffset = (tick() - packet.ostick) * 2
local interpolatedPosition = packet.cframe.Position + (direction * packet.velocity * pingOffset)

And now we feed these inputs to the Caster:Fire as follows

local simBullet = Caster:Fire(interpolatedPosition, direction, modifiedBulletSpeed, CastBehavior)

With this, we predict where the gun’s muzzle has moved to, based upon it’s last known position and velocity, and the time difference between when it was fired and now.

I hope this mod might be useful to some other folks in the Roblox community

:v:
FirstVertex

2 Likes

I am working on a gun system that uses fast cast (along with the PartCache module ) on the client as a substitute to simulating bullet velocity by other means. Is there a way to cancel a currentcast, as all my tools run on a single LocalScript in StarterPlayerScripts, and I need to end whatever the current cast is when they unequip the tool or if they unequip the tool while shooting.
Some of the problems I have come across include :ReturnPart not working properly as I set the current cast to nil when the tool is added back to the backpack, and multiple different casts stacking up on the same tool when it is reequipped.
I could maybe store the Tool’s data and whether it has an active cast in a table or metatable, and then use the cast found in there, but I would like to know if there is an alternative that I don’t know about.

Never mind, found a solution. Used similar methods to this:

Testing, I tried your method, and it worked well in studio, but when testing in server, you couldn’t move and shoot or no bullets would come out.

@IISato you could try adding a print(pingOffset) on the line right after it’s computed, to see what the actual value was. I’m wondering if a timezone or other timing difference could cause your ping to become unexpectedly large or small.

Has anyone used this module for large and slower moving projectiles? Think: Fireball.

I am currently tweening and and using .Touched on this “fireball” and getting a fair amount of desync between the server-sided hitbox and the client-sided visual effects of the fireball.

I have been lookign at FastCast for a while now, but I am not sure from lookig at the documentation if this:

  1. handle larger projectiles (maybe 5x5 studs max) and their hit detection
  2. handle slower moving projectiles
  3. sync the client-side visual and server-side hit detection on these slow projectiles

Thanks!

FC uses raycasting - the hit detection is one dimensional and has no width nor height, only length.

FC can handle any speed, be it 0.00000001 studs/sec or 100000000 studs/sec, it doesn’t care.

The manner in which you handle hit detection and visual display is on you, FC only provides the means of firing and detecting on a given side.

1 Like

I take it this means this module is not ideal for my use case.

Thanks :slight_smile:

1 Like

OK! I am back with another question, I have scanned the thread and I am not sure yet if this module is good for a simple grenade. All it needs is to arc through the air and have a little bounce, depending on the settings, does FastCast handle this?

Thank you so much for the module! Truly an amazing resource!

Although, I noticed that the pierce demo is more on a ricochet demo. The bullet doesn’t exactly pierce, it just ricochets. Really sorry if I seem stupid for not knowing why this is.

Thanks again!

PS. I noticed a module breaking error inside of the contained part cache module.

“Free Types leaked into this modules public interface. This is an internal Luau error. Please report it.”

2 Likes

Hello, for anyone that wants to implement bullet weight well then I made this little thing.

The script:

local BulletWeight = 3
local Gravity = -workspace.Gravity * BulletWeight
local BulletSpeed = 1250 / BulletWeight
local BulletGravity = Vector3.new(0,Gravity,0)

You can edit the weight, speed, and gravity if you wish to adjust it.

4 Likes

Hey, need some help. When you shoot close to a wall the bullet doesn’t form (since the trajectory is too short). But, is there any way to actually have the visual bullet with trajectories this short?

https://gyazo.com/f264bba352aa505296b981edc14cf61a

By the way, the likes image :fearful:

2 Likes

I can’t for the life of me figure out how to get the total distance that the active cast has travelled upon hitting a target? What am I missing here? Where do I obtain information like this

trying to do damage calculations but ain’t getting it. I see there’s a DistanceCovered thing in “Cast State Info” but… I have no idea how to access that

Edit: Ohh also this is unusual, I have not updated my game in a while but come back to see that FastCast’s cosmetic bullets have somehow entirely broken. Seems where as before I was using a front-facing part with a graphical part parented to and welded to the “base” part, but now the weld is being ignored so the graphical part of the projectile is not getting positioned alongside the cframed base part.

I wonder what caused this. Maybe roblox broke constraint welds and cframes?

3 Likes

Been trying to implement some sanity checks but not sure how to go about it. Any thoughts? I was going to record player positions from the past and then do an “instant cast” on the server (so basically just a normal cast without all the delay) and then check if in theory, it could’ve hit a character.

Problem is, I can’t read this new type lua code and so it’s very difficult to modify the code. Are there any other simpler effective sanity checks?

Can you put it inside of a github repo so I can add it to my game

Is it possible to stop the cast from terminating itself when it hits something? i.e. I need FastCast to keep calling OnRayHit and OnRayUpdated after the first OnRayHit is called.

I need this because I am making a sustained beam weapon, and i’m using FastCast to give the beam travel time.


Edit:

Also, related to this: I am unable to access the ActiveCast and move it inside the OnRayHit function.
When I do: cast.SetPosition() or cast.SetVelocity

I get: TestService: Exception thrown in your RayHit event handler: Workspace.GameObjects.Gem_API.Weapons.BeamPoint.Weapon.FastCastRedux.ActiveCast:684: Cannot statically invoke method 'SetPosition' - It is an instance method. Call it on an instance of this class created via ActiveCast.new(...)

In the documentation, it says that the cast parameter in the OnRayHit is an ActiveCast instance.