Best Methods To Handle Arrow Projectiles

I am wondering what the latest recommended methods are in handling an arrow projectile, such as firing an arrow from a bow.

1. Travel Time
I would like the arrow to travel in the air, and not be instantaneous like a bullet. Having a slight arc would be a bonus.

2. Speed & Range
I would like the arrow to have a given speed, and a limited distance range, say 100 studs.

3. Hit Detection
I am reluctant to trust the client with hit detection, since hackers can exploit this.

4. Client/Server Lag
This seems to cause the most problems in that the server sees the projectile in one location, while the clients sees it in another, due to lag.

3 Likes

you could generate an arrow and then adding an body velocitywith an direction and adding downforce to it

1 Like

1.
You can do what @DutchDeveloper said
edit: Or, you could prossibly use tweenservice, you just have to use a lot of math.

2.
I don’t know, never done anything like this. Sorry
edit: For limited distance, you can probably just check the magnitude on the start and then check the difference in a loop or something to make sure it doesn’t go further than your limit.

3.
You don’t have to trust the client. Give the server mouse.Hit and then get the look at CFrame by doing CFrame.new(arrow.Position, mouse.Hit.p).

4.
Client/Server lag shouldn’t be a problem if you do everything on the server. Remember to give the networkownership of the arrow to the server. There might be a small delay from when the client shoots the arrow until it actually happens but you can’t do much with it.

1 Like

Please use client-sided hit detection. Your non-exploiting players will thank you. You will need to do a server check to see if the hit is “possible” to prevent exploiters.

7 Likes

Agreed. This is what we do in Vesteria.

It is far better to validate if the shot is realistic (which is trivial math) than to have the server do the shot.

Using TweenService is a bad idea.

With TweenService you are restricted to using only certain paths (quadratic, linear, etc).

TweenService will ignore collisions and external forces, going right through them. This would also make it a very complicated process to detect collisions.

This can also only be done on the server which would cause some significant lag for non exploiting players.

1 Like

Here’s what I’d do in your situation:

(Code is running in a loop)

  1. Add a set acceleration to the velocity
  2. Multiply the velocity by a coefficient (e.g. 0.9) to simulate drag
  3. Subtract the standard gravity acceleration (32ft/s, the scale I use is 1 ft is 1 stud) multiplied by a coeffecient to account for lift
  4. Set the MaxForce to Mass * Velocity and power to MaxForce.Magnitude * Velocity
  5. Point the arrow in the direction of the velocity using a BodyGyro

If you want to calculate lift and drag for a morr realistic result, you can check out some drag, gravity, orbit, lift, delta-v, air density, and general projectile-related equations that I’ve coded here

I think it would be very over the top to implement these physics (I created the repo with the goal of simulating very realistic rockets), which is why I recommend for you to do something similar to DutchDeveloper’s idea.

EDIT: Github link should work now

7 Likes

You might want to check out this resource here:

7 Likes

I made a bow and arrow system that takes the mouse.Hit position (sent to server through remote) and then spawns an arrow, repeatedly moving it along it’s lookvector/tilting the angle slightly to account for drop. Collision is handled by raycasting and checking magnitude each frame. This method seems to work nicely but it can lag alot if the server is suffering from lag since the arrow is moved on heartbeat.

heres a place file I made with the bow and arrow:

2 Likes

For issues 1. and 2. I can recommend this post I wrote.

6 Likes

Client/Server lag shouldn’t be a problem if you do everything on the server. Remember to give the networkownership of the arrow to the server. There might be a small delay from when the client shoots the arrow until it actually happens but you can’t do much with it.

What about creating the arrows on each local client vs the server? I don’t know the game’s circumstances, but if everyone is going ham and shooting arrows the server may bog down creating objects and handling body movers etc. This would also remedy the visual delay on the shooter’s client.

1 Like

You mind telling me what kind of validation you do? This is my personal method which I got from @Xan_TheDragon.

local LAG_THRESHOLD = 4 --This is the added distance threshold to accomodate for lag.
local MAX_PART_DISTANCE_THRESHOLD = 2.5 --This determines the maximum distance from the center of a bodypart that got hit to the hit location. For instance, if I shoot the side of someone's torso, that distance would be 1 stud from the center to the side if they hit it perfectly half way up

function OnClientClaimedToHitPart(ClientHitPart, ClientHitPoint, ClientHitPartPos)
    local ServerHitPartPos = ClientHitPart.Position --This will be the part's position to the server, of course.
    local ClientMinDistance = (ClientHitPartPos.Position - ClientHitPoint).Magnitude
    local CrossPartDistance = (ServerHitPartPos - ClientHitPartPos).Magnitude
    
    if ClientMinDistance <= MAX_PART_DISTANCE_THRESHOLD and CrossPartDistance < LAG_THRESHOLD then
        --This hit should ideally be valid, because the difference between what the client says and what's really happening on the server are within acceptable bounds.
        --Do damage here or something
    end
end

I’m always looking to improving my methods.

2 Likes

My last answer was pretty vague. I’ll give you a more detailed one. You might want to use simpler methods of calculating drag and lift. If I have the time (right now I’m just writing this due to procrastination) I’ll consider making an even more detailed tutorial on projectiles.

Physics

You should run this code on either the client or the server. Keep in mind that setting Network Ownership will cause a delay, but running it on the server may cause the arrow to not look as smooth.

Firstly, apply a force, in the direction of mouse.Hit, when the mouse is released. Then, in a heartbeat loop, calculate the downforce (([gravitational acceleration] * mass) - lift) and the drag ((velocity * velocity * density / 2) * [facial area] * coefficient). You can base your lift equation off of this one.

You can add these two to get the resistant forces. You can apply this force by using either a VectorForce, BodyForce, or a BodyVelocity. If you’re using a BodyForce or VectorForce, don’t calculate gravity (it is already accounted for).

You should then set the orientation of your arrow, using a BodyGyro, to the direction of the velocity to simulate the arc. You can change the drag efficiency and the lift coefficient to make the arrow fly farther, shorter, higher, lower, or at a different speed.

If you’re using a BodyVelocity (which I recommend), you can get the velocity by adding ((force / mass) * [delta time]) to a variable at the end of your loop. You can get the power by simply multiplying force and velocity.

Hit Detection

Because we are using BodyMovers, we can use a Touched event on the server. When the touched event fires, you should first disconnect it to save memory and to make it so it can only damage what it hits first. Secondly, check if it hit a model with a Humanoid and deal the damage. I would make this directly proportional to the speed it collides at. You can weld it easily using WeldConstraints and run further code allowing for the player to pick it up - or just delete it.

2 Likes

This is one of the more common ways of replicating physics-based projectiles-it works well!
However since things are rendered on the clients, the client who fired the arrow has some open doors for manipulating the server’s hit registration.

Already a lot of good and useful comments here oof… but for hit detection I recommend using region and cast the region around the object. For sending the arrow I recommend using regular body movers for they are not as laggy and are easier to optimize for script performance. Use BodyVelocity and if its really necessary use Body Position.

A VectorForce would be the most efficient and the most basic option. There’s not much point creating regions every heartbeat when you can just use a Touched event.

3 Likes

Thanks, I was reading through for some ways for an arrow and I havent throught of using bodygyro in the direction of velocity until now.

1 Like

Hello, I’m interested in your way to deal with the physics part of the projectile as you stated in your reply here. I know I’m a couple months due old of this discussion but if you’d go more in detail perhaps in DMs of the physic calculations and Body Velocity/Gyro that would be great :slight_smile:

If you’re still interested something simple I do is,

--initialize the projectile
bullet.velocity = velocity
bullet.drop = v3(0, drop, 0)
bullet.oldposition = origin
bullet.position = origin

--run every frame on the projectile
bullet.position = bullet.position * cf(0, 0, bullet.velocity*step)
bullet.velocity = bullet.velocity + bullet.drop*step --if game is running at slower speed ray will travel at same speed and have same arc
local distance = (bullet.oldposition.p - bullet.position.p).magnitude
local newray = Ray.new(bullet.oldposition.p, (bullet.position.p - bullet.oldposition.p).unit * distance)
--hit detection or whatever you want here
bullet.oldposition = bullet.position --set this after doing any calculations

Yeah I wouldn’t really recommend this now it is a bit OTT for most games. Do something simple like setting the .Velocity property and setting a BodyGyro that points towards it.