Here’s a video of me not using an fps unlocker https://gyazo.com/2594d9203415db5131671176fd1a5026
Here’s a video of me using one https://gyazo.com/912a18c4e0ffb8a68567c8db2ae8cc63
Idk
Here’s a video of me not using an fps unlocker https://gyazo.com/2594d9203415db5131671176fd1a5026
Here’s a video of me using one https://gyazo.com/912a18c4e0ffb8a68567c8db2ae8cc63
Idk
Where and how do you use position
?
So position is the length of each part in the projectile * number, number is just so I know what position number it is, then position * ray direction gives me the actual position.
You need something more complicated, then.
Flip your thinking around: What happens if one frame takes a whole second to render? Does your code handle that?
You need a way to decouple the number of parts you draw from the number of frames.
For example (this isn’t tested, but it demonstrates the concept – I tried to name variables well so you can tell what they are):
local STUDS_PER_SECOND = 100 -- how many studs the lightning flies in a second
local SEGMENT_LENGTH = 4 -- how long each segment is
local function Fire()
local alreadyDrawn = 0
local position = 0
while true do -- need some condition here
-- get the time elapsed
local dt = game:GetService("RunService").Heartbeat:Wait()
-- the next position to jump to
local nextPosition = position + dt * STUDS_PER_SECOND
-- get how many segments we'll need to draw this frame
local needToDraw = math.floor(nextPosition / SEGMENT_LENGTH) - alreadyDrawn
for i = 1, needToDraw do
local segmentPosition = position + i * SEGMENT_LENGTH
local segmentNumber = alreadyDrawn + i -- if you need it for some reason
-- draw segment using segmentPosition as the position
end
position = nextPosition
alreadyDrawn += needToDraw
end
end
Basically i’ll show you what works in my projectileclass, because a lot of the code you shown seems to look wrong.
Lets get some formulas cleared first:
Distance = Velocity * TotalElapsedTime
or
Distance = Distance + (ElapsedTimeFromLastSetPosition * Velocity)
Second one is better since you can switch velocity in the middle of flight, because it’s instanaous velocity (the velocity at a paticular timeframe)
Now to use the second method here would be an example.
local Hearbeat = game:GetService("RunService").Heartbeat
function NewProjectile()
return {
Velocity = Vector3.new(10,10,0); -- digonal up movement basically the equation: y = x * VELOCITY SO in this case the velocity is 10
Model = workspace.Baseplate;
Position = workspace.Baseplate.Position;
}
end
local MyProjectile = NewProjectile()
Hearbeat:Connect(function(ElapsedTimeFromLastSetPosition)
MyProjectile.Position = MyProjectile.Position + (ElapsedTimeFromLastSetPosition * MyProjectile.Velocity)
MyProjectile.Model.Position = MyProjectile.Position
end)
The smaller the timeframe the smoother it will move because it will move less distance, at even smaller timeframes. (Good compability with FPS unlockers)
Idk how this would work with my projectiles seen as there not even moving or using an physics they just look like there moving.
So you calculate how many segments you need to make. Alr I’ll try it out thanks.
What the script I showed you isn’t with phyiscs is uses math and cframes.
Yeah but I’ve literally done the same thing, it doesn’t work the same for my case.
It works for me try excuting the same exact code I did first and tell me if it works for you, and then modify it.
Ok, but that’s not the problem he’s having? The Heartbeat is a form of a wait. This has nothing to do with the actual problem, a Heartbeat wait is more than enough to not crash the game. This is false information. The reason why OP is having the problem is because RunService events are variable frequency, firing once per frame, meaning the frequency at which they fire is based on the client’s frame rate.
The simple way to fix this is scaling your increments by delta time, as people are explaining above.
Also, I used to have problems with this as well with an old gun system I was messing around with, and I was too lazy to scale the increments by delta time, so you can just use a ratio with delta time since your increments are already for 60 FPS.
Scaling the values to find a ratio like 60 : userFPS is an easy way to scale the values, so it should look the same on every frame rate.
Example: 60 : 120 = 0.5, will make it slower to compensate to look the same, because 120 times a second is double 60, because your increments are made for 60 FPS
Also, you should define RunService outside of the loop since it’s a constant.
local rs = game:GetService("RunService")
local fps
local ratio
local number = 0
while true do
fps = 1/(rs.Heartbeat:Wait())
print(fps)
ratio = 60/fps -- Since it needs to be slower at higher framerates, the number needs to smaller as dt decreases
number = number + 1
local position = ((4 * 100) * number) * ratio
end
Heartbeat and other RunService events are variable frequency, so the frequency which they are fired based on a user’s frame rate.
Your increments are for 60 fps, meaning that it needs to be called 60 times a second for it to look good.
120 FPS is double that, 120 times a second, which is why the speed and stuff is faster. It’s twice as fast.
If you capped to 30, you should see that it gets slower, because it’s called 30 times in a second. Which means it would be about half as fast.
Also, your code is pretty vague, so I’m not sure if this is going to work. Can you provide a larger snippet so I can have more context? How are number and position being used?
The others’ advice to scale the displacement of your projectiles using delta time return values is solid. Although, you really be connecting a function and using the event-based approach instead of a while
or repeat
loop:
local conn
conn = game:GetService("RunService").Stepped:Connect(function (t, dt)
-- Step your projectile forward using dt, as above posts describe...
end)
-- Remember to call conn:Disconnect() once the projectile expires!
Doing it this way also has the benefit of being non-blocking, something that is crucial for good performance and API design.
More importantly…
Something I want to address is using Stepped over Heartbeat. Heartbeat is not the event to use for this case. You should be using Stepped
, 100% here.
See the Task Scheduler article on the Developer Hub for more info on this. Not to toot my own horn, but I wrote the article.
This isn’t what os.clock
is meant for! Don’t use it in this way. If you really need a timing mechanic like this, use Workspace.DistributedGameTime
instead.
RunService coordinates time already through the Stepped event, which returns timeElapsed, deltaTime
. See my post above for details.
Whats the difference with using a run service loop?
A while loop will hang the script in the loop until it is broken or its exit condition is met. Binding the same code to an event of RunService will let you be able to run code in a new thread.
In this pseudocode example “End” would never be printed since there is nothing that stops the while-loop. That line is considered to be ‘unreachable’
print("Before")
while (true) do
print("Loop")
end
print("After")
In this example, the event listener does not yield the code.
print("Before")
game:GetService("RunService").Stepped:Connect(function(t, dt)
print("Loop")
-- We also have the arguments "t" (how long the game has been running)
-- and "dt" (how much time has passed since the last frame)
end)
print("After")
The output from that would look something like this:
Before
After
Loop
Loop
Loop
Loop
...
What method to use depends on what your script is supposed to do. There are use cases where you need to wait until a loop is finished and there are cases where you would have code running in the background.
Exactly this. And more importantly, what was once your loop’s exit condition simply becomes a matter of disconnecting the connection returned by Connect
, either within the function or outside of it.
print("Before")
local conn
conn = game:GetService("RunService").Stepped:Connect(function(t, dt)
print("Loop")
-- You can stop it in here...
if stopCondition() then
conn:Disconnect()
end
end)
print("After")
-- ...or out here!
if stopCondition() then
conn:Disconnect()
end