How To Use A Skinned Mesh For A Custom Character (And Animate It)

Hello there, my friends of DevForum.

Today I would like to share something that I don’t think is even close to easy enough to find tutorials on.
By the end of this tutorial, you should know how to:
1: Import skinned meshes for use as custom characters
2: Animate them
3: Use them in-game

Without wasting any more time, let’s start.

STEP 1: IMPORTING THE CHARACTER
To use Avatar Importer, go into the plugins tab and select “Avatar Importer”
tutorial 6
From there, select “Custom” and select the 3d model of your character. NOTE: For this method, your character MUST be a .fbx file.

Now that you have the character model in the workspace, follow these steps:

  1. Rename the “FBXImportGeneric” group to “StarterCharacter”.
  2. You can delete the “InitialPoses” folder.
  3. Replace the AnimationController with a Humanoid.
  4. Add a part called “HumanoidRootPart”. Turn CanCollide off and move it around the character. Also turn the transparency to 1.

Now your explorer should look like this: (I changed the name of the cube later as well)
tutorial 9

STEP 2: CREATING MOTOR6D’S
First, you’ll need to install a plugin that can do this. I personally like to use this plugin, but there are others you can use, like RigEdit.
First, select the HumanoidRootPart.
Then, select your character.
Create a Motor6d between them. To do this with Constraint Editor, click “new Motor6d”
This should create a Motor6d parented to the HumanoidRootPart.
tutorial 10

STEP 3: ANIMATING THE CHARACTER
Now that we have the Motor6d set up, we can animate our character.
In the plugins tab, select Animation Editor.
tutorial 11
The animation editor will open, if you’ve done everything correctly, click on the character, and then create animation.
It should look something like this if you have your bones done correctly:
tutorial 12
This isn’t a tutorial on how to animate, so I’ll fast-forward to after I’ve made the animation.
Publish the animation, and keep the ID somewhere for later.

STEP 4: APPLYING THE ANIMATIONS
In this tutorial, we’ll use a custom animate script because I couldn’t get Roblox’s default one to work.
Create a LocalScript called “Animate” inside of StarterCharacterScripts, and put this script inside of it:

repeat task.wait() until script:FindFirstChildWhichIsA("Animation")

local idleAnim = script.Parent.Humanoid:LoadAnimation(script.Idle)
idleAnim.Looped = true
idleAnim.Priority = Enum.AnimationPriority.Idle
local runAnim = script.Parent.Humanoid:LoadAnimation(script.Run)
runAnim.Looped = true
runAnim.Priority = Enum.AnimationPriority.Movement
local jumpAnim = script.Parent.Humanoid:LoadAnimation(script.Jump)
jumpAnim.Looped = true
jumpAnim.Priority = Enum.AnimationPriority.Movement
local climbAnim = script.Parent.Humanoid:LoadAnimation(script.Climb)
climbAnim.Looped = true
climbAnim.Priority = Enum.AnimationPriority.Movement
local fallAnim = script.Parent.Humanoid:LoadAnimation(script.Fall)
fallAnim.Looped = true
fallAnim.Priority = Enum.AnimationPriority.Movement
local swimAnim = script.Parent.Humanoid:LoadAnimation(script.Swim)
swimAnim.Looped = true
swimAnim.Priority = Enum.AnimationPriority.Movement
local sitAnim = script.Parent.Humanoid:LoadAnimation(script.Sit)
sitAnim.Looped = true
sitAnim.Priority = Enum.AnimationPriority.Movement

game["Run Service"].Heartbeat:Connect(function()
	if script.Parent.HumanoidRootPart.Velocity.Magnitude > 0.1 then
		if script.Parent.Humanoid:GetState() == Enum.HumanoidStateType.Dead then
			for _, i in script.Parent.Humanoid:GetPlayingAnimationTracks() do
				i:Stop()
			end
		elseif script.Parent.Humanoid:GetState() == Enum.HumanoidStateType.Seated then
			if not sitAnim.IsPlaying then
				sitAnim:Play()
			end
		elseif script.Parent.Humanoid:GetState() == Enum.HumanoidStateType.Jumping then
			if not jumpAnim.IsPlaying then
				jumpAnim:Play()
			end
		elseif script.Parent.Humanoid:GetState() == Enum.HumanoidStateType.Climbing then
			if not climbAnim.IsPlaying then
				climbAnim:Play()
			end
		elseif script.Parent.Humanoid:GetState() == Enum.HumanoidStateType.Freefall then
			if not fallAnim.IsPlaying then
				fallAnim:Play()
			end
		elseif script.Parent.Humanoid:GetState() == Enum.HumanoidStateType.Swimming then
			if not swimAnim.IsPlaying then
				swimAnim:Play()
			end
		elseif script.Parent.Humanoid:GetState() == Enum.HumanoidStateType.Running then
			if not runAnim.IsPlaying then
				runAnim:Play()
			end
		end
	else
		for _, i in script.Parent.Humanoid:GetPlayingAnimationTracks() do
			if i.Name ~= "Idle" then
				i:Stop()
			end
		end
		if not idleAnim.IsPlaying then
			idleAnim:Play()
		end
	end
end)

If you don’t have any of those animations, comment out anything that you don’t use.
It may also be better to switch the position of the elseif’s.
Under the LocalScript, add 7 animations and name them like this:
tutorial 18
Put the ID of each animation you have into the animations accordingly.
And lastly, place your grouped character into StarterPlayer and everything should work!

Thank you for taking the time to read this tutorial. If you have any questions or critiques, feel free to leave a comment!
If something doesn’t work for you, also leave a comment. You can DM me about it too!

61 Likes

https://i.gyazo.com/70b249ec047984b9994bb1976cc8c7f5.mp4

I’m not using it for a StarterCharacter, but you did manage to fix my Playing Animation problem.
However, do you have any clue why it just… leaves? I didn’t script it to move or anything.

2 Likes

Nevermind, I fixed it. I had to adjust the HipHeight of the Humanoid.

1 Like

i have a problem with sizing down my chatacter, it just squashes it and ‘rips’ the mesh when i resize it because it takes up the whole map, how do i fix this?

Hi there, what do you mean by squashes and rips? Were you holding shift while resizing it?

1 Like

Assuming you’re using blender, when you export as a FBX make sure you turn the scale down to 0.01 and scale your rig in blender according to the roblox size you need.

4 Likes

Hello, this is a great tutorial and it cleared up most of the problems i was having with animations and such, although there is one problem - my custom character isnt playing animations. Do all animations need to be specified for it to work at all? Right now the only animation ive properly done is the walking, but it doesnt seem to work

heres how the explorer panel looks, is this correct?

4 Likes

Did you delete the RootPart? (Not the humanoidrootpart), if so, you deleted all of his bones.

oh i redid the model to use R15 lol

Oh, well in case you are still stuck with that problem the solution is this:

since you have a skinned mesh with multiple meshes, you have to make a new humanoid root part and place the bones inside it, and join or just attach new motor6ds from the humanoid root part to the meshes.

To properly reparent the bones, you have to copy their World CFrames (position and rotation) so when you reparent them you can put them on the location and rotation they belong.

Great guide! Thank you. But I have one problem with idle animation. It is activated every millisecond. It is activated every millisecond, which is very strange. It would seem that it’s okay, but for me it’s a problem.

Alright. I solved problem.

for _, i in script.Parent.Humanoid:GetPlayingAnimationTracks() do
			if i.Name ~= "Idle" then
				i:Stop()
			end
		end
end

If name of idle animation not “Idle”, you must to change the checked value for the animation name.

1 Like