private void PrepareForGameplay(ReplayInitialDataBuffer initialStateBuffer) { // Check if we can restore the previous scene state if (prePlaybackState != null) { // Restore the original game state if (restorePreviousSceneState == true) { RestoreSnapshot(prePlaybackState, initialStateBuffer); } // Reset to null so that next states are saved prePlaybackState = null; // Set delta so that changes are immediate ReplayTime.Delta = 1; // Be sure to update so interpolated objects can also update ReplayBehaviour.Events.CallReplayUpdateEvents(); } for (int i = 0; i < replayObjects.Count; i++) { ReplayManager.Preparer.PrepareForGameplay(replayObjects[i]); } }
private void PrepareForPlayback(ReplayInitialDataBuffer initialStateBuffer) { // Sample the current scene prePlaybackState = RecordSnapshot(0, initialStateBuffer); for (int i = 0; i < replayObjects.Count; i++) { // Prepare the object for playback ReplayManager.Preparer.PrepareForPlayback(replayObjects[i]); } }
/// <summary> /// Use this method to begin sampling the recorded objects in the scene. /// If <see cref="recordOnStart"/> is true then this method will be called automatically when the manager is initialized. /// Any state information will be recored via the assigned <see cref="ReplayTarget"/> (Default <see cref="ReplayMemoryTarget"/>). /// <param name="cleanRecording">When true, any previous recording data will be discarded</param> /// </summary> public static void BeginRecording(bool cleanRecording = true) { // Make sure we have a manager if (Instance == null) { return; } // Throw away any leftover recording data if (cleanRecording == true) { DiscardRecording(); } // Check if we are replaying if (IsReplaying == true) { StopPlayback(); } // Inform the replay target that we are about to begin recording Target.PrepareTarget(ReplayTargetTask.PrepareWrite); if (instance.state != PlaybackState.Recording_Paused) { // Reset the record timer Instance.recordStepTimer.Reset(); Instance.recordTimer.Reset(); // Every recording must begin with a frame that has a time stamp of 0 ReplaySnapshot currentState = Instance.scene.RecordSnapshot(0, Target.InitialStateBuffer); // Write it to the replay target Target.RecordSnapshot(currentState); } // Change to recording state Instance.state = PlaybackState.Recording; }
/// <summary> /// Take a snapshot of the current replay scene using the specified timestamp. /// </summary> /// <param name="timeStamp">The timestamp for the frame indicating its position in the playback sequence</param> /// <param name="initialStateBuffer">The <see cref="ReplayInitialDataBuffer"/> to restore dynamic object information from</param> /// <returns>A new snapshot of the current replay scene</returns> public ReplaySnapshot RecordSnapshot(float timeStamp, ReplayInitialDataBuffer initialStateBuffer) { ReplaySnapshot snapshot = new ReplaySnapshot(timeStamp); if (initialStateBuffer != null) { // Be sure to record any objects initial transform if they were spawned during the snapshot while (dynamicReplayObjects.Count > 0) { // Get the next object ReplayObject obj = dynamicReplayObjects.Dequeue(); // Make sure the object has not been destroyed if (obj != null) { // Record initial values initialStateBuffer.RecordInitialReplayObjectData(obj, timeStamp, obj.transform.position, obj.transform.rotation, obj.transform.localScale); } } } // Record each object in the scene foreach (ReplayObject obj in replayObjects) { ReplayState state = new ReplayState(); // Serialize the object obj.OnReplaySerialize(state); // Check if the state contains any information - If not then dont waste valuable memory if (state.Size == 0) { continue; } // Record the snapshot snapshot.RecordSnapshot(obj.ReplayIdentity, state); } return(snapshot); }
/// <summary> /// Restore the scene to the state described by the specified snapshot. /// </summary> /// <param name="snapshot">The snapshot to restore</param> /// <param name="initialStateBuffer">The <see cref="ReplayInitialDataBuffer"/> to restore dynamic object information from</param> public void RestoreSnapshot(ReplaySnapshot snapshot, ReplayInitialDataBuffer initialStateBuffer) { // Restore all events first snapshot.RestoreReplayObjects(this, initialStateBuffer); // Restore all replay objects foreach (ReplayObject obj in replayObjects) { // Get the state based on the identity ReplayState state = snapshot.RestoreSnapshot(obj.ReplayIdentity); // Check if no state information for this object was found if (state == null) { continue; } // Deserialize the object obj.OnReplayDeserialize(state); } }
/// <summary> /// Use this method to specify where in the replay sequence the playback should start. /// This method accepts normalized offsets values between 0 and 1 and performs validation before using the value. /// </summary> /// <param name="normalizedOffset">The normalized value representing the offset from the specified origin to start the playback from</param> /// <param name="origin">The playback node to take the offset from. If <see cref="PlaybackOrigin.End"/> is specified then the offset value will be used as a negative offset</param> public static void SetPlaybackFrameNormalized(float normalizedOffset, PlaybackOrigin origin = PlaybackOrigin.Start) { // Make sure we have a manager if (Instance == null) { return; } // Move our current sequence frame to the normalized value ReplaySnapshot frame = Instance.sequence.SeekPlayback(normalizedOffset, origin, true); // Check for a valid frame if (frame != null) { // Restore the scene state Instance.scene.RestoreSnapshot(frame, Target.InitialStateBuffer); // Call the reset event ReplayBehaviour.Events.CallReplayResetEvents(); // This calls 'updateReplay' on all replay obejcts in the scene ReplayBehaviour.Events.CallReplayUpdateEvents(); } }
/// <summary> /// The main update method of the replay manager. /// Causes the replay manager to update its state based on the current <see cref="state"/>. /// </summary> /// <param name="deltaTime"></param> public void UpdateState(float deltaTime) { if (deltaTime <= 0f) { Debug.Log(Time.timeScale); throw new InvalidOperationException("Delta time value must be greater than '0'"); } switch (state) { case PlaybackState.Idle: break; case PlaybackState.Playback_Paused: break; // Do nothing case PlaybackState.Recording_Paused: break; // Do nothing case PlaybackState.Recording_Paused_Playback: case PlaybackState.Playback: { // Make sure we are not in single frame mode if (singlePlayback == false) { ReplaySnapshot frame; // Update the sequencer ReplaySequenceResult result = sequence.UpdatePlayback(out frame, playbackEndBehaviour, deltaTime); // Check if the sequence has a new frame to be displayed if (result == ReplaySequenceResult.SequenceAdvance) { // Restore the scene state scene.RestoreSnapshot(frame, Target.InitialStateBuffer); } // Check if the sequence is at the end of the recording else if (result == ReplaySequenceResult.SequenceEnd) { StopPlayback(); } // Update the playback if (result == ReplaySequenceResult.SequenceIdle || result == ReplaySequenceResult.SequenceAdvance) { // This calls 'updateReplay' on all replay obejcts in the scene ReplayBehaviour.Events.CallReplayUpdateEvents(); } } } break; case PlaybackState.Recording: { // Update the replay timer - Pass the delta time instead of using Time.deltaTime ReplayTimer.Tick(deltaTime); // Make sure enough time has passed to record a sample if (recordStepTimer.HasElapsed() == true) { // Get the scene state ReplaySnapshot currentState = scene.RecordSnapshot(Instance.recordTimer.ElapsedSeconds, Target.InitialStateBuffer); // Write it to the replay target Target.RecordSnapshot(currentState); } } break; } }
// Methods public ReplaySnapshot SeekPlayback(float offset, PlaybackOrigin origin, bool normalized) { // Check for normalized if (normalized == false) { // Check for seek mode switch (origin) { case PlaybackOrigin.Start: playbackTime = offset; break; case PlaybackOrigin.End: playbackTime = target.Duration - offset; break; case PlaybackOrigin.Current: playbackTime += offset; break; } } else { // Clamp the input valid offset = Mathf.Clamp01(offset); // Check for seek mode switch (origin) { case PlaybackOrigin.Start: playbackTime = MapScale(offset, 0, 1, 0, target.Duration); break; case PlaybackOrigin.End: playbackTime = MapScale(offset, 1, 0, 0, target.Duration); break; case PlaybackOrigin.Current: playbackTime = MapScale(offset, 0, 1, playbackTime, target.Duration); break; } } // Clamp to valid range playbackTime = Mathf.Clamp(playbackTime, 0, target.Duration); // Restore the scene state current = target.RestoreSnapshot(playbackTime); // Check for change if (current != last) { // Update the replay time ReplayTime.Time = playbackTime; if (last != null && current != null) { // Check for backwards if (last.TimeStamp <= current.TimeStamp) { // Forward ReplayTime.Delta = MapScale(playbackTime, last.TimeStamp, current.TimeStamp, 0, 1); } else { // Backward ReplayTime.Delta = -MapScale(playbackTime, last.TimeStamp, current.TimeStamp, 1, 0); } } } //else //{ // ReplayTime.Delta = 0; //} // Store current frame last = current; return(current); }
public ReplaySequenceResult UpdatePlayback(out ReplaySnapshot frame, PlaybackEndBehaviour endBehaviour, float deltaTime) { PlaybackDirection direction = ReplayTime.TimeScaleDirection; // Default to idle ReplaySequenceResult result = ReplaySequenceResult.SequenceIdle; if (last != null) { if (direction == PlaybackDirection.Forward) { // Calculatet the delta time ReplayTime.Delta = MapScale(playbackTime, last.TimeStamp, current.TimeStamp, 0, 1); } else { // Calculate the delta for reverse playback ReplayTime.Delta = -MapScale(playbackTime, current.TimeStamp, last.TimeStamp, 0, 1); } } else { if (current == null) { ReplayTime.Delta = 0; } else { ReplayTime.Delta = MapScale(playbackTime, 0, current.TimeStamp, 0, 1); } } // Clamp delta ReplayTime.Delta = Mathf.Clamp01(ReplayTime.Delta); float delta = Mathf.Abs(deltaTime * ReplayTime.TimeScale); //if (fixedTime == true) //{ // // Find the fixed time delta // delta = (Time.fixedDeltaTime * ReplayTime.TimeScale); //} //else //{ // // Find the time delta // delta = (Time.deltaTime * Mathf.Abs(ReplayTime.TimeScale)); //} // Advance our frame switch (direction) { case PlaybackDirection.Forward: { playbackTime += delta; } break; case PlaybackDirection.Backward: { playbackTime -= delta; } break; } switch (endBehaviour) { default: case PlaybackEndBehaviour.EndPlayback: { // Check for end of playback if (playbackTime >= target.Duration || playbackTime < 0) { frame = null; return(ReplaySequenceResult.SequenceEnd); } break; } case PlaybackEndBehaviour.LoopPlayback: { if (playbackTime >= target.Duration || playbackTime < 0) { playbackTime = (direction == PlaybackDirection.Forward) ? 0 : target.Duration; } break; } case PlaybackEndBehaviour.StopPlayback: { if (playbackTime >= target.Duration) { playbackTime = target.Duration; } else if (playbackTime < 0) { playbackTime = 0; } break; } } // Try to get the current frame ReplaySnapshot temp = target.RestoreSnapshot(playbackTime); // Check for valid frame if (temp != null) { // Check for sequence advancement if (current != temp) { // Snap to next frame ReplayTime.Delta = 0; // Set the result result = ReplaySequenceResult.SequenceAdvance; // Update last frame last = current; } // Update the current frame current = temp; } else { // Do nothing - We may be inbetween replay frames // Trigger sequence end //frame = null; //return ReplaySequenceResult.SequenceEnd; } // The sequencer only updated its timing values and there was no state change frame = current; return(result); }