I must admit, Live2D SDKs is difficult to learn. No API documentation and so.
Now I'm stuck on very basic things, Playing Animation.
MotionController.PlayAnimation() consist of:
void CubismMotionController.PlayAnimation(AnimationClip clip, [int layerIndex = 0], [int priority = 2], [bool isLoop = true], [float speed = 1])
with **priority** parameter (default: **2**):
CubismMotionPriority.cs
public class CubismMotionPriority
{
public const int PriorityNone = 0;
public const int PriorityIdle = 1;
public const int PriorityNormal = 2;
public const int PriorityForce = 3;
}
So, I play a character login/intro motion:
CharacterViewer.csmotionController.PlayAnimation(animations["login"], isLoop: false);
From the documentation, you need to subscribe to `AnimationEndHandler` event to make the next motion transition faded.
I follow the tutorial and make a method to subscribe to:
CharacterViewer.cs
//...
motionController.AnimationEndHandler += OnLoginComplete;
//...
//...
private void OnLoginComplete(float instanceId)
{
// This will play `idle` animation continously
motionController.PlayAnimation(animations["idle"], priority: 1);
motionController.AnimationEndHandler -= OnLoginComplete;
}
//...
Above codes work as expected. From `login` -> `idle` motion with fade transition.
On another hand, i perform a Raycast to the character. When I click a part with `Touch` component, it will play a specified animation. (NB: There's 3 parts with `Touch` component)
Touch.cs
//...
if (hitCount > 0 && results[0].Drawable.TryGetComponent(out Touch _)) // If the part found...
{
// This will play Touch animation with janky transition, no fade
CharacterViewer.MotionController.PlayAnimation(
CharacterViewer.Animations["touch_body"],
priority: 3,
isLoop: false
);
CharacterViewer.MotionController.AnimationEndHandler += OnTouchComplete; // It doesn't subscribed to the handler whatsoever :v
}
//...
//...
void OnTouchComplete(float instanceId)
{
Debug.Log("Back to Idle...");
CharacterViewer.MotionController.PlayAnimation(
CharacterViewer.Animations["idle"],
priority: 3,
isLoop: true
);
CharacterViewer.MotionController.AnimationEndHandler -= OnTouchComplete;
}
//...
The `Touch` animation will play because I used `Force` priority. Using `Normal` priority also work, but it's give me a log message: `can't start motion.`. There's no fade transition because the Idle animation is looped (AnimationEndHandler practically will not be called because the animation will not ended. CMIIW).
The method (`OnTouchComplete()`) I registered to the event isn't called. So, the character can't go back to the Idle animation. According to the docs:
It's clear that I don't play `Touch` animation looped. So why, `OnTouchComplete()` isn't called?
Alternatively, if I wait until the current animation is done, also not working.
Touch.cs
//...
CharacterViewer.MotionController.PlayAnimation(
CharacterViewer.Animations["touch_body"],
priority: 2,
isLoop: false
);
await UniTask.WaitUntil( // Equivalent to Task.WaitUntil in System.Threading.Tasks
() => !CharacterViewer.MotionController.IsPlayingAnimation()
);
OnTouchComplete(0); // Execute with dummy argument
//...
Bruh, it's drives me nuts... If anyone can help me is much appreciated
Answers
Thank you for using our products.
We apologize for any inconvenience caused by the lack of documentation.
We will be enhancing the documentation in the future.
Could you tell us what kind of behavior you would like to achieve?
Best regards.
I want to control the motion fade from one to another either manually or already defined in Fade Motion Data without depending on MotionController.AnimationEndHandler.
Also a better way to sequence the motion and overriding the current motion without confused about the motion priority.
CubismMotionController.PlayAnimation() can be called at any time.
AnimationEndHandler is only a callback delegate that is called when the animation has finished playing.
If there is no processing to be performed when the animation ends, there is no need to use it.
For example, the following is a snippet of a case where a motion is played when the screen is clicked.
The third argument of CubismMotionController.PlayAnimation(), priority, is the priority of the animation to be played.
If it is less than or equal to the priority of the currently playing animation, it will not be played.
https://github.com/Live2D/CubismUnityComponents/blob/304d0d0c827b7bcb86c6f5d9b0a6398eaccda556/Assets/Live2D/Cubism/Framework/Motion/CubismMotionPriority.cs#L14
In the sample, when the torso is clicked while the Idle animation is playing, the animation is interrupted to play, and during the playback of that animation, it is not interrupted even if the torso is clicked again.
However, if PriorityForce is passed, the animation is forced to play regardless of the current priority.
Use this method if you want to handle animations without considering priority.
https://github.com/Live2D/CubismUnityComponents/blob/304d0d0c827b7bcb86c6f5d9b0a6398eaccda556/Assets/Live2D/Cubism/Framework/Motion/CubismMotionController.cs#L107
Best regards.
The only way you can do is subscribe to
MotionController.AnimationEndHandler
, CMIIW.Fades are handled automatically by CubismFadeController when motion is played.
When using CubismMotionController, CubismFadeController._fadeStates is a reference to CubismMotionLayer.
From there, information about the motion played by CubismMotionController.PlayAnimation() can be retrieved, and the fade is calculated.
However, if each component is added at runtime, for example, there may be no reference to CubismMotionLayer, and calling CubismMotionController.PlayAnimation() will not start the fade process.
In that case, call CubismFadeController.Refresh() after adding each component.
Best regards.