Run it
Copy the script and paste it into your executor.
config = {
stiffness = ,
damping = ,
linearamp = .(, , ),
angularamp = .(, , ),
wagenabled = ,
wagdrop = ,
wagsway = ,
wagroll = ,
wagblendin = ,
wagblendout = ,
wagspeed = ,
timescale = ,
windenabled = ,
gravityenabled = ,
customwind = .zero,
customforce = .zero,
maxdistance = ,
maxtails = ,
movethreshold = ,
includednames = {} :: { },
}
run = :()
players = :()
player = players.LocalPlayer
char = player.Character player.CharacterAdded:()
cam = .CurrentCamera
sin = .sin
cos = .cos
min = .min
max = .max
insert = .insert
find = .find
remove = .remove
sort = .sort
timestep = /
invtimestep =
tails = {} :: { {
root: BasePart,
prevroot: ,
prevdelta: ,
weld: Weld,
origcf: ,
prevweld: ,
curweld: ,
angle: ,
angvel: ,
wagblend: number,
seed: number,
pivot: ,
invpivot: ,
char: Model,
invscale: number,
dist: number,
rendering: boolean,
} }
tracked = {} :: { [Model]: boolean }
wagtime =
windtime =
accum =
winddir = .zero
windalpha =
windalpha04 =
windalpha006 =
haswind =
gravforce = .zero
linearforce = .zero
haslinear =
(pos: , vel: , stiff: number, damp: number): (, )
accel = stiff * pos + damp * vel
newvel = vel - accel * timestep
pos + newvel * timestep, newvel
(v: ):
.(v.X, v.Y, v.Z)
(a: number, b: number, t: number): number
a + (b - a) * t
()
windforce = config.windenabled (.GlobalWind + config.customwind) config.customwind
windforce.Magnitude >
winddir = windforce.Unit
winddir = .zero
windalpha = windforce.Magnitude *
windalpha04 = windalpha *
windalpha006 = windalpha * windalpha *
haswind = windalpha >
()
linearforce = config.gravityenabled (gravforce + config.customforce) config.customforce
haslinear = linearforce.Magnitude >
()
gravforce = .(, (( - .Gravity) * , -))
()
()
()
:():(updatewind)
:():(updategravity)
(tail):
blend = tail.wagblend
tail.angvel.Magnitude >
blend = blend < blend * ( - config.wagblendout)
blend = blend > (blend, , config.wagblendin)
tail.wagblend = blend
blend <=
.zero
wt = wagtime + tail.seed
ft = wt + (wt) * (wt * ) *
.(
((ft * ) + ) * config.wagdrop * blend,
(ft) * config.wagsway * blend,
(ft) * -config.wagroll * blend
)
(tail, alpha: number)
scale = tail.invscale * tail.char:()
cf = tail.prevweld:(tail.curweld, alpha)
config.wagenabled
cf = cf * (cf:(tail.pivot * ((tail)) * tail.invpivot)):()
tail.weld.C0 = cf.Rotation + cf.Position * scale
(tail)
tail.prevroot = tail.root.CFrame
tail.prevdelta = .identity
tail.prevweld = tail.origcf
tail.curweld = tail.origcf
tail.angle = .zero
tail.angvel = .zero
tail.wagblend =
tail.rendering =
(tail)
tail.rendering =
scale = tail.invscale * tail.char:()
tail.weld.C0 = tail.origcf.Rotation + tail.origcf.Position * scale
(tail, doweld: boolean, alpha: number)
rootcf = tail.root.CFrame
delta = tail.prevroot:(rootcf)
delta.Position.Magnitude > config.movethreshold
delta = delta.Rotation
accel = tail.prevdelta:(delta)
force = accel.Position * config.linearamp
haswind
s4 = (windtime * )
unstable = (windtime * * s4) * windalpha04
stable = (windtime * ) * s4
windforce = winddir * windalpha006 * ( + (stable + unstable) * )
force -= rootcf:(windforce)
haslinear
force -= rootcf:(linearforce)
pivot = tail.pivot
disp = pivot.Position - tail.prevweld.Position
torque = disp:(force)
x, y, z = accel:()
angaccel = .(x, y, z) * config.angularamp
newangle, newangvel = (tail.angle, tail.angvel, config.stiffness, config.damping)
tail.angle = newangle
scale = tail.invscale * tail.char:()
tail.angvel = newangvel + torque / scale - angaccel
newcur = pivot * (newangle) * tail.invpivot
newprev = tail.curweld
doweld
cf = newprev:(newcur, alpha)
config.wagenabled
cf = cf * (cf:(pivot * ((tail)) * tail.invpivot)):()
tail.weld.C0 = cf.Rotation + cf.Position * scale
tail.prevroot = rootcf
tail.prevdelta = delta
tail.prevweld = newprev
tail.curweld = newcur
(root: BasePart): ?
waistrig = root:()
waistrig waistrig:()
waistrig.CFrame * .(, , root.Size.Z * )
waistback = root:()
waistback waistback:()
waistback.CFrame * .(, , )
(name: ): number
seed =
i = , #name
seed += .(name, i) ^
seed
(tail)
idx = (tails, tail)
idx
(tails, idx)
(characterModel: Model, weld: Weld, tailpart: BasePart, custompivot: | BasePart?)
flipped =
root = weld.Part0
root
()
root == tailpart
root = weld.Part1
root
()
flipped =
pivot: ?
custompivot
pivot = (custompivot) == custompivot custompivot.CFrame
pivot = (root)
pivot
()
origcf:
flipped
origcf = weld.C1 * weld.C0:()
weld.Part0, weld.Part1 = root, tailpart
origcf = weld.C0 * weld.C1:()
weld.C0 = origcf
weld.C1 = .identity
localpivot = origcf:(.(pivot.Position))
seed = (characterModel.Name)
plr = players:(characterModel)
plr
seed += plr.UserId
tail = {
root = root,
prevroot = root.CFrame,
prevdelta = .identity,
weld = weld,
origcf = origcf,
prevweld = origcf,
curweld = origcf,
angle = .zero,
angvel = .zero,
wagblend = ,
seed = seed,
pivot = origcf * localpivot,
invpivot = localpivot:(),
char = characterModel,
invscale = / characterModel:(),
dist = ,
rendering = ,
}
(tails, tail)
weld.AncestryChanged:(()
weld.Parent
(tail)
)
(characterModel: Model, accessory: Accessory)
handle = accessory:()
handle handle:()
accessory.ChildAdded:(()
(characterModel, accessory)
)
weld = handle:()
weld weld:()
(characterModel, weld, handle, )
(accessory: Accessory): boolean
acctype = accessory.AccessoryType
accname = accessory.Name:()
acctype == .AccessoryType.Back acctype == .AccessoryType.Waist
accname:(, , )
i = , #config.includednames
accessory.Name == config.includednames[i]
(characterModel: Model)
tracked[characterModel]
tracked[characterModel] =
( .. characterModel.Name)
characterModel.ChildAdded:((child)
child:() (child)
( .. child.Name)
(characterModel, child)
)
count =
_, child characterModel:()
child:() (child)
( .. child.Name)
(characterModel, child)
count +=
count >
( .. count .. )
()
characterModel:():(()
characterModel.Parent
tracked[characterModel] =
)
(char)
player.CharacterAdded:((newchar)
char = newchar
tails = {}
tracked = {}
wagtime =
windtime =
accum =
()
.()
(newchar)
)
(a, b): boolean
a.dist < b.dist
run.PreRender:((dt: number)
dt = (dt, ) * config.timescale
wagtime += dt * config.wagspeed
windtime += dt
accum += dt
steps =
accum >= timestep
accum -= timestep
steps +=
campos = cam.CFrame.Position
rendering = {}
_, tail tails
dist = (tail.root.CFrame.Position - campos).Magnitude
dist <= config.maxdistance
tail.dist = dist
(rendering, tail)
tail.rendering
(tail)
(rendering, sortbydist)
interp = steps ==
alpha = accum * invtimestep
updated =
_, tail rendering
updated >= config.maxtails
tail.rendering
(tail)
interp
tail.rendering
(tail, alpha)
tail.rendering
(tail)
i = , steps
(tail, i == steps, alpha)
updated +=
)
( .. #tails .. )works with any ugc with the name "tail" in it based on tuxphysicstails by @r3dtuxedo

Universal