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

Bug

Looks like you made a mistake with the distance traveled. You added displacement.Magnitude. This is fine with non-piercing projectiles, but it reduces the “range” (total distance to be traveled) on piercing projectiles (test this out with the DebugGun by shooting it at bunch of parts together while piercing demo is on, it should not go 1000 studs (the default range), but less because of hitting those parts). I believe you were supposed to add (point-lastPoint).magnitude right before the if hit and hit ~= cosmeticBulletObject then line? Unless you intended to reduce its “range” on hit?

Feature request

could you add a way to change the projectile’s trajectory mid-fire/mid-flight? Maybe store the projectile and check if another FastCast:Fire() sent the same object as projectile, and discard the projectiles’s previous :Fire()?

1 Like

That needs to be done manually for now. When I get around to adding the ability to redirect bullets during flight-time, this will probably be an option.

Not with default behavior, no, but I can see about adding this in.

The underlying issue is actually kind of complicated it seems, or I’m overcomplicating it due to being so tired. The old behavior set lastPoint = at. This worked great, with the exception that sometimes rays got spaced apart and this is why people were reporting issues where bullets with physics fell through the floor.

I’m going to do some work on this issue after I sleep so ideally I should have it fixed in a little while.

Hey I’ve got a weird issue going on with FastCast. I’m replacing my current system with it, but I’ve run into an issue that’s making it impossible currently. I’ve based it off the code from the example blaster you gave. Here’s my code:

EDIT: Bottom two lines are part of the script, but I couldn’t get it to past correctly. self.HitP is equivalent to

(Mouse.Hit.p - FirePointObject.WorldPosition).Unit

local Caster = FastCast.new() --Create a new caster object.
FastCast.VisualizeCasts = true

local directionalCF = CFrame.new(Vector3.new(), self.HitP)
local RNG = Random.new()
local TAU = math.pi * 2	

self.HitP = (directionalCF * CFrame.fromOrientation(0, 0, RNG:NextNumber(0, TAU)) * CFrame.fromOrientation(math.rad(RNG:NextNumber(0, 0.5)), 0, 0)).LookVector
print(self.HitP)

local modifiedBulletSpeed = (self.HitP * 200) + self.Player.Character.HumanoidRootPart.Velocity;

self.Projectile.CFrame = CFrame.new(self.Player.Character.Weapon.Barrel.Attach.WorldPosition, self.Player.Character.Weapon.Barrel.Attach.WorldPosition + self.HitP)
self.Projectile.Parent = workspace.Projectiles;

Caster:Fire(self.Player.Character.Weapon.Barrel.Attach.WorldPosition, self.HitP * 1000, modifiedBulletSpeed, self.Projectile, self.Player.Character, false, Vector3.new(0,0,0));

Caster.LengthChanged:Connect(function(castOrigin, segmentOrigin, segmentDirection, length, cosmeticBulletObject)
	local bulletLength = self.Projectile.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)
	self.Projectile.CFrame = baseCFrame * CFrame.new(0, 0, -(length - bulletLength))
end)
Caster.RayHit:Connect(onHit);

EDIT2: It seems to happen whenever the mouse is pointed at an object. That is, if I point towards a wall, it will go up. If I point towards the sky, it goes normally.

Basically, depending on which way the character’s is pointing, if it’s pointing down towards a baseplate, the rocket instead of shooting straight forward will shoot up at an angle. That’s not a behavior I want - i’d rather it go to the ground, even if that means it makes contact and explodes right away. I’ve attached a video belowrobloxapp-20200626-1302033.wmv (2.0 MB)

1 Like

This seems to be an issue with your method of hit detection. The module itself does not do anything to the input direction value.

You should consider double-checking how you grab the mouse’s position in 3D space. It seems to be hitting some part in front of your character (maybe you’re clicking on the view model?)

Good news folks! FastCast V10.0.0 is out!
https://github.com/XanTheDragon/FastCastAPIDocs/wiki/Changelist#fastcast-1000

This fixes the bugs reported by many others as well as the most recent reported by @problems_0zz where physics based casts were all finnicky. This also implements @GFink’s request of accessing segment velocity. THIS CHANGE WILL NOT WORK WITH OLDER VERSIONS OF THE API. See the LengthChanged event, RayHit event, and CanRayPierce callback on the wiki for more information.
[color=#808080][size=2]The main reason this occurred was because I implemented the wrong math. When calculating the velocity of the given segment, I didn’t calculate it from the initial velocity and acceleration. Instead, I used the initial velocity alone. This resulted in things getting all mixed up, which in turn caused physics casts to be “pulled apart” (which is what allowed them to clip through things at times). Whoops.[/size][/color]

As for the new changes, you code should look a little something like this. Note the implementation of the segmentVelocity values.

function CanRayPierce(hitPart, hitPoint, normal, material, segmentVelocity)
    -- ...
end

function OnRayHit(hitPart, hitPoint, normal, material, segmentVelocity, cosmeticBulletObject)
    -- ...
end

function OnRayUpdated(castOrigin, segmentOrigin, segmentDirection, length, segmentVelocity, cosmeticBulletObject)
    -- ...
end
5 Likes

I was creating a weapon and implementing both FastCast and the PartCache module into it, and it resulted in me discovering an error in the PartCache module. On Line 303, the rayHitEvent variable that is being fired is missing a variable, so the cosmeticBulletObject is one variable too early and is the 5th variable. Maybe I’m wrong, but I found that when rayHitEvent is fired on Line 250, cosmeticBulletObject is the 6th variable.

Line 250
rayHitEvent:Fire(hit, point, normal, material, segmentVelocity, cosmeticBulletObject)

Line 301-304

if distanceTravelled > distance then
   connection:Disconnect()
   rayHitEvent:Fire(nil, lastPoint, nil, nil, cosmeticBulletObject)
end

Since I’m using the provided gun from this thread that uses the module, I’ll paste some code from it. This error in the module results in this line not being correct in the case where the fired projectile goes past its max distance:
function OnRayHit(hitPart, hitPoint, normal, material, segmentVelocity, cosmeticBulletObject)

Basically the cosmeticBulletObject in the line above ends up being nil if the projectile goes too far, since the event in that case isn’t fired correctly. This doesn’t have any negative effects on the module itself, though it might confuse anyone trying to incorporate the PartCache module with this one since they need a reference to their cosmetic bullet so they can re-cache the bullet.

Once again, maybe I’m wrong but adding another nil in the event fire on Line 303 fixed it. Thought I’d just throw this out there so it can be fixed. Besides that, thank you for making this module! It works really great and has saved me a ton of time :happy3:

1 Like

Oh awesome. Thanks for letting me know. Yes, line 303 should have a zero value for segmentVelocity. I’ll release an update to patch that right now.

Edit: Patch is live.

2 Likes

I think the FastCast:FireWithWhitelist and FastCast:FireWithBlacklist functions are not handling the canPierceFunction variable correctly. They are not passing it into the BaseFireMethod function, in which only FastCast:Fire seems to do so. I noticed this as I was adding a playerWhoFired variable to the end of each function, so that I could know who shot a bullet after it hits someone so I could award points accordingly. I’m using FireWithBlackList and the MandateType assert was failing and saying the canPierceFunction was an instance, so that was quite confusing for awhile lol

Hello, first I’d like to say I love the module really helped me out. I’ve been working on a Naval Cannon for some time now and I just recently started making grape shot(shot gun like pellets) able to be fired. To do so I did a i loop for x amount of times I want pellets fired. After I realized my pellets were hitting each other I switched to “FireWithBlacklist”. Problem is I’m still having the same problem with my pellets hitting each other. https://gyazo.com/75781c34f7ab514361d0b612d0b1c7a1 Any help would be appreciated.

Yeah I don’t know why that was removed… Hm. I think it was for something in debugging to test the odd pierce behavior reported some ways above.

It’s been re-added.

I can say one thing is that and does not join arrays together. In Lua, and will select the only non-nil operand, and if both are not nil, it always selects the operand on the right (so in your case blacklist is only Cannon:GetDescendants() and nothing else).

Inside of the FastCast module is a utility module called “Table”. You can use this if you’d like, simply do local table = require(FastCastModuleHere.Table). After you have my custom table module overwriting the stock table option in scripts (via that line just above), you can use table.join(projectiles:GetDescendants(), Cannon:GetDescendants())


Those replies aside, FastCast 10.0.2 has been released to fix the bug mentioned just above. Additionally, there is now a prototype of the module that uses’s the Luau Strong Type Beta feature included in both models! This should ideally run faster than the actual module (though I don’t think the speed increases will be that notable). Do note that this will not work in live games yet, and you need to enable the beta feature to actually use the module in the first place.

1 Like

I got bullet reflecting to work! This module is legit a lifesaver. I tried several physics based approaches previously and none of those methods worked nearly as good as this has so far. The crazy part is that I can combo automatic fire, fast firing speed, shotgun pellets, and have all those bullets ricochet and it’s still fast - literally no lag. Just feels so unbelievable. Thank you so much for making this!

9 Likes

Awesome work! I’ll release a feature for this officially when I release the ability to alter trajectory during runtime (so it will be using considerably different code than what you have).

The only real issue developers may face is that this is going to be yet another complete restructure of the API + events. I’ll get into the details later down the line, but the basic gist is that casts that are currently being simulated will be their own objects.

1 Like

How would you go about adding a recoil to this? Or did I overlook the module and missed where I could have learn about it?

Will you be updating the module to support the new method of casting rays?
WorldRoot:Raycast() which also takes RaycastParameters and returns RaycastResults?

https://developer.roblox.com/en-us/api-reference/function/WorldRoot/Raycast

Maybe. I don’t know. It depends on how necessary it is. For recognition of modern API? Sure. If I do it, I’ll make a caster able to work in certain specific world contexts too for support in things like ViewportFrame, but that’s for later.

Alright I’ve been doing some API changes for the update that allows redirecting the cast in realtime. This is not live yet, just in the works.

Here’s what I have so far, if you all think I should add or change something, let me know.

First things first. Under this new update, calling Caster:Fire() will now return an instance of a new type called ActiveCast. ActiveCast offers a number of important methods, including:

  • ActiveCast:GetPosition, ActiveCast:GetVelocity, and ActiveCast:GetAcceleration (as well as methods to set this data too). There is also AddVelocity and AddAcceleration for gradual changes.
    • These methods will allow you to alter the trajectory during runtime. Should be self-explanatory.
  • ActiveCast:Pause, ActiveCast:Resume, and ActiveCast:Terminate
    • Pause and Resume should be self-explanatory. A paused cast will be frozen and not simulate. Events will not fire, and the bullet will not move nor will time increment.
    • Terminate will cause a cast to end early. It has a boolean parameter called skipRayHitCall which, if true, will cause the RayHit event to not fire when the cast is terminated. If false, the RayHit event will fire with its default empty args, exactly like when a cast times out and hits its maximum distance.

Secondly, events are changing just a bit. The core Caster object will still have its RayHit and LengthChanged events, with the exception that their first parameter will be a reference to the ActiveCast that caused those events to fire. On top of this, ActiveCast instances now have their own separate events for RayHit and LengthChanged that function identically to what you are used to right now.

On top of this, I’m adding methods to dispose of casters. There is an instance leak for the signal objects. calling this new dispose method will delete the bindables that are used for events.

Any opinions on this structure? Anything that should be changed?

3 Likes

I have made a grappling hook with this fast cast module, you can check it out here.

2 Likes

Finally got FastCast to not be framerate based. This gif should show it well. Sometimes it has a few small wonky points which I have no idea whats causing them
YNpL0FfVwt

5 Likes

You added a way to manually enforce resolution? …Interesting, I could get that added later on. That could be very beneficial.

1 Like

I’ve been having trouble getting this to fire models instead of parts. The projectile glitches out and I can’t orient it properly. Any ideas on how to achieve this?

1 Like