- Home /
Failed to inplement smooth increment of weapon trail between frames in Mecanim animation
Hello.I am currently scripting a weapon swipe trail tool.I find this one created by Evan Greenwood very helpful: https://www.assetstore.unity3d.com/en/#!/content/2458
I am tweaking and simplying it to meet my needs.Everything goes well until I use Mecanim Animator instead of Legacy Animation Component to play the character action.The trail does not appear smooth any more,It appears jagged updating only one step per frame.
I let the WeaponTrail script,which is the core drawing class,untouched.
What I modified are BladeMaster and AnimationController. As I switch to Mecanim and I do not need to add trajectory movement to the character,Evan's AnimationController has few usage for me. So I merge some of its functions into BladeMaster and simplify them into a single Manager like script:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
// This is the PocketRPG Blade Master class stripped down to just a bit of animation... written by Evan Greenwood
[AddComponentMenu("TATools/LSF TrailManager Mechanim")]
public class LSF_TrailManager_Mechanim : MonoBehaviour
{
public WeaponTrail MyWeaponSwipe;
public float TrailDuration = 1.3f;
public float FadeOutTime = 0.3f;
protected float DeltaTime = 0.033f;
private bool Bool_ShowTrail = false;
//protected float StepDuration = 0.1f;
protected int thinkState = 0;
protected float timeScale = 1; // This is here for personal time distortion... like freeze spells that slow enemies... (changing this affects the animation rate)
protected List<WeaponTrail> trails;
protected float m = 0;
protected Vector3 lastEulerAngles = Vector3.zero;
protected Vector3 lastPosition = Vector3.zero;
protected Vector3 eulerAngles = Vector3.zero;
protected Vector3 position = Vector3.zero;
private float tempT = 0;
public bool gatherDeltaTimeAutomatically = true; // ** You may want to set the deltaTime yourself for personal slow motion effects
protected float animationIncrement = 0.001f; // ** This sets the number of time the controller samples the animation for the weapon trails
protected void Awake()
{
transform.position = Vector3.zero;
trails = new List<WeaponTrail>();
lastPosition = transform.position;
lastEulerAngles = transform.eulerAngles;
}
protected void Start()
{
AddTrail(MyWeaponSwipe); // Adds the trails to the animationController which will run them
MyWeaponSwipe.SetTime(0.0f, 0, 1);
}
protected void Update()
{
DeltaTime = Mathf.Clamp(Time.deltaTime * timeScale, 0, 0.066f);
}
protected virtual void LateUpdate()
{
if (gatherDeltaTimeAutomatically)
{
DeltaTime = Mathf.Clamp(Time.deltaTime, 0, 0.066f);
}
RunAnimations();
}
public void StartTrailFromAnimationClip()
{
MyWeaponSwipe.StartTrail(0.5f, 0.4f); // Fades the trail in
Bool_ShowTrail = true;
Debug.Log("Start trail");
}
public void EndTrailFromAnimationClip()
{
//MyWeaponSwipe.FadeOut(FadeOutTime);
MyWeaponSwipe.ClearTrail();
Bool_ShowTrail = false;
Debug.Log("End trail");
}
public void AddTrail(WeaponTrail trail)
{
trails.Add(trail);
}
void RunAnimations()
{
if (DeltaTime > 0)
{eulerAngles = transform.eulerAngles;
position = transform.position;
while (tempT < DeltaTime)
{
// ** This loop runs slowly through the animation at very small increments,a bit expensive, but necessary to achieve smoother than framerate weapon trails
tempT += animationIncrement;
// ** steps forward by a small increment
m = tempT / DeltaTime;
transform.eulerAngles = new Vector3(Mathf.LerpAngle(lastEulerAngles.x, eulerAngles.x, m), Mathf.LerpAngle(lastEulerAngles.y, eulerAngles.y, m), Mathf.LerpAngle(lastEulerAngles.z, eulerAngles.z, m));
transform.position = Vector3.Lerp(lastPosition, position, m);
// ** Samples the animation at that moment
// ** Adds the information to the WeaponTrail
for (int j = 0; j < trails.Count; j++)
{
if (trails[j].time > 0)
{
trails[j].TrailIterate(Time.time - DeltaTime + tempT);
}
else
{
trails[j].ClearTrail();
}
}
}
// ** End of loop
tempT -= DeltaTime;
// ** Sets the position and rotation to what they were originally
transform.position = position;
transform.eulerAngles = eulerAngles;
lastPosition = position;
lastEulerAngles = eulerAngles;
// ** Finally creates the meshes for the WeaponTrails (one per frame)
for (int j = 0; j < trails.Count; j++)
{
if (trails[j].time > 0)
{
trails[j].UpdateTrail(Time.time, DeltaTime);
}
}
}
}
}
As I analyze the code.I think the part doing the trick to interpolate many smooth tiny "sections" into the mesh between two frames is as below:
while (tempT < DeltaTime)
{
// ** This loop runs slowly through the animation at very small increments,a bit expensive, but necessary to achieve smoother than framerate weapon trails
tempT += animationIncrement;
// ** steps forward by a small increment
m = tempT / DeltaTime;
transform.eulerAngles = new Vector3(Mathf.LerpAngle(lastEulerAngles.x, eulerAngles.x, m), Mathf.LerpAngle(lastEulerAngles.y, eulerAngles.y, m), Mathf.LerpAngle(lastEulerAngles.z, eulerAngles.z, m));
transform.position = Vector3.Lerp(lastPosition, position, m);
// ** Samples the animation at that moment
// ** Adds the information to the WeaponTrail
for (int j = 0; j < trails.Count; j++)
{
if (trails[j].time > 0)
{
trails[j].TrailIterate(Time.time - DeltaTime + tempT);
}
else
{
trails[j].ClearTrail();
}
}
}
It works totally well when I use a legacy Animation(though the exact codes with legacy animation are not the same as above).But when I change it to Mecanim Animator,and use Animation Events on the fbx to trigger the mesh section creating functions,it looks like the tiny smooth interpolated sections between two frame steps do not work anymore.
Trail in Legacy Animation:
Trail in Mecanim animation:
Please,how to solve this issue?
Update: After examing the codes again,I find that it is the FadeInCurrentState() function that really does the job :
void FadeInCurrentState (float aI)
{
// ** This is called every sample to ease in the current animation
currentStateTime += aI * currentState.speed;
currentState.time = currentStateTime;
}
void RunAnimations ()
{
if (DeltaTime > 0)
{
// ** The Animation Controller also turns the character
// ** This is very important for smooth trails... Otherwise each frame the trail will jump to wherever the new rotation causes it to be
// ** This could be taken further and make the animation controller move the character as well... You might want to do this if you have a character that moves very quickly
while (IncrementProgress < DeltaTime)
{
// ** This loop runs slowly through the animation at very small increments,a bit expensive, but necessary to achieve smoother than framerate weapon trails
IncrementProgress += animationIncrement;
// ** steps forward by a small increment
if (currentState != null)
FadeInCurrentState (animationIncrement);
// ** Samples the animation at that moment
GetComponent<Animation>().Sample ();
// ** Adds the information to the WeaponTrail
for (int j = 0; j < trails.Count; j++)
{
if (trails[j].time > 0) {
trails[j].TrailIterate (Time.time - DeltaTime + IncrementProgress);
} else {
trails[j].ClearTrail ();
}
}
}
// ** End of loop
IncrementProgress -= DeltaTime;
//
// ** Finally creates the meshes for the WeaponTrails (one per frame)
//
for (int j = 0; j < trails.Count; j++) {
if (trails[j].time > 0) {
trails[j].UpdateTrail (Time.time, DeltaTime);
}
}
}
}
So the question becomes "is there a counterpart of AnimationState.time in mecanim Animator,so that I can add fine control on the character animation in realtime?"Because as the codes show,it is using the AnimationState.time to make the animation forward many tiny steps between 2 updates.But I do not find similar methods in the Animation class.
Thank you guys.
Your answer
![](https://koobas.hobune.stream/wayback/20220612095443im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
coroutine question c# 2 Answers
Why is this coroutine only firing once? 3 Answers
How does WaitForSeconds actually work behind the scenes? 1 Answer
Get IEnumerator from name or convert MethodInfo to IEnumerator 2 Answers
Coroutine start and stop 2 Answers