Has anyone tried to to use this for a Zombie type game with some custom NPCs?
I wonder how efficient this module would remain if you recalculated path finding every 1 second for the zombies?
Has anyone tried to to use this for a Zombie type game with some custom NPCs?
I wonder how efficient this module would remain if you recalculated path finding every 1 second for the zombies?
This is a good module. Easy to convert my existing server tweens to client tweens which is good since I heard that too many server tweens are bad, the only downside is though is that fast tweens are not smooth. This can be noticed in things like flashing warning lights which tween on and off in ~0.3 seconds since they are simulating old incandescent bulbs which fade on/off as the filament heats and cools. Sometimes it will just blink straight on and off as if it was not being tweened at all and some others go in steps from off to 1/2 brightness and then full. If there is any workarounds for this then I would continue rolling out this change to my vehicle indicators.
I love this module, thanks!
One thing im messing around with right now is that my tweens seem to only work with parts that are streamed in at the time the server code executes. Im still trying a few more things, but if anyone has solved this issue - please let me know.
Hi,
I have the tweening working for moving a part forward but want to bounce the part at the same time, how is this done? can you run two tweens at the same time?
Regards
Hey so Iâm trying to tween a model and when I run it it doesnât even move and no errors om aware of either. Thanks!
TweenModule:Construct(Elevator.Doors.DoorR.DoorR.PrimaryPart, TweenInfo.new(DoorOpenTime, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut), {CFrame = Elevator.Doors.DoorR.GoTo.CFrame}):Play()
TweenService Plus is an amazing resource, however, I believe I have found a bug, and it is quite concerning.
Bug: TweenService Plus affects Proximity Prompts.
I have an object that is tweened in a constant loop. This object contains a Proximity Prompt that prints a string when triggered. The object is tweened using TweenService Plus from point A â point B â point C â point A. Each tween is approximately 35 seconds long. During the first roughly 17 seconds of each tween, the Proximity Prompt is triggered, and the string is printed in the output. However, in the second half of the tween, the Proximity Prompt does not print anything in the output.
I tested many different possibilities to determine the cause of this issue, however, I settled on the conclusion that this bug was due to the object being tweened. This is because, when I removed all the tweens, the Proximity Prompt worked like a charm without any issues. Although I am no professional, I suspect that the TweenService Plus module is affecting UserInputService or TweenService which Proximity Prompts use.
I hope you can look into this issue or provide some ideas as to what the cause may be.
Thank you in advance.
Please tell me it works with the Streaming_Enabled property set to true, I also made a api that does exactly the same, just that it supports robloxâs content streaming api, it works fine except that it doesnt have that latency and events feature, if your api works with streaming enabled then Iâd be happy to use it!
Just an update: here is what I have found.
Letâs say you have a proximity prompt in a part that is tweened with this TweenService Plus module. The âTriggeredâ event will stop working occasionally, however, other events such as âTriggerEndedâ, âHoldDownBeganâ, and âHoldDownEndedâ will function without any issues.
Hello!
I have run into this issue in the past and I have answers. This is indeed not a TweenService Plus issue, but rather a security feature from roblox. Let me explain:
- The server has the part in a certain place, say
Vector3.new(10,0,0)
- The client has the part in another place, say
Vector3.new(-10,0,0)
To confirm this, and to clarify this issue to the OP, can you please check these points:
Thank you very much for clarifying the issue I was facing.
The tween is about 40 seconds long and travel approximately 70 studs within this time frame. Since I am using TweenService Plus to tween, the object I am tweening will appear on the server as if it is teleporting.
--[[
TweenService+ V1.2 (UNOFFICIAL)
@rek_kie on 8/9/20
Please read the devforum post here!
https://devforum.roblox.com/t/tweenservice-plus/716025
Update Log [V1.2 - UNOFFICIAL]:
- Minor Improvement
Update log [V1.1]:
:Play() now has a mainObject parameter
-- You are now able to set the object to calculate distance from if you are using :Play() with a range. This is useful if
you are trying to tween something that isn't a BasePart, but you still need to calculate distance from something to tween
the object.
Setup instructions:
Put this module into REPLICATEDSTORAGE, nowhere else.
To set up, all you have to do is require this module somewhere on a local script, and require it on a server script to be
ready for use on the server side.
Documentation:
=== FUNCTIONS ===
tweenServicePlus:Construct(instance Object, TweenInfo info, table Properties, number timeThreshold, bool debugMode, bool clientSync)
Returns: tweenObject
- The "equivalent" to TweenService:Create(). The time threshold is the latency threshold if clientSync (latency compensation)
is enabled. The object is the object to tween, the TweenInfo is the info to use, the properties and are properties you
want to tween to.
- debugMode defaults to false, and clientSync defaults to true.
tweenObject:Play(array/instance clients, number Range, string rootPart, BasePart mainObject)
- If clients are not specified, then the tween will play for all clients (the default setting). If you want to specify clients, either
pass in an ARRAY of clients, or pass in one individual client. These should be PLAYER INSTANCES.
- If a range (a NUMBER) is specified, then the tween will only play for clients (you can specify them as stated above) within the range
(in studs) of the object being tweened. EXTREMELY useful for optimization and reducing client load. You wouldn't need to tween
something for a client if they're somewhere like 1000 studs away from the object.
- rootPart: Only works when a range is specified. Defaults to HumanoidRootPart. If you specify this (has to be a string)
then the distance calculations will be from the distance from the tweened object to the specified rootPart. This uses a
recursive :FindFirstChild(), so make sure that your rootPart has a unique name so it isn't mistaken for something else in the
player.
-- mainObject: The object to calculate distance from if you are using :Play() with a range. Defaults to the original object you're trying to tween.
This is useful if you are trying to tween something that isn't a BasePart, but you still need to calculate distance from something
to tween the object. (Ex. You want to tween a pointlight and use a range of 50 studs, but a pointlight doesn't have a workspace
position. So, you can set the mainObject to a BasePart and then the range would be 50 studs within that part.)
tweenObject:Cancel(array/instance clients)
- This behaves like the normal TweenService. Documentation is on developer.roblox.com
- Cancels the tween. If clients are specified (either an ARRAY of player instances or one player instance), then the tween will only
cancel for the specified clients. Otherwise, it cancels for all clients.
- Always stays in sync with the server. If you cancel a tween and then call :Play() again, it will still have TweenService's normal
behavior and take the initial tween time to finish.
Tip: Cancelling for specific clients is not reccommended, though. It only has a few use cases and could lead to things getting
out of sync between your clients and the server.
tweenObject:Pause(array/instance clients)
- This also behaves like the normal TweenService. Documentation is on developer.roblox.com
- Pauses the tween. If clients are specified (either an ARRAY of player instances or one player instance), then the tween will only
pause for the specified clients. Otherwise, it pauses for all clients. If a tween is paused while it wa
- Also always stays in sync with the server. If you pause a tween and then call :Play() again, it will still have TweenService's normal
behavior and resumes where the tween had left off when it was paused.
=== EVENTS ===
- tweenObject.Cancelled -- Fires when a tween is cancelled.
- tweenObject.Resumed -- Fires when a tween is played after being paused.
- tweenObject.Paused -- Fires when a tween is paused.
- tweenObject.Completed -- Fires when a tween is completed.
]]--
local tweenService = {}
local clock = require(script.SyncedTime)
local rs = game:GetService("RunService")
local ts = game:GetService("TweenService")
local http = game:GetService("HttpService")
local wrap = coroutine.wrap
local tEvent = script:WaitForChild("TweenCommunication")
if rs:IsServer() then
if not clock:IsSynced() then -- make sure our clock is synced
repeat
clock:Sync()
task.wait(.5)
until clock:IsSynced()
end
end
local function infoToTable(tInfo)
local info = {}
info["Time"] = tInfo.Time or 1
info["EasingStyle"] = tInfo.EasingStyle or Enum.EasingStyle.Quad
info["EasingDirection"] = tInfo.EasingDirection or Enum.EasingDirection.Out
info["RepeatCount"] = tInfo.RepeatCount or 0
info["Reverses"] = tInfo.Reverses or false
info["DelayTime"] = tInfo.DelayTime or 0
return info
end
local function assign(object, properties, debugMode)
if not object or not properties then return end
for property, value in pairs(properties) do
object[property] = value
if debugMode then
print("Set "..object.Name.. "'s "..property.." to ".. tostring(value)..".")
end
end
end
function tweenService:Construct(obj, info, properties, timeThreshold, debugMode, clientSync)
if not obj then warn("This object doesn't exist.") return end
if not info then warn("Please provide some TweenInfo!") return end
if not properties then warn("Please provide some properties to tween to!") return end
if timeThreshold and not type(timeThreshold) == "number" then warn("Latency threshold must be a number!") return end
if debugMode and not type(debugMode) == "boolean" then warn("The debugMode parameter must be true or false!") return end
if clientSync and not type(clientSync) == "boolean" then warn("The parameter clientSync must be true or false!") return end
local startProperties
if info.Reverses then
for property, value in pairs(properties) do
startProperties[property] = obj[property]
end
end
debugMode = debugMode or false
clientSync = clientSync or true
local events = {
["Cancelled"] = true,
["Completed"] = true,
["Paused"] = true,
["Resumed"] = true
}
local tObject = {
["PlaybackState"] = Enum.PlaybackState.Begin,
["TweenId"] = http:GenerateGUID(false), -- so that we can identify each tween.
["IsPaused"] = false,
["IsCancelled"] = false,
["LastPlay"] = clock:GetTime(),
["TimeElapsed"] = 0
}
local function changeState(state)
tObject.PlaybackState = state
if debugMode then
print("Playback state changed. New playback state:", tostring(state))
end
end
for name, event in pairs(events) do -- Setting up the events to connect to
events[name] = Instance.new("BindableEvent")
tObject[name] = events[name].Event
end
local tweenWait = function(n)
n = n or 1/60
local now = tick()
repeat
rs.Heartbeat:Wait()
if tObject.IsPaused == true or tObject.IsCancelled == true then
if debugMode then
print("Tween cancelled/paused server-side.")
end
return true
end
until tick() - now >= n
end
local function completionWait(t)
local func = wrap(function()
if tObject.IsPaused then
t = t - tObject.TimeElapsed
if debugMode then
print("Tween is resuming from a pause. Length:", t)
end
events.Resumed:Fire()
tObject.IsPaused = false
end
if tObject.IsCancelled then
tObject.IsCancelled = false
end
if info.DelayTime > 0 then
changeState(Enum.PlaybackState.Delayed)
task.wait(info.DelayTime)
end
tObject.LastPlay = clock:GetTime()
changeState(Enum.PlaybackState.Playing)
local cancelled = tweenWait(t)
print("Cancelled:", cancelled)
if not cancelled then
assign(obj, properties, debugMode)
if not info.Reverses then
changeState(Enum.PlaybackState.Completed)
events.Completed:Fire()
elseif info.Reverses then
task.wait(t)
if not tObject.IsPaused then
assign(obj, startProperties, debugMode)
changeState(Enum.PlaybackState.Completed)
events.Completed:Fire()
end
end
end
end)
func()
end
function tObject:Play(clients, range, rootPart, mainObject)
rootPart = rootPart or "HumanoidRootPart"
if mainObject then
if not mainObject:IsA("BasePart") or not mainObject:IsDescendantOf(workspace) then warn("The mainObject must be a BasePart in the workspace.") return end
end
mainObject = mainObject or obj
if mainObject ~= obj and debugMode then
print("Set the main object to", mainObject.Name)
end
if clients then
clients = (type(clients) == "table") and clients or {clients} -- If you only provide a single player, it turns it into a table for you
if range then
for _, player in ipairs(clients) do
if player and player:IsA("Player") and player.Character then
local runTween = wrap(function()
local root = player.Character:FindFirstChild(rootPart, true)
if root then
if debugMode then
print("Found root part of "..player.Name..":", rootPart)
end
local dist = (mainObject.Position - root.Position).magnitude
if dist <= range then -- Tween it only for clients in the range
tEvent:FireClient(player, obj, infoToTable(info), properties, clock:GetTime(), tObject.TweenId, timeThreshold, debugMode, nil, clientSync) -- Tell the client to tween the object and the timestamp of when it was sent
if debugMode then
print("Sent tween data to "..player.Name..". Distance from object:", dist)
end
end
end
end)
runTween()
end
end
else
for _, player in ipairs(clients) do
if player and player:IsA("Player") and player.Character then
local runTween = wrap(function()
tEvent:FireClient(player, obj, infoToTable(info), properties, clock:GetTime(), tObject.TweenId, timeThreshold, debugMode, nil, clientSync)
end)
runTween()
end
end
end
else
if range then
for _, player in ipairs(game.Players:GetPlayers()) do
if player and player:IsA("Player") and player.Character then
local runTween = wrap(function()
local root = player.Character:FindFirstChild(rootPart, true)
if root then
if debugMode then
print("Found root part of "..player.Name..":", rootPart)
end
local dist = (mainObject.Position - root.Position).magnitude
if dist <= range then -- Tween it only for clients in the range
tEvent:FireClient(player, obj, infoToTable(info), properties, clock:GetTime(), tObject.TweenId, timeThreshold, debugMode, nil, clientSync) -- Tell the client to tween the object and the timestamp of when it was sent
if debugMode then
print("Sent tween data to "..player.Name..". Distance from object:", dist)
end
end
end
end)
runTween()
end
end
else
tEvent:FireAllClients(obj, infoToTable(info), properties, clock:GetTime(), tObject.TweenId, timeThreshold, debugMode, nil, clientSync)
end
end
completionWait(info.Time)
end
function tObject:Cancel(clients)
if clients then
clients = (type(clients) == "table") and clients or {clients}
for _, client in ipairs(clients) do
tEvent:FireClient(client, nil, nil, nil, clock:GetTime(), tObject.TweenId, nil, debugMode, "Cancel")
end
else
tEvent:FireAllClients(nil, nil, nil, clock:GetTime(), tObject.TweenId, nil, debugMode, "Cancel")
end
events.Cancelled:Fire()
tObject.IsCancelled = true
changeState(Enum.PlaybackState.Cancelled)
end
function tObject:Pause(clients)
if clients then
clients = (type(clients) == "table") and clients or {clients}
for _, client in ipairs(clients) do
tEvent:FireClient(client, nil, nil, nil, clock:GetTime(), tObject.TweenId, nil, debugMode, "Pause")
end
else
tEvent:FireAllClients(nil, nil, nil, clock:GetTime(), tObject.TweenId, nil, debugMode, "Pause")
end
tObject.TimeElapsed = clock:GetTime() - tObject.LastPlay
tObject.LastPlay = clock:GetTime()
events.Paused:Fire()
tObject.IsPaused = true
changeState(Enum.PlaybackState.Paused)
end
return tObject
end
if rs:IsClient() then
local player = game.Players.LocalPlayer
local tweens = {}
tEvent.OnClientEvent:Connect(function(obj, info, properties, timestamp, tweenID, threshold, debugMode, modify, sync)
if modify then
local tweenToEdit = tweens[tweenID]
if not tweenToEdit then -- if the tween doesn't exist, just return and give a warning
warn("The tween you tried to modify does not exist.")
return
end
if modify == "Cancel" then
tweenToEdit:Cancel()
if debugMode then
local latency = clock:GetTime() - timestamp
print("Cancelled a tween. Latency:", latency)
end
return
elseif modify == "Pause" then
tweenToEdit:Pause()
if debugMode then
local latency = clock:GetTime() - timestamp
print("Paused a tween. Latency:", latency)
end
return
end
end
local latency = clock:GetTime() - timestamp
local newtime = sync and info.Time - latency or info.Time -- If enabled, this syncs the tween up based on latency.
-- Ex. If the server told a client to do a 10 second tween and it
-- took .7 seconds to get to the client, then the time for the
-- client's tween would be 10 - .7 = 9.3 seconds.
-- I'm using Quenty's module to get a global timestamp, so
-- that server and client time are synced. This results in a
-- perfect sync between client and server tween completion.
-- To better see this for yourself, turn on debug mode and
-- look at the output to see how the server prints exactly
-- when the client tween ends. (If you are in Studio, Make sure you
-- stay watching only on the client though, because switching to
-- the server view pauses the client's game session on Play Solo)
if debugMode then
print("Approximate latency for ".. player.Name ..": " ..latency .. " seconds. \n New tween time: ".. newtime .." seconds")
end
threshold = threshold or 0 -- Defaults to 0. When you set this, this basically means the amount of time
-- the new tween time (after calculating latency) needs to be greater than
-- for the tween to play. If the new tween time after calculating latency is less than
-- or equal to than the threshold, the tween will not play.
if newtime > threshold and obj and properties and tweenID then -- some checks
local newInfo = TweenInfo.new(newtime, info.EasingStyle, info.EasingDirection, info.RepeatCount, info.Reverses, info.DelayTime)
local trackTween = wrap(function()
if not tweens[tweenID] then
tweens[tweenID] = ts:Create(obj, newInfo, properties)
end
tweens[tweenID]:Play()
tweens[tweenID].Completed:Wait()
tweens[tweenID] = nil
end)
trackTween()
if debugMode then
local printProperties = wrap(function()
print("Currently tweening properties of "..obj.Name..":")
for property, value in pairs(properties) do
print(property .. " to "..tostring(value))
end
end)
printProperties()
end
end
end)
end
return tweenService
u can replace the BindableEvent with Custom Signal Module (optional)
Does anyone know a proper way to reduce the packet size for the remote event âTween Communication?â
Edit: Just changing the TweenInfo tableâs string indexes to list indexes decreased recieved packet data by more than 30%.
u can replace the remotevents with alternative networking module such like BridgeNet2, Red, Red2, FastNet2, etc. they are open-sourced
Definitely checking those out later. Looks like a very good potential modification to my game.
Side note: What does your unofficial v1.2 change? I was curious since it looks like much of the same thing.
Hey there. I was wondering if this module can be used across various scripts, such as being able to cancel a concurring tween that is happening in another script? Using variables such as _G is extremely frustrating, so if this is true then it would significantly help the workflow of cross-script tweens.
the tweening isnât working properly for me. all the tweens are instantaneous from the start to the end goal
what did you do to fix it? im having the same issue
Had the same issue, in the TweenServicePlus module at the very top under âSetup Instructionsâ, it says to require the module on the client side in a localscript.
So legit just
local TweenService = require(game.ReplicatedStorage.TweenServicePlus)
on any script on the client.
**Also make sure you use his module or youâll get some errors like I did **