private void FireAnimationEvents(MeshAnimation cAnim, float totalSpeed, bool finished) { if (cAnim.events.Length > 0 && eventReciever != null && previousEventFrame != currentFrame) { if (finished) { if (totalSpeed < 0) { // fire off animation events, including skipped frames for (int i = previousEventFrame; i >= 0; i++) { cAnim.FireEvents(eventReciever, i); } previousEventFrame = 0; } else { // fire off animation events, including skipped frames for (int i = previousEventFrame; i <= cAnim.totalFrames; i++) { cAnim.FireEvents(eventReciever, i); } previousEventFrame = -1; } return; } else { if (totalSpeed < 0) { // fire off animation events, including skipped frames for (int i = currentFrame; i > previousEventFrame; i--) { cAnim.FireEvents(eventReciever, i); } } else { // fire off animation events, including skipped frames for (int i = previousEventFrame + 1; i <= currentFrame; i++) { cAnim.FireEvents(eventReciever, i); } } previousEventFrame = currentFrame; } } }
/// <summary> /// Play an animation by index /// </summary> /// <param name="index">Index of the animation</param> public void Play(int index) { if (index < 0 || animations.Length <= index || currentAnimIndex == index) { return; } if (queuedAnims != null) { queuedAnims.Clear(); } currentAnimIndex = index; currentAnimation = animations[currentAnimIndex]; currentFrame = 0; currentAnimTime = 0; previousEventFrame = -1; pingPong = false; isPaused = false; nextTick = Time.time; }
public void UpdateTick(float time) { if (initialized == false) { return; } MeshAnimation cAnim = currentAnimation; if ((isVisible == false && updateWhenOffscreen == false) || isPaused || speed == 0 || cAnim.playbackSpeed == 0) // return if offscreen or crossfading { return; } // if the speed is below the normal playback speed, wait until the next frame can display float lodFPS = LODLevels.Length > currentLodLevel ? LODLevels[currentLodLevel].fps : FPS; float totalSpeed = Mathf.Abs(cAnim.playbackSpeed * speed); float tickRate = Mathf.Max(0.0001f, 1f / lodFPS / totalSpeed); float actualDelta = time - lastFrameTime; bool finished = false; float pingPongMult = pingPong ? -1 : 1; if (speed * cAnim.playbackSpeed < 0) { currentAnimTime -= actualDelta * pingPongMult * totalSpeed; } else { currentAnimTime += actualDelta * pingPongMult * totalSpeed; } if (currentAnimTime < 0) { currentAnimTime = cAnim.length; finished = true; } else if (currentAnimTime > cAnim.length) { if (cAnim.wrapMode == WrapMode.Loop) { currentAnimTime = 0; } finished = true; } nextTick = time + tickRate; lastFrameTime = time; float normalizedTime = currentAnimTime / cAnim.length; int previousFrame = currentFrame; currentFrame = Mathf.Min(Mathf.RoundToInt(normalizedTime * cAnim.totalFrames), cAnim.totalFrames - 1); // do WrapMode.PingPong if (cAnim.wrapMode == WrapMode.PingPong) { if (finished) { pingPong = !pingPong; } } if (finished) { bool stopUpdate = false; if (queuedAnims != null && queuedAnims.Count > 0) { Play(queuedAnims.Dequeue()); stopUpdate = true; } else if (cAnim.wrapMode != WrapMode.Loop && cAnim.wrapMode != WrapMode.PingPong) { nextTick = float.MaxValue; stopUpdate = true; } if (OnAnimationFinished != null) { OnAnimationFinished(cAnim.animationName); } if (stopUpdate) { FireAnimationEvents(cAnim, totalSpeed, finished); return; } } // generate frames if needed and show the current animation frame cAnim.GenerateFrameIfNeeded(baseMesh, currentFrame); // if crossfading, lerp the vertices to the next frame if (currentCrossFade.isFading) { if (currentCrossFade.currentFrame >= currentCrossFade.framesNeeded) { currentFrame = 0; previousFrame = -1; currentAnimTime = 0; ReturnCrossfadeToPool(); } else { #if !THREADS_ENABLED GenerateCrossfadeFrame(); #endif if (currentCrossFade.generatedFrame >= currentCrossFade.currentFrame) { if (crossFadeMesh == null) { crossFadeMesh = GetCrossfadeFromPool(); } crossFadeMesh.vertices = currentCrossFade.frame.positions; crossFadeMesh.bounds = currentCrossFade.frame.bounds; if (recalculateCrossfadeNormals) { crossFadeMesh.RecalculateNormals(); } meshFilter.sharedMesh = crossFadeMesh; currentCrossFade.ReturnFrame(); currentCrossFade.currentFrame++; if (currentCrossFade.currentFrame < currentCrossFade.framesNeeded) { EnqueueAnimatorForCrossfade(this); } // move exposed transforms bool exposedTransforms = childMap != null; bool applyRootMotion = cAnim.rootMotionMode == MeshAnimation.RootMotionMode.AppliedToTransform; if (exposedTransforms || applyRootMotion) { float delta = currentCrossFade.currentFrame / (float)currentCrossFade.framesNeeded; MeshFrameData fromFrame = currentCrossFade.fromFrame; MeshFrameData toFrame = currentCrossFade.toFrame; // move exposed transforms if (exposedTransforms) { for (int i = 0; i < cAnim.exposedTransforms.Length; i++) { Transform child = null; if (fromFrame.exposedTransforms.Length <= i || toFrame.exposedTransforms.Length <= i) { continue; } if (childMap != null && childMap.TryGetValue(cAnim.exposedTransforms[i], out child)) { Matrix4x4 f = fromFrame.exposedTransforms[i]; Matrix4x4 t = toFrame.exposedTransforms[i]; Matrix4x4 c = MatrixLerp(f, t, delta); MatrixUtils.FromMatrix4x4(child, c); } } } // apply root motion if (applyRootMotion) { Vector3 pos = Vector3.Lerp(fromFrame.rootMotionPosition, toFrame.rootMotionPosition, delta); Quaternion rot = Quaternion.Lerp(fromFrame.rootMotionRotation, toFrame.rootMotionRotation, delta); transform.Translate(pos, Space.Self); transform.Rotate(rot.eulerAngles * Time.deltaTime, Space.Self); } } } } } if (currentCrossFade.isFading == false) { cAnim.DisplayFrame(meshFilter, currentFrame, previousFrame); if (currentFrame != previousFrame) { bool exposedTransforms = childMap != null; bool applyRootMotion = cAnim.rootMotionMode == MeshAnimation.RootMotionMode.AppliedToTransform; if (exposedTransforms || applyRootMotion) { MeshFrameData frame = cAnim.GetNearestFrame(currentFrame); // move exposed transforms if (exposedTransforms) { for (int i = 0; i < cAnim.exposedTransforms.Length; i++) { Transform child = null; if (frame.exposedTransforms.Length > i && childMap != null && childMap.TryGetValue(cAnim.exposedTransforms[i], out child)) { MatrixUtils.FromMatrix4x4(child, frame.exposedTransforms[i]); } } } // apply root motion if (applyRootMotion) { if (previousFrame > currentFrame) { // animation looped around, apply motion for skipped frames at the end of the animation for (int i = previousFrame + 1; i < cAnim.frames.Length; i++) { MeshFrameData rootFrame = cAnim.GetNearestFrame(i); transform.Translate(rootFrame.rootMotionPosition, Space.Self); transform.Rotate(rootFrame.rootMotionRotation.eulerAngles * Time.deltaTime, Space.Self); } // now apply motion from first frame to current frame for (int i = 0; i <= currentFrame; i++) { MeshFrameData rootFrame = cAnim.GetNearestFrame(i); transform.Translate(rootFrame.rootMotionPosition, Space.Self); transform.Rotate(rootFrame.rootMotionRotation.eulerAngles * Time.deltaTime, Space.Self); } } else { for (int i = previousFrame + 1; i <= currentFrame; i++) { MeshFrameData rootFrame = cAnim.GetNearestFrame(i); transform.Translate(rootFrame.rootMotionPosition, Space.Self); transform.Rotate(rootFrame.rootMotionRotation.eulerAngles * Time.deltaTime, Space.Self); } } } } } } if (OnFrameUpdated != null) { OnFrameUpdated(); } FireAnimationEvents(cAnim, totalSpeed, finished); // update the lod level if needed if (LODLevels.Length > 0 && (LODCamera != null || Camera.main)) { if (LODCamera == null) { LODCamera = Camera.main.transform; } float dis = (LODCamera.position - mTransform.position).sqrMagnitude; int lodLevel = 0; for (int i = 0; i < LODLevels.Length; i++) { if (dis > LODLevels[i].distance * LODLevels[i].distance) { lodLevel = i; } } if (currentLodLevel != lodLevel) { currentLodLevel = lodLevel; } } }
private void Start() { if (animations.Length == 0) { Debug.LogWarning("No animations for MeshAnimator on object: " + name + ". Disabling.", this); this.enabled = false; return; } for (int i = 0; i < animations.Length; i++) { MeshAnimation animation = animations[i]; if (animation == null) { continue; } animation.GenerateFrames(baseMesh); if (animation.exposedTransforms != null) { for (int j = 0; j < animation.exposedTransforms.Length; j++) { string childName = animation.exposedTransforms[j]; if (string.IsNullOrEmpty(childName)) { continue; } Transform childTransform = transform.Find(childName); if (childTransform != null) { if (childMap == null) { childMap = new Dictionary <string, Transform>(); } if (childMap.ContainsKey(childName) == false) { childMap.Add(childName, childTransform); hasExposedTransforms = true; } } } } } if (meshFilter == null) { meshFilter = GetComponent <MeshFilter>(); } if (!s_meshCount.ContainsKey(baseMesh)) { s_meshCount.Add(baseMesh, 1); } else { s_meshCount[baseMesh]++; } if (playAutomatically) { Play(defaultAnimation.name); } else { isPaused = true; } #if THREADS_ENABLED if (s_crossfadeThreads.Count < MeshAnimatorManager.AnimatorCount / 15f && s_crossfadeThreads.Count < 20) { s_shutDownThreads = false; var t = new System.Threading.Thread(new System.Threading.ThreadStart(GenerateThreadedCrossfade)); t.Start(); s_crossfadeThreads.Add(t); } #endif for (int i = 0; i < LODLevels.Length; i++) { float d = LODLevels[i].distance; LODLevels[i].distanceSquared = d * d; } initialized = true; }