Projectile+ | An all-in-one projectile system [1.0]

Get Module Here | Test Place

I would've uploaded the marketplace model but Roblox kept on flagging it for some reason

All Documentation is included in the module.

This Projectile system was created by me @Bartokens as a challenge for myself. I wanted to recreate the projectile systems seen in games like Marvel Rivals and Overwatch, where you have full control over hit detection and how they move. The main reason why I decided to make this was to see if I could, since I couldn’t find any existing open-source projectile systems on Roblox. It’s free to use, and currently functional, however there are a few bugs with the current version, so it’s not entirely perfect.

What is Projectile+?

Projectile+ or ProjectilePlus is an all-in-one projectile system, and by that I mean it includes everything you would want in a projectile system, and if it doesn't then please request new features. This includes:

* Projectiles with customizeable hit detection, sizes, speeds, and paths.
* Projectile bouncing
* Projectiles with gravity
* Projectiles with homing
* Projectile "Catchers" which can be used to intercept projectiles.
* Hitscan 
* Projectile time control
* Client Visuals

As far as performance goes, you can expect FPS drops at a few hundred projectiles, but this is also dependent on how your handling your VFX. The projectiles use custom physics rather than built-in Roblox body movers or physics. It uses basic SUVAT equations during runtime, and I got the idea for that from this post

Features

Basic Projectiles

Projectiles can be made with a simple:

|---|---|
||local StartPosition |
||local Direction |
||local Owner = Character|
||local Key = Laser|
||local Radius = 2|
||local LifeTime|
||local Speed = 100|
|||
||local Projectile = ProjectileSystem.CreateProjectile(Owner,Key,Radius,Direction,StartPosition,LifeTime,Speed)|

Doing this will instantly create a projectile. The parameters should be self explanatory, but you can read more about them in the documentation.

Projectiles also have properties that can be changed during runtime, including speed and radius. You can read about all of them in the documentation.

Bouncing Projectiles

Projectiles have a special property called ReflectEnabled. This can be toggled on and off whenever you like, and makes it so that the projectile bounces off everything it runs into. This includes players, but that behavior can be toggled.

Projectiles with gravity

Projectiles by default don’t have gravity, but they have a special property called GravityEnabled, as well as a property called GravityForce. These can both be changed during runtime, and customize how gravity works for individual projectiles.

Projectiles with homing

Projectiles have a special method called Homing, which makes the projectile actively follow a specified BasePart. The strength of the homing is a separate parameter called HomingStrength. Additionally you can change the HomingTargetPosition property during runtime, which makes the projectile home in on a specific Vector3.



Projectile Catchers

There is a special class of objects within the system called “Projectile Catchers”. These are imaginary boxes that do not take up physical space in the workspace, but rather use @azavier123 's zone module to detect when projectiles run into them. This can be used for things like barriers that destroy projectiles, or abilities that can reflect projectiles. Catchers can be welded to BaseParts as well, allowing them to move with whatever they’re welded to.

Overwatch Style Barriers


Ultrakill style coin reflect

Inspired by Magneto’s ultimate ability from Marvel Rivals:

Hitscan

Now this is probably the most underdeveloped part of the module, but for good reason. Hitscan is not something you need this module to do, as it can be done very easily by just using raycasts. If you aren’t aware, Hitscan type hit detection and Projectile type hit detection are two very different methods of creating bullets in shooter games. Hitscans hit instantly, and projectiles take time to actually hit their target. The only difference between this and raycasting normally is that this Projectile Catchers can intercept Hitscans.

Time Control

My favorite feature of this module is the ability to control the time scale of projectiles. Each projectile has a special property called TimeScale that is 1 by default. This property can be changed during runtime. Increasing it makes it go faster, and lowering it makes it go slower. You can actually go in the negatives, and reverse the time of the projectile.

Time Stop:


Projectiles that start at normal time but slowly reverse:

Client Visuals

By default the projectiles are invisible. In order to see them, you’ll have to require the Client module under the main module. You would require this in a LocalScript, ideally under StarterPlayerScripts. Then, you can detect the Client.ProjectileAdded event, and then use the WeldPart method, which makes any BaseParts you pass through it follow the trajectory of the projectile. You can even weld multiple BaseParts to one projectile at once.

Full Documentation (If you don't want to download the module)

`–Version 1 Documentation
–Projectile Plus developed by Bartokens. Zone module by azavier123

–Projectile Plus is an all-in one projectile system that handles client-server interactions and uses it’s own physics calculations.
–Projectiles are not parts, but rather tables that are constantly updated on the server.
–You can create your own parts on the client for visuals, and handle all events on the client such as projectile hit.
–You can also differentiate between hitting players and hitting the map.
–Includes Homing, Gravity, and Bouncing features.

–Setup Guide:
–1. Place the ProjectilePlus module in ReplicatedStorage.
–2. In any server script, require the module (the parent of this script) to activate it.
–3. In any local script, require the Client module to activate the client.
–4. You can now use any of the Server or Client methods without error.

–Server Methods:


–ProjectilePlus.CreateProjectile(

– Owner : Model, --The owner of the projectile. You would set this to the character of the player.

– Key : string, --A unique identifier to give the projectile which can be referenced on the client.

– Radius : number, --The radius of the Projectile.

– Velocity : Vector3, --The direction the projectile will move towards.

– StartPosition : Vector3, --The position the projectile starts at.

– Lifetime : number, --How long the projectile will last in seconds before being destroyed.

– Speed : number, --The speed of the projectile

– raycastParams : RaycastParams --Optional RaycastParams

–)
–Creates a projectile on the server

–Projectile Properties:

--Owner --The owner of the projectile. Ignored on hit detection. 

--Speed --How fast the projectile moves.

--TimeScale --1 by default. Can be used to speed up or slow down the projectile.

--Radius --The radius of the projectile

--ReflectEnabled --Determines whether or not the projectile bounces when hitting map surfaces.

--Velocity --The direction the projectile moves in

--Position --The position of the projectile. Updates every frame.

--CFrame --Read only property. The CFrame of the projectile. Updates every frame.

--Key --The unique ID of the projectile

--Lifetime --How long the projectile will exist in seconds.

--LastTime --The last time the projectile was updated on the server.

--StartTime --The time the projectile was created.

--Debounce --The debounce time of the projectile. Used to prevent hit events from firing multiple times.

--HitList --A table of all hit players by the projectile. Resets for each individual player on debounce.

--DestroyOnHit --Determines whether or not the projectile is destroyed when hitting a player.

--DestroyOnMapHit --Determines whether or not the projectile is destroyed when hitting the map.

--CheckForPeople --Determines whether or not the projectile actively checks for players. Turn this off if you don't need player detection

--CheckForMap --Determines whether or not the projectile actively checks for the map. Turn this off if you don't need map detection

--UseRaycastingForPeople --When true the projectile will use raycasting instead of spherecasting for player detection. Ideal for smaller projectiles.

--UseRaycastingForMap --When true the projectile will use raycasting instead of spherecasting for map detection. Ideal for larger projectiles.

--raycastParams --The raycastParams used for hit detection.

--OParams --The OParams used for spatial queries.

--OnHitSomeone --A function that is fired when the projectile hits a player.

--OnHitMap --A function that is fired when the projectile hits the map.

--ID --The unique ID of the projectile created by HTTPService:GenerateGUID()

--HomingTarget --The current homing target of the projectile. Only works when projectile:Homing() is called.

--HomingTargetPosition --The current homing target position. Only works when projectile:Homing() is called.

--HomingStrength --How aggresively the projectile will follow the homing target. 

--GravityEnabled --Toggles gravity for the projectile.

--GravityForce --The strength of gravity for the projectile. 1 by default.

–Projectile Methods:

--projectile:Destroy() --Destroys the projectile.	

--projectile:Homing(Target, Strength) --Makes the projectile begin homing on a specified target part.

--projectile:StopHoming() --Makes the projectile stop homing.

–Projectile Events:

--projectile.Destroying() --Fired when the projectile is destroyed.

--projectile.HitSomeone(Hit,HitPosition) --Fired when the projectile hits a player.

--projectile.HitMap(Hit,HitPosition) --Fired when the projectile hits the map.

–ProjectilePlus.CreateCatcher(

--Size: Vector3, --The size of the Catcher

--Cframe: CFrame, --The CFrame of the Catcher

--Shape: Enum.PartType --The Shape of the Catcher.

–)
–Creates a projectile catcher which detects when projectiles collide with it.

–Catcher Properties:

--OnCaught --An optional function which is fired on projectile caught.

--Debounce --How many seconds before the Catcher can catch the same projectile again.

--CaughtList --The list of projectiles caught by the Catcher. Individual projectiles removed on debounce.

--Position --The position of the Catcher.

--CFrame --The CFrame of the Catcher.

--Size --The size of the Catcher.

--Shape --The shape of the Catcher.

–Catcher Methods:

--catcher:Destroy() --Destroys the Catcher.

--catcher:WeldTo(WeldTarget, Offset) --Welds the Catcher to a part with an optional CFrame offset. The part can also be any table with a CFrame property, meaning Catchers can be welded to projectiles.

--catcher:UnWeld() --Unwelds the Catcher.

–Catcher Events:

--catcher.Caught(Projectile) --Fired when a projectile is caught.

--catcher.Destroyed() --Fired when the Catcher is destroyed.

–ProjectilePlus:HitScan(

--Origin: Vector3,  --The origin point of the Hitscan raycast.

--EndPoint: Vector3, --The end point of the Hitscan raycast.

--MaxRange: number, --The max range of the Hitscan raycast.

--raycastParams: RaycastParams --Optional rayCastParams.

–)
–A built in HitScan function. It is really just a simple raycast, but it can be intercepted by Projectile Catchers.
–Returns a model, a table, or a part depending on if it hit a player, a catcher, or the map respectively.
–Currently does not work with wedge or cylinder catchers.


–ProjectilePlus:Initialize()

–Activates the server side of the module. This must be called before any other functions, though it is automatically called on first require.


–ProjectilePlus:GetProjectiles()

–Returns all existing projectiles.


–ProjectileSystem:ClearProjectiles()

–Destroys all existing projectiles.


–ProjectileSystem:PlayerCleanup(Player:Player)

–Destroys all existing projectiles belonging to a specific player. Automatically called on player leaving if AutoPlayerCleanup is set to true.


–Client Events:

–Client.ProjectileAdded(Projectile)
–An event that fires when projectiles are created on the server and then passed to the client.

–Client.ProjectileDestroying(Projectile)
–An event that fires when projectiles are destroyed on the server and the client.

–Special Client Projectile Methods:

–Projectile:WeldPart(part: BasePart, offset: CFrame?)
–Welds a BasePart to the projectile by updating it’s CFrame every frame with an optional offset.

–Projectile:ClearWelds()
–Clears all active welds

`

Current Bugs

Bugs

Now Ideally I would’ve liked to release this system bug free, but I’m on a tight schedule so I don’t have the time to figure this out at the moment. Currently there is one somewhat noticeable effect that has to do with how the client handles projectiles. Currently, the client does it’s own physics simulations. This removes the need for remote events, and allows the client projectiles to follow the exact same trajectories as the server projectiles. However, there are some cases where the client projectiles follow offset paths compared to the server projectiles. This becomes more and more noticeable the more the server lags. This is most noticeable with projectiles that curve:


As you can see in the video, the server projectiles (colored red) go exactly to where the mouse is, but the client projectiles (colored blue) are slightly offset as the projectiles curve. Honestly, I’ve been experimenting with solutions but nothing has worked as I’ve wanted it too. If you have any ideas than I’m open to suggestions.

And that’s about it. Please let me know how you like the system. I’ll be updating it occasionally if I find the time.

14 Likes

A lot of problems in code here.

Let’s start with Signal module. You do not reuse threads and instead make new one for each function, this is a bad practice which leads to time execution inefficiency and memory spikes.

You make a new disconnect function for each connection. That is a bad practice as well. As well as making 2 tables for a signal. And why are you cloning a table of listeners???


Now I’ll go to the main module.
Catchers create new functions that exclusive for them. For what reason is this your choice for realization? (P.S. it’s not true) It gives huge amount of memory usage. As well as the WeldTo which creates new connection each time instead of utilizing a single one.
image
What is the reason of “pcall” here?
image
Why are you creating empty CFrame for each projectile where you can just set it to already created empty CFrame?
Also, what is the point of proxy table, when you could just use a newproxy(true) to an original projectile object and do all the magic things here? You’re just creating a ton of memory usage here.

Overall, only thing you did correctly is formulas. The code itself is heavily inefficient and gives memory leak. Waiting for further updates which’ll delete all the mistakes you did at first. Zone module needs a rewrite as well in my opinion.

Also, I advise you to compress the data you’re sending to client, because it won’t destroy the receive queue.
image

9 Likes

pretty cool module. idea, make it so you can have projectile path predictions, to tell where a projectile will travel before you fire it

2 Likes

Liking the timescale inclusion there! One thing to note is that in the references video (my projectile one) I didn’t have access to particles having a changeable timescale property. I’m only glancing over videos, but it’d be a cool inclusion to have!

2 Likes