/// <summary> /// Called by the replay system when the object should be recorded. /// </summary> /// <param name="state">The state object to serialize the animator into</param> public override void OnReplaySerialize(ReplayState state) { // Check for animator if (observedAnimator == null) { return; } // Calcualte the number of layers we want to serialize int layerCount = (recordAllLayers == true) ? observedAnimator.layerCount : 1; //// Write the layer count state.Write(layerCount); // Serialize all layer info for (int i = 0; i < layerCount; i++) { // Get the current animator state AnimatorStateInfo info = observedAnimator.GetCurrentAnimatorStateInfo(i); // Get the normalized time float normalizedTime = info.normalizedTime; // Store the state information state.Write(info.fullPathHash); state.Write(normalizedTime); } }
/// <summary> /// Called by the replay system when the object should return to a previous state. /// </summary> /// <param name="state">The state object to deserialize the animator from</param> public override void OnReplayDeserialize(ReplayState state) { // Check for animator if (observedAnimator == null) { return; } // Read the number of layers int layerCount = state.Read32(); // Check if we need to reallocate if (layerCount > animatorStates.Length) { animatorStates = new ReplayAnimatorState[layerCount]; } // Read all layer information for (int i = 0; i < layerCount; i++) { // Update last values animatorStates[i].lastHash = animatorStates[i].targetHash; animatorStates[i].lastNormalizedTime = animatorStates[i].targetNormalizedTime; // Deserialize animator values animatorStates[i].targetHash = state.Read32(); animatorStates[i].targetNormalizedTime = state.ReadFloat(); } // Check for paused if (ReplayManager.IsPaused == true) { observedAnimator.speed = 0; } }
/// <summary> /// Deserializes all active <see cref="ReplayEvent"/> from the state and dispatches any events to the <see cref="OnReplayEvent(ReplayEvent)"/> handler. /// </summary> /// <param name="state">The <see cref="ReplayState"/> to read the variable data from</param> protected virtual void ReplayDeserializeEvents(ReplayState state) { // Read the event size short size = state.Read16(); // Read all events for (int i = 0; i < size; i++) { // Create the event ReplayEvent evt = new ReplayEvent(); // Read the event id evt.eventID = state.ReadByte(); // Read the data size byte dataSize = state.ReadByte(); // Read the event data evt.eventData = state.ReadState(dataSize); try { // Trigger the event callback OnReplayEvent(evt); } catch (Exception e) { Debug.LogException(e); } } }
/// <summary> /// Deserializes all active <see cref="ReplayVariable"/> from the state. /// </summary> /// <param name="state">The <see cref="ReplayState"/> to read the variable data from</param> protected virtual void ReplayDeserializeVariables(ReplayState state) { // Get the size short size = state.Read16(); // Read all variables for (int i = 0; i < size; i++) { bool matchedVariable = false; // Read the variable name hash int variableHash = state.Read32(); // Check for matching variable foreach (ReplayVariable variable in replayVariables) { // We have found a matching variable if (variable.Name.GetHashCode() == variableHash) { // Deserialize the variable variable.OnReplayDeserialize(state); matchedVariable = true; break; } } // We failed to resolve the state - we cannot continue reading because the state contains invalid data if (matchedVariable == false) { break; } } }
/// <summary> /// Write the entire contents of a <see cref="ReplayState"/> to this <see cref="ReplayState"/>. /// All bytes will be appended. /// </summary> /// <param name="other">The other state to append</param> public void Write(ReplayState other) { // Copy all bytes foreach (byte value in other.bytes) { Write(value); } }
/// <summary> /// Called by the replay system when this <see cref="ReplayObject"/> should deserialize its replay data. /// </summary> /// <param name="state">The <see cref="ReplayState"/> to deserialize the data from</param> public void OnReplayDeserialize(ReplayState state) { // Always read the prefab name state.ReadString(); // Read all data while (state.EndRead == false) { // Read the id ReplayIdentity identity = state.ReadIdentity(); // Read the size short size = state.Read16(); // Mathc flag bool matchedData = false; // Check for matched id foreach (ReplayBehaviour behaviour in observedComponents) { // Check for destroyed components if (behaviour == null) { continue; } // Check for matching sub id if (behaviour.Identity == identity) { // We have matched the data so we can now deserialize behaviourState = state.ReadState(size); // Deserialize the component behaviour.OnReplayDeserialize(behaviourState); // Set matched flags matchedData = true; break; } } // Check for found data if (matchedData == false) { // We need to consume the data to maintain the state for (int i = 0; i < size; i++) { // Call read byte multiple times to save allocating an array state.ReadByte(); } Debug.LogWarning("Possible replay state corruption for replay object " + gameObject); } } }
/// <summary> /// Called by the relay system when the object should return to a previous state. /// </summary> /// <param name="state">The state object to deserialize the transform from</param> public override void OnReplayDeserialize(ReplayState state) { // Update the last transform OnReplayReset(); // Read the transform flags targetFlags = (ReplayTransformFlags)state.Read16(); // Check for low precision mode if ((targetFlags & ReplayTransformFlags.LowPrecision) == 0) { // Read the position if ((targetFlags & ReplayTransformFlags.Position) != 0) { targetPosition = state.ReadVec3(); } // Read the rotation if ((targetFlags & ReplayTransformFlags.Rotation) != 0) { targetRotation = state.ReadQuat(); } // Read the scale if ((targetFlags & ReplayTransformFlags.Scale) != 0) { targetScale = state.ReadVec3(); } } else { // Read the position if ((targetFlags & ReplayTransformFlags.Position) != 0) { targetPosition = state.ReadVec3LowPrecision(); } // Read the rotation if ((targetFlags & ReplayTransformFlags.Rotation) != 0) { targetRotation = state.ReadQuatLowPrecision(); } // Read the scale if ((targetFlags & ReplayTransformFlags.Scale) != 0) { targetScale = state.ReadVec3LowPrecision(); } } }
public override void OnReplaySerialize(ReplayState state) { // Require particle system if (observedParticleSystem == null) { return; } // Check for playing particles system - Dont waste data when nothing is happening //if (particles.isPlaying == false) // return; // Write particle time state.Write(observedParticleSystem.time); }
public override void OnReplayDeserialize(ReplayState state) { // Require particle system if (observedParticleSystem == null) { return; } // Save the last time lastTime = targetTime; // Read the target simulation time targetTime = state.ReadFloat(); observedParticleSystem.Simulate(targetTime - lastTime, true, false); }
/// <summary> /// Push an event to the replay system for recording. /// </summary> /// <param name="eventID">The event id to uniquley identify the event type</param> /// <param name="state">The state data for the event or null if no state data is required</param> public void ReplayRecordEvent(byte eventID, ReplayState state = null) { // Create the replay event ReplayEvent evt = new ReplayEvent { eventID = eventID, eventData = state, }; // Check for null state if (evt.eventData == null) { evt.eventData = new ReplayState(); } // Push to event queue replayEvents.Enqueue(evt); }
/// <summary> /// Serializes all active <see cref="ReplayVariable"/> to the state. /// </summary> /// <param name="state">The <see cref="ReplayState"/> to write the varaible data to</param> protected virtual void ReplaySerializeVariables(ReplayState state) { // Get size short size = (short)replayVariables.Length; // Write the size to the state state.Write(size); // Write all variables foreach (ReplayVariable variable in replayVariables) { // Write the hash code of the name so we can identify the data later state.Write(variable.Name.GetHashCode()); // Write the variable to the state variable.OnReplaySerialize(state); } }
/// <summary> /// Called by the replay system when this <see cref="ReplayObject"/> should serialize its replay data. /// </summary> /// <param name="state">The <see cref="ReplayState"/> to serialize the data to</param> public void OnReplaySerialize(ReplayState state) { // Check for empty components if (observedComponents.Length == 0) { return; } // Always write the prefab name state.Write(prefabIdentity); // Serialize all components foreach (ReplayBehaviour behaviour in observedComponents) { // Check for destroyed components if (behaviour == null) { continue; } // Clear the shared buffer behaviourState.Clear(); // Serialzie the behaviour behaviour.OnReplaySerialize(behaviourState); // Check for no data - dont waste memory and time serializing nothing if (behaviourState.Size == 0) { continue; } // Write the meta data state.Write(behaviour.Identity); state.Write((short)behaviourState.Size); state.Write(behaviourState); } }
/// <summary> /// Serializes all awaiting <see cref="ReplayEvent"/> to the state. /// </summary> /// <param name="state">The <see cref="ReplayState"/> to write the varaible data to</param> protected virtual void ReplaySerializeEvents(ReplayState state) { // Get the number of events short size = (short)replayEvents.Count; // Write the size to the state state.Write(size); // Write all events while (replayEvents.Count > 0) { // Get the next event ReplayEvent evt = replayEvents.Dequeue(); // Write the event id state.Write(evt.eventID); // Write the size and event data state.Write((byte)evt.eventData.Size); state.Write(evt.eventData); } }
/// <summary> /// Called by the replay system when all replay state information should be serialized. /// </summary> /// <param name="state">The <see cref="ReplayState"/> to write the data to</param> public virtual void OnReplaySerialize(ReplayState state) { #if UNITY_EDITOR // Serialize may be called by the editor in which case we will need to find variables on each call if (Application.isPlaying == false) { ReplayFindVariables(); } #endif ReplayBehaviourDataFlags flags = ReplayBehaviourDataFlags.None; // Generate the data flags so we can read the correct data back if (replayEvents.Count > 0) { flags |= ReplayBehaviourDataFlags.Events; } if (replayVariables.Length > 0) { flags |= ReplayBehaviourDataFlags.Variables; } // Always write the flag byte state.Write((byte)flags); // Serialize replay events if ((flags & ReplayBehaviourDataFlags.Events) != 0) { // Serialize all events ReplaySerializeEvents(state); } // Serialize all replay variables by default if ((flags & ReplayBehaviourDataFlags.Variables) != 0) { // Serialize all variables ReplaySerializeVariables(state); } }
/// <summary> /// Called by the replay system when all replay state information should be deserialized. /// </summary> /// <param name="state">The <see cref="ReplayState"/> to read the data from</param> public virtual void OnReplayDeserialize(ReplayState state) { // Check if we can read if (state.EndRead == true) { return; } // Get the flag byte ReplayBehaviourDataFlags flags = (ReplayBehaviourDataFlags)state.ReadByte(); // Deserialize events if there are any if ((flags & ReplayBehaviourDataFlags.Events) != 0) { ReplayDeserializeEvents(state); } // Deserialize variables if there are any if ((flags & ReplayBehaviourDataFlags.Variables) != 0) { ReplayDeserializeVariables(state); } }
/// <summary> /// Called by the replay system when the object should be recorded. /// </summary> /// <param name="state">The state object to serialize the transform into</param> public override void OnReplaySerialize(ReplayState state) { // Calcualte the flags ReplayTransformFlags flags = 0; // Build the flag information if (recordPosition != ReplayTransformRecordSpace.None) { flags |= ReplayTransformFlags.Position; if (recordPosition == ReplayTransformRecordSpace.Local) { flags |= ReplayTransformFlags.LocalPosition; } } if (recordRotation != ReplayTransformRecordSpace.None) { flags |= ReplayTransformFlags.Rotation; if (recordRotation == ReplayTransformRecordSpace.Local) { flags |= ReplayTransformFlags.LocalRotation; } } if (recordScale == true) { flags |= ReplayTransformFlags.Scale; } // Check for any flags if (flags == 0) { return; } // Check for low precision if (lowPrecision == true) { flags |= ReplayTransformFlags.LowPrecision; } // Write the flags state.Write((short)flags); // Check for low precision mode if (lowPrecision == false) { // Write the position if ((flags & ReplayTransformFlags.Position) != 0) { if ((flags & ReplayTransformFlags.LocalPosition) != 0) { state.Write(transform.localPosition); } else { state.Write(transform.position); } } // Write the rotation if ((flags & ReplayTransformFlags.Rotation) != 0) { if ((flags & ReplayTransformFlags.LocalRotation) != 0) { state.Write(transform.localRotation); } else { state.Write(transform.rotation); } } // Write the scale if ((flags & ReplayTransformFlags.Scale) != 0) { state.Write(transform.localScale); } } else { // Write the position if ((flags & ReplayTransformFlags.Position) != 0) { if ((flags & ReplayTransformFlags.LocalPosition) != 0) { state.WriteLowPrecision(transform.localPosition); } else { state.WriteLowPrecision(transform.position); } } // Write the rotation if ((flags & ReplayTransformFlags.Rotation) != 0) { if ((flags & ReplayTransformFlags.LocalRotation) != 0) { state.WriteLowPrecision(transform.localRotation); } else { state.WriteLowPrecision(transform.rotation); } } // Write the scale if ((flags & ReplayTransformFlags.Scale) != 0) { state.WriteLowPrecision(transform.localScale); } } }
/// <summary> /// Push an event to the replay system for recording. /// </summary> /// <param name="eventID">The event id to uniquley identify the event type</param> /// <param name="state">The state data for the event or null if no state data is required</param> public void ReplayRecordEvent(ReplayEvents eventID, ReplayState state = null) { // Call through ReplayRecordEvent((byte)eventID, state); }