Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local runService = game:GetService("RunService")
- local heartbeat = runService.Heartbeat
- local ikModule = require(script:WaitForChild("IK"))
- local ragdollModule = require(script:WaitForChild("Ragdoll"))
- local abs = math.abs
- local min = math.min
- local max = math.max
- local clamp = math.clamp
- local cf = CFrame.new
- local cfa = CFrame.Angles
- local v3n = Vector3.new
- local table_insert = table.insert
- local table_remove = table.remove
- local profilebegin = debug.profilebegin
- local profileend = debug.profileend
- local originV3 = v3n(0, 0, 0)
- local originCF = cf(0, 0, 0)
- local animator = {}
- local animators = {}
- local animatorMeta = {
- __index = animator,
- __tostring = function(self)
- if self.Destroyed then
- return "[Destroyed Animator]"
- else
- return "[Animator | "..tostring(self.Character).."]"
- end
- end,
- }
- local animatorParts = {
- -- Root part
- "HumanoidRootPart",
- -- Head & torso parts
- "Head",
- "UpperTorso",
- "LowerTorso",
- -- Arm parts
- "LeftUpperArm",
- "LeftLowerArm",
- "Left_Hand",
- "RightUpperArm",
- "RightLowerArm",
- "Right_Hand",
- -- Leg parts
- "LeftUpperLeg",
- "LeftLowerLeg",
- "Left_Foot",
- "RightUpperLeg",
- "RightLowerLeg",
- "Right_Foot",
- }
- local destroyAnimator
- local createAnimator
- local isAnimator
- do -- Animator class
- do -- Animation object functions
- function animator:AddAnimation(animationType, animationName, animationData)
- assert(type(animationType) == "string", "animationType must be a string")
- assert(type(animationName) == "string", "animationName must be a string")
- assert(type(animationData) == "table", "animationData must be a table")
- -- Get the animation id from the data table
- local contentId = "" do
- if type(animationData.Id) == "number" then
- contentId = "rbxassetid://" .. animationData.Id
- elseif type(animationData.Id) == "string" then
- contentId = animationData.Id
- else
- error("animationData.Id must be a number or string")
- end
- end
- do -- Add the animation track to the list of animation tracks
- -- Create a new animation track
- local controller = self.Controller
- local newAnimation = Instance.new("Animation")
- newAnimation.AnimationId = contentId
- newAnimation.Parent = controller
- local animationTrack = controller:LoadAnimation(newAnimation)
- -- Add it into the table
- local animationTracks = self.AnimationTracks
- local typeAnimations = animationTracks
- if type(typeAnimations) ~= "table" then
- typeAnimations = {}
- animationTracks[animationType] = typeAnimations
- end
- typeAnimations[animationName] = animationTrack
- end
- do -- Add the animation data to the list of animation datas
- local animationDatas = self.AnimationDatas
- local typeAnimations = animationDatas
- if type(typeAnimations) ~= "table" then
- typeAnimations = {}
- animationDatas[animationType] = typeAnimations
- end
- typeAnimations[animationName] = animationData
- end
- end
- function animator:AddAnimations(animationTypes)
- assert(type(animationTypes) == "table", "animationTypes must be a table")
- for animationType, animationNames in pairs(animationTypes) do
- for animationName, animationData in pairs(animationNames) do
- self:AddAnimation(animationType, animationName, animationData)
- end
- end
- end
- function animator:StopAnimation(animationType, animationName, ...)
- assert(type(animationType) == "string", "animationType must be a string")
- assert(type(animationName) == "string", "animationName must be a string")
- local animationTracks = self.AnimationTracks[animationType]
- local animationTrack = animationTracks and animationTracks[animationName]
- assert(animationTrack, "animationTrack is missing for animation: "..animationType..", "..animationName)
- local animationDatas = self.AnimationDatas[animationType]
- local animationData = animationDatas and animationDatas[animationName]
- assert(animationData, "animationData is missing for animation: "..animationType..", "..animationName)
- if animationTrack.IsPlaying then
- animationTrack:Stop(...)
- end
- end
- function animator:StopAnimations(animationType, ...)
- assert(type(animationType) == "string", "animationType must be a string")
- local playingAnimations = self.PlayingAnimationTracks[animationType]
- if playingAnimations and next(playingAnimations) then
- for animationName, animationTrack in pairs(playingAnimations) do
- if animationTrack.IsPlaying then
- animationTrack:Stop(...)
- end
- end
- end
- end
- function animator:PlayAnimation(animationType, animationName, stopPrevious, ...)
- if stopPrevious == nil then stopPrevious = false end
- assert(type(animationType) == "string", "animationType must be a string")
- assert(type(animationName) == "string", "animationName must be a string")
- assert(type(stopPrevious) == "boolean", "stopPrevious must be a boolean")
- local animationTracks = self.AnimationTracks[animationType]
- local animationTrack = animationTracks and animationTracks[animationName]
- assert(animationTrack, "animationTrack is missing for animation: "..animationType..", "..animationName)
- local animationDatas = self.AnimationDatas[animationType]
- local animationData = animationDatas and animationDatas[animationName]
- assert(animationData, "animationData is missing for animation: "..animationType..", "..animationName)
- if stopPrevious then
- self:StopAnimations(animationType, ...)
- end
- if animationTrack then
- animationTrack:Play(...)
- end
- end
- end
- do -- Update functions
- local function updateState(animator, delta)
- local rootPart = animator.RootPart
- local thisCFrame = rootPart.CFrame
- local lastCFrame = animator.CFrame
- local inverseDelta = 1 / delta
- local objectDiff = lastCFrame:toObjectSpace(thisCFrame).p
- local worldDiff = thisCFrame.p - lastCFrame.p
- animator.ObjectDirection = objectDiff.Unit
- animator.ObjectVelocity = objectDiff * inverseDelta
- animator.WorldDirection = worldDiff.Unit
- animator.WorldVelocity = worldDiff * inverseDelta
- animator.CFrame = thisCFrame
- end
- local function updateAnimations(animator, delta)
- end
- local function updateIK(animator, delta)
- profilebegin("UpdateIKLegs")
- profileend()
- profilebegin("UpdateIKArms")
- profileend()
- profilebegin("UpdateIKLook")
- profileend()
- end
- function animator:Update()
- if self.Destroyed then
- return
- end
- local thisUpdate = tick()
- local lastUpdate = self.LastUpdate or 0
- local delta = clamp(thisUpdate - lastUpdate, 0, 1)
- self.LastUpdate = thisUpdate
- profilebegin("UpdateState")
- updateState(self, delta)
- profileend()
- profilebegin("UpdateAnimations")
- updateAnimations(self, delta)
- profileend()
- profilebegin("UpdateIK")
- updateIK(self, delta)
- profileend()
- end
- end
- do -- Auto update functions
- local autoUpdatedAnimators = {}
- function animator:AddToAutoUpdate()
- if not self.AddedToAutoUpdate then
- self.AddedToAutoUpdate = true
- table_insert(autoUpdatedAnimators, self)
- end
- end
- function animator:RemoveFromAutoUpdate()
- if self.AddedToAutoUpdate then
- self.AddedToAutoUpdate = nil
- for i, animator in pairs(autoUpdatedAnimators) do
- if animator == self then
- local num = #autoUpdatedAnimators
- autoUpdatedAnimators[i] = autoUpdatedAnimators[num]
- autoUpdatedAnimators[num] = nil
- break
- end
- end
- end
- end
- heartbeat:Connect(function()
- profilebegin("UpdateAnimators")
- for _, animator in pairs(autoUpdatedAnimators) do
- animator:Update()
- end
- profileend()
- end)
- end
- do -- Other functions
- function animator:Destroy()
- if not self.Destroyed then
- self.Destroyed = true
- -- Remove animator from auto updating
- self:RemoveFromAutoUpdate()
- -- Remove character from animators hash
- local character = self.Character
- if animators[character] then
- animators[character] = nil
- end
- -- Disconnect any connections
- local connections = self.Connections
- if type(connections) == "table" then
- for _, connection in pairs(connections) do
- if connection.Connected then
- connection:Disconnect()
- end
- end
- end
- -- Clean up memory
- self.Connections = nil
- self.Character = nil
- self.RootPart = nil
- self.CFrame = nil
- self.ObjectDirection = nil
- self.ObjectVelocity = nil
- self.WorldDirection = nil
- self.WorldVelocity = nil
- self.PlayingAnimationTracks = nil
- self.AnimationTracks = nil
- self.AnimationDatas = nil
- end
- end
- function animator:Connect()
- if not self.Connected then
- self.Connected = true
- -- Variables used for connecting
- local character = self.Character
- local rootPart = self.RootPart
- -- Gather joint & attachment data
- local joints = {}
- local jointC0s = {}
- local jointC1s = {}
- local jointParts = {}
- local jointPart0s = {}
- local jointPart1s = {}
- local attachments = {}
- for _, descendant in pairs(character:GetDescendants()) do
- if descendant:IsA("Motor") then
- local jointName = descendant.Name
- local part0 = descendant.Part0
- local part1 = descendant.Part1
- joints[jointName] = descendant
- jointC0s[jointName] = descendant.C0
- jointC1s[jointName] = descendant.C1
- jointPart0s[jointName] = part0
- jointPart1s[jointName] = part1
- if not jointParts[part0.Name] then
- jointParts[part0.Name] = part0
- end
- if not jointParts[part1.Name] then
- jointParts[part1.Name] = part1
- end
- elseif descendant:IsA("Attachment") then
- local attachmentName = descendant.Name
- if not attachmentName:find("Rig") then
- attachments[attachmentName] = descendant
- end
- end
- end
- self.Joints = joints
- self.JointC0s = jointC0s
- self.JointC1s = jointC1s
- self.JointParts = jointParts
- self.JointPart0s = jointPart0s
- self.JointPart1s = jointPart1s
- self.Attachments = attachments
- -- Automatically destroy animator if character or root part is destroyed
- local connections = {}
- local function checkAutoDestroy()
- if character.Parent == nil
- or rootPart.Parent == nil then
- self:Destroy()
- end
- end
- table_insert(connections, character.AncestryChanged:Connect(checkAutoDestroy))
- table_insert(connections, character:GetPropertyChangedSignal("Parent"):Connect(checkAutoDestroy))
- table_insert(connections, rootPart:GetPropertyChangedSignal("Parent"):Connect(checkAutoDestroy))
- self.Connections = connections
- -- Set any current animation controller, or create one if one wasn't found
- local controller
- local animationController = character:FindFirstChildOfClass("AnimationController")
- local humanoid = character:FindFirstChildOfClass("Humanoid")
- if humanoid then
- controller = humanoid
- elseif animationController then
- controller = animationController
- else
- controller = Instance.new("AnimationController")
- controller.Parent = character
- end
- self.Controller = controller
- -- Add the animator to the animators hash
- animators[character] = self
- end
- end
- end
- do -- Create / destroy / typecheck functions (public)
- function destroyAnimator(character)
- assert(character ~= nil, "character must not be nil")
- local animator = animators[character]
- if animator and not animator.Destroyed then
- animator:Destroy()
- return true
- end
- return false
- end
- function createAnimator(character)
- assert(character ~= nil, "character must not be nil")
- assert(typeof(character) == "Instance", "character must be an Instance")
- assert(character:IsA("Model"), "character must be a Model")
- assert(character.PrimaryPart, "character must have a PrimaryPart")
- if animators[character] then
- return animators[character]
- end
- local newAnimator = setmetatable({}, animatorMeta)
- newAnimator.RootPart = character.PrimaryPart
- newAnimator.Character = character
- newAnimator.CFrame = originCF
- newAnimator.ObjectDirection = originV3
- newAnimator.ObjectVelocity = originV3
- newAnimator.WorldDirection = originV3
- newAnimator.WorldVelocity = originV3
- newAnimator.PlayingAnimationTracks = {}
- newAnimator.AnimationTracks = {}
- newAnimator.AnimationDatas = {}
- newAnimator:Connect()
- return newAnimator
- end
- function isAnimator(object)
- if type(object) == "table" then
- if getmetatable(object) == animatorMeta then
- return true
- end
- end
- return false
- end
- end
- end
- local module = setmetatable({}, {
- __call = function(self, ...)
- return createAnimator(...)
- end
- })
- function module:Destroy(...)
- return destroyAnimator(...)
- end
- function module:Create(...)
- return createAnimator(...)
- end
- function module:IsAnimator(...)
- return isAnimator(...)
- end
- return module
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement