Procedural Animation: What it is and how to do it

I’ve been having trouble with this too, and this is what I’m trying. You can just create your keyframes in the animation editor, then save the C1 value of the M6D that’s being changed with the command bar. The animation editor doesn’t change the C0 value at all, only the C1 value. Then you can just tween the player’s C1 value to that. It works perfectly. It’s a little tedious, but I don’t know of any plugins to help with this.


This is the code I pasted into the command bar to test it.

local MainC1 = workspace.TestDummy.Torso["Left Shoulder"].C1 
game:GetService("TweenService"):Create(workspace.Dummy.Torso["Left Shoulder"],TweenInfo.new(1),{ C1 = MainC1 }):Play()
4 Likes

Can you make a tutorial about springs in the character animations(not fps in any case), i mean’t as example if the player fell while he is falling the spring would make the camera go up/once he touchs the ground it bounces back. nice tutorial

Did you do a relative difference or just saved the last C1 values…? Has everything worked out for you?

1 Like

The tediousness outweighs its usefulness imo. This is beyond tedious. No wonder Black Magic took forever to come out with new classes and still to this day take ages. Not only do you have to play test but just in making the animation you have to play test the damn cframes. Finding the right cframes is probably a headache in itself unless im missing a simple solution.

i can imagine one way of making a virtual frame thing in studio where you line up 12 dummies = 12 frames. Run a script to get the cframe of every part for each dummy. And that’s if you’re animating at 12 frames per second. I animate at 60 frames per second. I just kept it at default ahahah.

then do the tween stuff.

THAT WOULD TAKE FOREVER. Like i just made an animation with 30 frames at 60 fps.

especially holy cowzers imagine doing this with r15 lmfaooo.

OMG imagine the LIKE SP 8. Long animations like that and doing procedural animation. I’d oof myself ahahah. Much props to any scripters still scripting animations. I, no way in heck would i submit myself to that torment unless i really had too.

i understand doing it for like fun, like the script battle game. But for a full fighting game, much props bc i couldn’t submit myself to that.

EDIT: All the info was blowing my mind i overthought it a bit. It’s not terrible but like i still l wouldn’t do it.

6 Likes

For this reason, I’ve developed my own module to convert Roblox KeyFrameSequences into playable procedural animations. For some reason, it’s a very sought-after thing and is surprisingly easier than it looks. If you want to REALLY make a game out of procedural animations, I recommend putting time into an elaborate Animation converter or maker. Or… give it some time and I might release my Module!

6 Likes

Do you have any useful references or links for these 3? I kinda wanna learn this.

For animation terminology and theory, the New Frame Plus and Video Game Animation Study channels on Youtube are a good resource. Obligatory “read The Animator’s Survival Kit” as well.

CFrame math stuff can be found on the Roblox API site. [Article 1] [Article 2]

2 Likes

This is a typo. It is called “linear interpolation”, not “interpretation”.

P.S. nice tutorial. I will probably implement procedural animations in my games someday.

nice concept, but please add photos/videos as I do not understand a word you are saying. I am more of a visual learner and I believe others are too.

Having the same issue, the hand in my example is appearing behind the player before Tweening to the intended target.

game.Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(Character)
		SwordProceduralM11(player, Character)
	end)
end)

    function SwordProceduralM11(Player, Character)
    	local CFrameValue = Instance.new("CFrameValue")
    	local SavedBaseCFrame = Character.Torso["Right Shoulder"].C0
    	
    	local Goal = {}
    	
    	CFrameValue.Changed:Connect(function()
    		Character.Torso["Right Shoulder"].C0 = CFrameValue.Value
    	end)
    	
    	local TweenSettings = TweenInfo.new(6, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0)
    	Goal.Value = SavedBaseCFrame * CFrame.Angles(math.rad(65), math.rad(70), math.rad(90))
    	local tween = TweenService:Create(CFrameValue, TweenSettings, Goal)
    	tween:Play()
    end

Any idea on how I’d remove this initial rogue keyframe?

If you do not like the linear look of Lerp, and you have understanding of math and math functions and other math stuff then you can use functions(math functions) to make the interpolation look cooler and different.
You can make some graphs of your targeted interpolation in desmos like so,


and apply it to Roblox like so:

for x = 0.00, 1.00, wait() do
    local y = 1/(1+math.exp(2.71828,(-10x+5)))--Sigmond Wave, e is approximately 2.71828
    startpart.CFrame = startcf:lerp(endcf, y)
    wait()
end

too lazy to see if that works :slight_smile:

you can use any function(math function) you can find or come up with and just apply it to Roblox :grinning:
Using this instead of Tweening might be slightly easier? and less glitchy and computer intensive.

7 Likes

Hello, apologies for late reply. If I understand right, you are essentially proposing using your own tweening functions. This is certainly an option, and not a bad one either! As you said, tweening the C0 of a Motor6D directly instead of a CFrameValue with an event connecting it to the target C0 might lead to slightly less resource utilization, and would also allow custom easings.
I will say, however, that using TweenService allows you to easily delay and reverse poses using the respective properties of TweenInfo. Roblox also has InterpolationThrottling (although it still annoys me that tweens themselves aren’t replicated and instead the results are!!!) which is pretty useful.

I’m late to this thread, but here’s a fun fact:
The default character animations from 2007-2012(?) used Procedural Animation to move a character.
Although, they manually changed the CFrame of the joints, which made the animations look quite choppy.

2 Likes

pls do.
i think it would really help, especially for people who don’t understand this concept

1 Like

Animating a ValueObject to harness TweenService for standard values is a not unheard of yet hacky solution.
For a comprehensive system, animations could generally be represented with one function that receives the time it’s been playing for. A main animation loop would run the function for whatever animation is selected to play (by string id, assuming you stored the animation functions in a table) and keep track of playing time for that function.
Linear would be implemented by default in these functions, (as most animations tend to be,) but could be augmented by TweenService:GetValue() or your own easing/wave functions. Thoughts?

I believe you’re looking for a way to better mix and transition between animations. The goal of this tutorial is to animate without relying on Roblox’s system.

TweenService:GetValue() would be a viable alternative to connecting a CFrame to a dummy CFrameValue, but you’d have to use Cframe:Lerp() and a while loop to animate the CFrame, and ultimately I felt it was easier to instead to just use a dummy CFrameValue.

sorry to bump up this topic, but i got no clue on making something similar to this

very late but i hope it stop people from suffer.I have no idea how you even could animate through C0 (i think its literally impossible) robloxanimator works is that it gets C1 and removes C1 cframe - Pose.Cframe and this result will be a new Motor6D.C1 i made a plugin and post about it
Motor6D before apllying animation will be considered as offset
post
plugin
For real procedural generation you would need to do crazy math
Edit: oh so basically just :Inverse() would make C0 work

If I remember correctly, my reasoning for using C0 was so that animations wouldn’t get completely overwritten by the animator script.

Also cool module.

2 Likes

i just found that C0 is just C1 in reverse kinda.I added way to use them into module.IF you wonder how it works just dont use :Inverse() and multiply to offset already