Component that records a GameObject's local position and rotation into a MovieRecorder for ghost playback. It exposes SampleRate, StartRecording/StopRecording, and produces a GhostRecording from the finished clip.
using Sandbox.MovieMaker;
namespace Machines.Ghost;
/// <summary>
/// Records car position/rotation via MovieRecorder for ghost playback.
/// </summary>
public sealed class GhostRecorder : Component
{
/// <summary>
/// Sample rate in frames per second.
/// </summary>
[Property]
public float SampleRate { get; set; } = 10;
/// <summary>
/// Whether the recorder is currently capturing.
/// </summary>
public bool IsRecording { get; private set; }
private MovieRecorder _recorder;
/// <summary>
/// Begins recording, clearing any previous data.
/// </summary>
public void StartRecording()
{
StopRecording();
var options = new MovieRecorderOptions( SampleRate: (int)SampleRate )
.WithCaptureAction( x =>
{
var track = x.GetTrackRecorder( GameObject );
track.Property( nameof( GameObject.LocalPosition ) ).Capture();
track.Property( nameof( GameObject.LocalRotation ) ).Capture();
} );
_recorder = new MovieRecorder( Scene, options );
_recorder.Start();
IsRecording = true;
}
/// <summary>
/// Stop recording and finalize the data.
/// </summary>
public void StopRecording()
{
if ( _recorder is not null )
{
_recorder.Stop();
IsRecording = false;
}
}
/// <summary>
/// Stops recording on destroy to avoid a dangling recorder on scene change.
/// </summary>
protected override void OnDestroy()
{
StopRecording();
}
/// <summary>
/// Returns the completed recording. Call after <see cref="StopRecording"/>.
/// </summary>
public GhostRecording GetRecording( string carResourcePath, string playerName = null )
{
if ( _recorder == null )
return null;
var clip = _recorder.ToClip();
if ( clip == null )
return null;
_recorder = null;
return GhostRecording.Create( carResourcePath, clip, playerName );
}
}