adding basis for input recorder, still needs to debug why this isn't working when editor is in the background
This commit is contained in:
Родитель
e50a3d8427
Коммит
95d4ce7ece
|
@ -89,3 +89,4 @@ crashlytics-build.properties
|
||||||
|
|
||||||
# Mac file setting
|
# Mac file setting
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
Basic/ClientDriven_clone_0
|
||||||
|
|
|
@ -201,10 +201,34 @@ MonoBehaviour:
|
||||||
m_Script: {fileID: 11500000, guid: c521a646e08a84279a7bcf9c775080a4, type: 3}
|
m_Script: {fileID: 11500000, guid: c521a646e08a84279a7bcf9c775080a4, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
m_ClientPlayerMove: {fileID: 5667156634780145037}
|
|
||||||
isObjectPickedUp:
|
isObjectPickedUp:
|
||||||
m_InternalValue: 0
|
m_InternalValue: 0
|
||||||
m_LocalHeldPosition: {x: 0, y: 2.85, z: 0}
|
m_LocalHeldPosition: {x: 0, y: 2.85, z: 0}
|
||||||
|
--- !u!114 &1465562511556424027
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1116025501350672692}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: d35d5308cbb1fcd479dbf0c516db2aa7, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_StartRecordingWhenEnabled: 0
|
||||||
|
m_RecordFrames: 1
|
||||||
|
m_ReplayOnNewDevices: 1
|
||||||
|
m_SimulateOriginalTimingOnReplay: 0
|
||||||
|
m_RecordStateEventsOnly: 0
|
||||||
|
m_CaptureMemoryDefaultSize: 2097152
|
||||||
|
m_CaptureMemoryMaxSize: 10485760
|
||||||
|
m_DevicePath:
|
||||||
|
m_RecordButtonPath: <Keyboard>/r
|
||||||
|
m_PlayButtonPath: <Keyboard>/p
|
||||||
|
m_ChangeEvent:
|
||||||
|
m_PersistentCalls:
|
||||||
|
m_Calls: []
|
||||||
--- !u!114 &5667156634780145037
|
--- !u!114 &5667156634780145037
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a9ba9a3d42bc74ddf8c3d003dc3f81cc
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 544ea342e5333486e9ce814098eedfb8
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8becf87c92a4b4a119ffec85af4b2cf5
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5a6dffe792b1b4b2fae63ccb1d441f05
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"displayName": "Input Recorder",
|
||||||
|
"description": "Shows how to capture and replay input events. Also useful by itself to debug input event sequences."
|
||||||
|
}
|
|
@ -0,0 +1,472 @@
|
||||||
|
using System;
|
||||||
|
using UnityEngine.Events;
|
||||||
|
using UnityEngine.InputSystem.Layouts;
|
||||||
|
using UnityEngine.InputSystem.LowLevel;
|
||||||
|
|
||||||
|
////TODO: allow multiple device paths
|
||||||
|
|
||||||
|
////TODO: streaming support
|
||||||
|
|
||||||
|
////REVIEW: consider this for inclusion directly in the input system
|
||||||
|
|
||||||
|
namespace UnityEngine.InputSystem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A wrapper component around <see cref="InputEventTrace"/> that provides an easy interface for recording input
|
||||||
|
/// from a GameObject.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This component comes with a custom inspector that provides an easy recording and playback interface and also
|
||||||
|
/// gives feedback about what has been recorded in the trace. The interface also allows saving and loading event
|
||||||
|
/// traces.
|
||||||
|
///
|
||||||
|
/// Capturing can either be constrained by a <see cref="devicePath"/> or capture all input occuring in the system.
|
||||||
|
///
|
||||||
|
/// Replay by default will happen frame by frame (see <see cref="InputEventTrace.ReplayController.PlayAllFramesOneByOne"/>).
|
||||||
|
/// If frame markers are disabled (see <see cref="recordFrames"/>), all events are queued right away in the first
|
||||||
|
/// frame and replay completes immediately.
|
||||||
|
///
|
||||||
|
/// Other than frame-by-frame, replay can be made to happen in a way that tries to simulate the original input
|
||||||
|
/// timing. To do so, enable <see cref="simulateOriginalTimingOnReplay"/>. This will make use of <see
|
||||||
|
/// cref="InputEventTrace.ReplayController.PlayAllEventsAccordingToTimestamps"/>
|
||||||
|
/// </remarks>
|
||||||
|
public class InputRecorder : MonoBehaviour
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether a capture is currently in progress.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>True if a capture is in progress.</value>
|
||||||
|
public bool captureIsRunning => m_EventTrace != null && m_EventTrace.enabled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether a replay is currently being run by the component.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>True if replay is running.</value>
|
||||||
|
/// <seealso cref="replay"/>
|
||||||
|
/// <seealso cref="StartReplay"/>
|
||||||
|
/// <seealso cref="StopReplay"/>
|
||||||
|
public bool replayIsRunning => m_ReplayController != null && !m_ReplayController.finished;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, input recording is started immediately when the component is enabled. Disabled by default.
|
||||||
|
/// Call <see cref="StartCapture"/> to manually start capturing.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>True if component will start recording automatically in <see cref="OnEnable"/>.</value>
|
||||||
|
/// <seealso cref="StartCapture"/>
|
||||||
|
public bool startRecordingWhenEnabled
|
||||||
|
{
|
||||||
|
get => m_StartRecordingWhenEnabled;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
m_StartRecordingWhenEnabled = value;
|
||||||
|
if (value && enabled && !captureIsRunning)
|
||||||
|
StartCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Total number of events captured.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>Number of captured events.</value>
|
||||||
|
public long eventCount => m_EventTrace?.eventCount ?? 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Total size of captured events.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>Size of captured events in bytes.</value>
|
||||||
|
public long totalEventSizeInBytes => m_EventTrace?.totalEventSizeInBytes ?? 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Total size of capture memory currently allocated.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>Size of memory allocated for capture.</value>
|
||||||
|
public long allocatedSizeInBytes => m_EventTrace?.allocatedSizeInBytes ?? 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to record frame marker events when capturing input. Enabled by default.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>True if frame marker events will be recorded.</value>
|
||||||
|
/// <seealso cref="InputEventTrace.recordFrameMarkers"/>
|
||||||
|
public bool recordFrames
|
||||||
|
{
|
||||||
|
get => m_RecordFrames;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (m_RecordFrames == value)
|
||||||
|
return;
|
||||||
|
m_RecordFrames = value;
|
||||||
|
if (m_EventTrace != null)
|
||||||
|
m_EventTrace.recordFrameMarkers = m_RecordFrames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to record only <see cref="StateEvent"/>s and <see cref="DeltaStateEvent"/>s. Disabled by
|
||||||
|
/// default.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>True if anything but state events should be ignored.</value>
|
||||||
|
public bool recordStateEventsOnly
|
||||||
|
{
|
||||||
|
get => m_RecordStateEventsOnly;
|
||||||
|
set => m_RecordStateEventsOnly = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Path that constrains the devices to record from.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>Input control path to match devices or null/empty.</value>
|
||||||
|
/// <remarks>
|
||||||
|
/// By default, this is not set. Meaning that input will be recorded from all devices. By setting this property
|
||||||
|
/// to a path, only events for devices that match the given path (as dictated by <see cref="InputControlPath.Matches"/>)
|
||||||
|
/// will be recorded from.
|
||||||
|
///
|
||||||
|
/// By setting this property to the exact path of a device at runtime, recording can be restricted to just that
|
||||||
|
/// device.
|
||||||
|
/// </remarks>
|
||||||
|
/// <seealso cref="InputControlPath"/>
|
||||||
|
/// <seealso cref="InputControlPath.Matches"/>
|
||||||
|
public string devicePath
|
||||||
|
{
|
||||||
|
get => m_DevicePath;
|
||||||
|
set => m_DevicePath = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string recordButtonPath
|
||||||
|
{
|
||||||
|
get => m_RecordButtonPath;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
m_RecordButtonPath = value;
|
||||||
|
HookOnInputEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string playButtonPath
|
||||||
|
{
|
||||||
|
get => m_PlayButtonPath;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
m_PlayButtonPath = value;
|
||||||
|
HookOnInputEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The underlying event trace that contains the captured input events.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>Underlying event trace.</value>
|
||||||
|
/// <remarks>
|
||||||
|
/// This will be null if no capture is currently associated with the recorder.
|
||||||
|
/// </remarks>
|
||||||
|
public InputEventTrace capture => m_EventTrace;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The replay controller for when a replay is running.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>Replay controller for the event trace while replay is running.</value>
|
||||||
|
/// <seealso cref="replayIsRunning"/>
|
||||||
|
/// <seealso cref="StartReplay"/>
|
||||||
|
public InputEventTrace.ReplayController replay => m_ReplayController;
|
||||||
|
|
||||||
|
public int replayPosition
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (m_ReplayController != null)
|
||||||
|
return m_ReplayController.position;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
////TODO: allow setting replay position
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether a replay should create new devices or replay recorded events as is. Disabled by default.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>True if replay should temporary create new devices.</value>
|
||||||
|
/// <seealso cref="InputEventTrace.ReplayController.WithAllDevicesMappedToNewInstances"/>
|
||||||
|
public bool replayOnNewDevices
|
||||||
|
{
|
||||||
|
get => m_ReplayOnNewDevices;
|
||||||
|
set => m_ReplayOnNewDevices = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to attempt to re-create the original event timing when replaying events. Disabled by default.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>If true, events are queued based on their timestamp rather than based on their recorded frames (if any).</value>
|
||||||
|
/// <seealso cref="InputEventTrace.ReplayController.PlayAllEventsAccordingToTimestamps"/>
|
||||||
|
public bool simulateOriginalTimingOnReplay
|
||||||
|
{
|
||||||
|
get => m_SimulateOriginalTimingOnReplay;
|
||||||
|
set => m_SimulateOriginalTimingOnReplay = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChangeEvent changeEvent
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (m_ChangeEvent == null)
|
||||||
|
m_ChangeEvent = new ChangeEvent();
|
||||||
|
return m_ChangeEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartCapture()
|
||||||
|
{
|
||||||
|
if (m_EventTrace != null && m_EventTrace.enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CreateEventTrace();
|
||||||
|
m_EventTrace.Enable();
|
||||||
|
m_ChangeEvent?.Invoke(Change.CaptureStarted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopCapture()
|
||||||
|
{
|
||||||
|
if (m_EventTrace != null && m_EventTrace.enabled)
|
||||||
|
{
|
||||||
|
m_EventTrace.Disable();
|
||||||
|
m_ChangeEvent?.Invoke(Change.CaptureStopped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartReplay()
|
||||||
|
{
|
||||||
|
if (m_EventTrace == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (replayIsRunning && replay.paused)
|
||||||
|
{
|
||||||
|
replay.paused = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StopCapture();
|
||||||
|
|
||||||
|
// Configure replay controller.
|
||||||
|
m_ReplayController = m_EventTrace.Replay()
|
||||||
|
.OnFinished(StopReplay)
|
||||||
|
.OnEvent(_ => m_ChangeEvent?.Invoke(Change.EventPlayed));
|
||||||
|
if (m_ReplayOnNewDevices)
|
||||||
|
m_ReplayController.WithAllDevicesMappedToNewInstances();
|
||||||
|
|
||||||
|
// Start replay.
|
||||||
|
if (m_SimulateOriginalTimingOnReplay)
|
||||||
|
m_ReplayController.PlayAllEventsAccordingToTimestamps();
|
||||||
|
else
|
||||||
|
m_ReplayController.PlayAllFramesOneByOne();
|
||||||
|
|
||||||
|
m_ChangeEvent?.Invoke(Change.ReplayStarted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopReplay()
|
||||||
|
{
|
||||||
|
if (m_ReplayController != null)
|
||||||
|
{
|
||||||
|
m_ReplayController.Dispose();
|
||||||
|
m_ReplayController = null;
|
||||||
|
m_ChangeEvent?.Invoke(Change.ReplayStopped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PauseReplay()
|
||||||
|
{
|
||||||
|
if (m_ReplayController != null)
|
||||||
|
m_ReplayController.paused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearCapture()
|
||||||
|
{
|
||||||
|
m_EventTrace?.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadCaptureFromFile(string fileName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(fileName))
|
||||||
|
throw new ArgumentNullException(nameof(fileName));
|
||||||
|
|
||||||
|
CreateEventTrace();
|
||||||
|
m_EventTrace.ReadFrom(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveCaptureToFile(string fileName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(fileName))
|
||||||
|
throw new ArgumentNullException(nameof(fileName));
|
||||||
|
m_EventTrace?.WriteTo(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnEnable()
|
||||||
|
{
|
||||||
|
// Hook InputSystem.onEvent before the event trace does.
|
||||||
|
HookOnInputEvent();
|
||||||
|
|
||||||
|
if (m_StartRecordingWhenEnabled)
|
||||||
|
StartCapture();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnDisable()
|
||||||
|
{
|
||||||
|
StopCapture();
|
||||||
|
StopReplay();
|
||||||
|
UnhookOnInputEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnDestroy()
|
||||||
|
{
|
||||||
|
m_ReplayController?.Dispose();
|
||||||
|
m_ReplayController = null;
|
||||||
|
m_EventTrace?.Dispose();
|
||||||
|
m_EventTrace = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool OnFilterInputEvent(InputEventPtr eventPtr, InputDevice device)
|
||||||
|
{
|
||||||
|
// Filter out non-state events, if enabled.
|
||||||
|
if (m_RecordStateEventsOnly && !eventPtr.IsA<StateEvent>() && !eventPtr.IsA<DeltaStateEvent>())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Match device path, if set.
|
||||||
|
if (string.IsNullOrEmpty(m_DevicePath) || device == null)
|
||||||
|
return true;
|
||||||
|
return InputControlPath.MatchesPrefix(m_DevicePath, device);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEventRecorded(InputEventPtr eventPtr)
|
||||||
|
{
|
||||||
|
m_ChangeEvent?.Invoke(Change.EventCaptured);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInputEvent(InputEventPtr eventPtr, InputDevice device)
|
||||||
|
{
|
||||||
|
if (!eventPtr.IsA<StateEvent>() && !eventPtr.IsA<DeltaStateEvent>())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(m_PlayButtonPath))
|
||||||
|
{
|
||||||
|
var playControl = InputControlPath.TryFindControl(device, m_PlayButtonPath) as InputControl<float>;
|
||||||
|
if (playControl != null && playControl.ReadValueFromEvent(eventPtr) >= InputSystem.settings.defaultButtonPressPoint)
|
||||||
|
{
|
||||||
|
if (replayIsRunning)
|
||||||
|
StopReplay();
|
||||||
|
else
|
||||||
|
StartReplay();
|
||||||
|
|
||||||
|
eventPtr.handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(m_RecordButtonPath))
|
||||||
|
{
|
||||||
|
var recordControl = InputControlPath.TryFindControl(device, m_RecordButtonPath) as InputControl<float>;
|
||||||
|
if (recordControl != null && recordControl.ReadValueFromEvent(eventPtr) >= InputSystem.settings.defaultButtonPressPoint)
|
||||||
|
{
|
||||||
|
if (captureIsRunning)
|
||||||
|
StopCapture();
|
||||||
|
else
|
||||||
|
StartCapture();
|
||||||
|
|
||||||
|
eventPtr.handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
protected void OnValidate()
|
||||||
|
{
|
||||||
|
if (m_EventTrace != null)
|
||||||
|
m_EventTrace.recordFrameMarkers = m_RecordFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[SerializeField] private bool m_StartRecordingWhenEnabled = false;
|
||||||
|
|
||||||
|
[Tooltip("If enabled, additional events will be recorded that demarcate frame boundaries. When replaying, this allows "
|
||||||
|
+ "spacing out input events across frames corresponding to the original distribution across frames when input was "
|
||||||
|
+ "recorded. If this is turned off, all input events will be queued in one block when replaying the trace.")]
|
||||||
|
[SerializeField] private bool m_RecordFrames = true;
|
||||||
|
|
||||||
|
[Tooltip("If enabled, new devices will be created for captured events when replaying them. If disabled (default), "
|
||||||
|
+ "events will be queued as is and thus keep their original device ID.")]
|
||||||
|
[SerializeField] private bool m_ReplayOnNewDevices;
|
||||||
|
|
||||||
|
[Tooltip("If enabled, the system will try to simulate the original event timing on replay. This differs from replaying frame "
|
||||||
|
+ "by frame in that replay will try to compensate for differences in frame timings and redistribute events to frames that "
|
||||||
|
+ "more closely match the original timing. Note that this is not perfect and will not necessarily create a 1:1 match.")]
|
||||||
|
[SerializeField] private bool m_SimulateOriginalTimingOnReplay;
|
||||||
|
|
||||||
|
[Tooltip("If enabled, only StateEvents and DeltaStateEvents will be captured.")]
|
||||||
|
[SerializeField] private bool m_RecordStateEventsOnly;
|
||||||
|
|
||||||
|
[SerializeField] private int m_CaptureMemoryDefaultSize = 2 * 1024 * 1024;
|
||||||
|
[SerializeField] private int m_CaptureMemoryMaxSize = 10 * 1024 * 1024;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
[InputControl(layout = "InputDevice")]
|
||||||
|
private string m_DevicePath;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
[InputControl(layout = "Button")]
|
||||||
|
private string m_RecordButtonPath;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
[InputControl(layout = "Button")]
|
||||||
|
private string m_PlayButtonPath;
|
||||||
|
|
||||||
|
[SerializeField] private ChangeEvent m_ChangeEvent;
|
||||||
|
|
||||||
|
private Action<InputEventPtr, InputDevice> m_OnInputEventDelegate;
|
||||||
|
private InputEventTrace m_EventTrace;
|
||||||
|
private InputEventTrace.ReplayController m_ReplayController;
|
||||||
|
|
||||||
|
private void CreateEventTrace()
|
||||||
|
{
|
||||||
|
////FIXME: remaining configuration should come through, too, if changed after the fact
|
||||||
|
if (m_EventTrace == null || m_EventTrace.maxSizeInBytes == 0)
|
||||||
|
{
|
||||||
|
m_EventTrace?.Dispose();
|
||||||
|
m_EventTrace = new InputEventTrace(m_CaptureMemoryDefaultSize, growBuffer: true, maxBufferSizeInBytes: m_CaptureMemoryMaxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_EventTrace.recordFrameMarkers = m_RecordFrames;
|
||||||
|
m_EventTrace.onFilterEvent += OnFilterInputEvent;
|
||||||
|
m_EventTrace.onEvent += OnEventRecorded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HookOnInputEvent()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(m_PlayButtonPath) && string.IsNullOrEmpty(m_RecordButtonPath))
|
||||||
|
{
|
||||||
|
UnhookOnInputEvent();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_OnInputEventDelegate == null)
|
||||||
|
m_OnInputEventDelegate = OnInputEvent;
|
||||||
|
InputSystem.onEvent += m_OnInputEventDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnhookOnInputEvent()
|
||||||
|
{
|
||||||
|
if (m_OnInputEventDelegate != null)
|
||||||
|
InputSystem.onEvent -= m_OnInputEventDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Change
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
EventCaptured,
|
||||||
|
EventPlayed,
|
||||||
|
CaptureStarted,
|
||||||
|
CaptureStopped,
|
||||||
|
ReplayStarted,
|
||||||
|
ReplayStopped,
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class ChangeEvent : UnityEvent<Change>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d35d5308cbb1fcd479dbf0c516db2aa7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,227 @@
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine.Events;
|
||||||
|
|
||||||
|
////TODO: add ability to inspect contents of event traces in a separate window
|
||||||
|
|
||||||
|
namespace UnityEngine.InputSystem.Editor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A custom inspector for <see cref="InputRecorder"/>. Adds UI elements to store captures in files, to load them from
|
||||||
|
/// there, and to initiate replays from within the editor. It also shows information for when captures or replays are
|
||||||
|
/// in progress.
|
||||||
|
/// </summary>
|
||||||
|
[CustomEditor(typeof(InputRecorder))]
|
||||||
|
internal class InputRecorderInspector : UnityEditor.Editor
|
||||||
|
{
|
||||||
|
protected void OnEnable()
|
||||||
|
{
|
||||||
|
m_DevicePathProperty = serializedObject.FindProperty("m_DevicePath");
|
||||||
|
m_RecordButtonPath = serializedObject.FindProperty("m_RecordButtonPath");
|
||||||
|
m_PlayButtonPathProperty = serializedObject.FindProperty("m_PlayButtonPath");
|
||||||
|
m_RecordFramesProperty = serializedObject.FindProperty("m_RecordFrames");
|
||||||
|
m_RecordStateEventsOnlyProperty = serializedObject.FindProperty("m_RecordStateEventsOnly");
|
||||||
|
m_ReplayOnNewDevicesProperty = serializedObject.FindProperty("m_ReplayOnNewDevices");
|
||||||
|
m_SimulateTimingOnReplayProperty = serializedObject.FindProperty("m_SimulateOriginalTimingOnReplay");
|
||||||
|
m_CaptureMemoryDefaultSizeProperty = serializedObject.FindProperty("m_CaptureMemoryDefaultSize");
|
||||||
|
m_CaptureMemoryMaxSizeProperty = serializedObject.FindProperty("m_CaptureMemoryMaxSize");
|
||||||
|
m_StartRecordingWhenEnabledProperty = serializedObject.FindProperty("m_StartRecordingWhenEnabled");
|
||||||
|
|
||||||
|
m_AllInput = string.IsNullOrEmpty(m_DevicePathProperty.stringValue);
|
||||||
|
|
||||||
|
m_PlayText = EditorGUIUtility.TrIconContent("PlayButton", "Play the current input capture.");
|
||||||
|
m_PauseText = EditorGUIUtility.TrIconContent("PauseButton", "Pause the current input playback.");
|
||||||
|
m_ResumeText = EditorGUIUtility.TrIconContent("PauseButton On", "Resume the current input playback.");
|
||||||
|
m_StepForwardText = EditorGUIUtility.TrIconContent("d_StepButton", "Play the next input event.");
|
||||||
|
m_StepBackwardText = EditorGUIUtility.TrIconContent("d_StepLeftButton", "Play the previous input event.");
|
||||||
|
m_StopText = EditorGUIUtility.TrIconContent("PlayButton On", "Stop the current input playback.");
|
||||||
|
m_RecordText = EditorGUIUtility.TrIconContent("Animation.Record", "Start recording input.");
|
||||||
|
|
||||||
|
var recorder = (InputRecorder)serializedObject.targetObject;
|
||||||
|
m_OnRecordEvent = _ => Repaint();
|
||||||
|
recorder.changeEvent.AddListener(m_OnRecordEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnDisable()
|
||||||
|
{
|
||||||
|
var recorder = (InputRecorder)serializedObject.targetObject;
|
||||||
|
recorder.changeEvent.RemoveListener(m_OnRecordEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
var recorder = (InputRecorder)serializedObject.targetObject;
|
||||||
|
|
||||||
|
using (var scope = new EditorGUI.ChangeCheckScope())
|
||||||
|
{
|
||||||
|
var newAllInput = EditorGUILayout.Toggle(m_AllInputText, m_AllInput);
|
||||||
|
if (!newAllInput)
|
||||||
|
{
|
||||||
|
using (new EditorGUI.IndentLevelScope())
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(m_DevicePathProperty, m_DeviceText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (newAllInput != m_AllInput)
|
||||||
|
{
|
||||||
|
m_DevicePathProperty.stringValue = string.Empty;
|
||||||
|
}
|
||||||
|
m_AllInput = newAllInput;
|
||||||
|
|
||||||
|
EditorGUILayout.PropertyField(m_RecordFramesProperty);
|
||||||
|
EditorGUILayout.PropertyField(m_RecordStateEventsOnlyProperty);
|
||||||
|
EditorGUILayout.PropertyField(m_ReplayOnNewDevicesProperty);
|
||||||
|
EditorGUILayout.PropertyField(m_SimulateTimingOnReplayProperty);
|
||||||
|
EditorGUILayout.PropertyField(m_StartRecordingWhenEnabledProperty, m_RecordWhenEnabledText);
|
||||||
|
|
||||||
|
var defaultSizeInMB = m_CaptureMemoryDefaultSizeProperty.intValue / (1024 * 1024);
|
||||||
|
var newDefaultSizeInMB = EditorGUILayout.IntSlider(m_DefaultSizeText, defaultSizeInMB, 1, 100);
|
||||||
|
if (newDefaultSizeInMB != defaultSizeInMB)
|
||||||
|
m_CaptureMemoryDefaultSizeProperty.intValue = newDefaultSizeInMB * 1024 * 1024;
|
||||||
|
|
||||||
|
var maxSizeInMB = m_CaptureMemoryMaxSizeProperty.intValue / (1024 * 1024);
|
||||||
|
var newMaxSizeInMB = maxSizeInMB < newDefaultSizeInMB
|
||||||
|
? newDefaultSizeInMB
|
||||||
|
: EditorGUILayout.IntSlider(m_MaxSizeText, maxSizeInMB, 1, 100);
|
||||||
|
if (newMaxSizeInMB != maxSizeInMB)
|
||||||
|
m_CaptureMemoryMaxSizeProperty.intValue = newMaxSizeInMB * 1024 * 1024;
|
||||||
|
|
||||||
|
EditorGUILayout.PropertyField(m_RecordButtonPath, m_RecordButtonText);
|
||||||
|
EditorGUILayout.PropertyField(m_PlayButtonPathProperty, m_PlayButtonText);
|
||||||
|
|
||||||
|
if (scope.changed)
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
using (new EditorGUILayout.HorizontalScope())
|
||||||
|
{
|
||||||
|
////TODO: go-to-next and go-to-previous button
|
||||||
|
// Play and pause buttons.
|
||||||
|
EditorGUI.BeginDisabledGroup(recorder.eventCount == 0 || recorder.captureIsRunning);
|
||||||
|
var oldIsPlaying = recorder.replayIsRunning;
|
||||||
|
var newIsPlaying = GUILayout.Toggle(oldIsPlaying, !oldIsPlaying ? m_PlayText : m_StopText, EditorStyles.miniButton,
|
||||||
|
GUILayout.Width(50));
|
||||||
|
if (oldIsPlaying != newIsPlaying)
|
||||||
|
{
|
||||||
|
if (newIsPlaying)
|
||||||
|
recorder.StartReplay();
|
||||||
|
else
|
||||||
|
recorder.StopReplay();
|
||||||
|
}
|
||||||
|
if (newIsPlaying && recorder.replay != null && GUILayout.Button(recorder.replay.paused ? m_ResumeText : m_PauseText, EditorStyles.miniButton,
|
||||||
|
GUILayout.Width(50)))
|
||||||
|
{
|
||||||
|
if (recorder.replay.paused)
|
||||||
|
recorder.StartReplay();
|
||||||
|
else
|
||||||
|
recorder.PauseReplay();
|
||||||
|
}
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
|
||||||
|
// Record button.
|
||||||
|
EditorGUI.BeginDisabledGroup(recorder.replayIsRunning);
|
||||||
|
var oldIsRecording = recorder.captureIsRunning;
|
||||||
|
var newIsRecording = GUILayout.Toggle(oldIsRecording, m_RecordText, EditorStyles.miniButton, GUILayout.Width(50));
|
||||||
|
if (oldIsRecording != newIsRecording)
|
||||||
|
{
|
||||||
|
if (newIsRecording)
|
||||||
|
recorder.StartCapture();
|
||||||
|
else
|
||||||
|
recorder.StopCapture();
|
||||||
|
}
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
|
||||||
|
// Load button.
|
||||||
|
EditorGUI.BeginDisabledGroup(recorder.replayIsRunning);
|
||||||
|
if (GUILayout.Button("Load"))
|
||||||
|
{
|
||||||
|
var filePath = EditorUtility.OpenFilePanel("Choose Input Event Trace to Load", string.Empty, "inputtrace");
|
||||||
|
if (!string.IsNullOrEmpty(filePath))
|
||||||
|
recorder.LoadCaptureFromFile(filePath);
|
||||||
|
}
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
|
||||||
|
// Save button.
|
||||||
|
EditorGUI.BeginDisabledGroup(recorder.eventCount == 0 || recorder.replayIsRunning);
|
||||||
|
if (GUILayout.Button("Save"))
|
||||||
|
{
|
||||||
|
var filePath = EditorUtility.SaveFilePanel("Choose Where to Save Input Event Trace", string.Empty, $"{recorder.gameObject.name}.inputtrace", "inputtrace");
|
||||||
|
if (!string.IsNullOrEmpty(filePath))
|
||||||
|
recorder.SaveCaptureToFile(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear button.
|
||||||
|
if (GUILayout.Button("Clear"))
|
||||||
|
{
|
||||||
|
recorder.ClearCapture();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
////TODO: allow hotscrubbing
|
||||||
|
// Play bar.
|
||||||
|
EditorGUILayout.IntSlider(recorder.replayPosition, 0, (int)recorder.eventCount);
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
using (new EditorGUI.DisabledScope())
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField(m_InfoText, EditorStyles.miniBoldLabel);
|
||||||
|
using (new EditorGUI.IndentLevelScope())
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField($"{recorder.eventCount} events", EditorStyles.miniLabel);
|
||||||
|
EditorGUILayout.LabelField($"{recorder.totalEventSizeInBytes / 1024} kb captured", EditorStyles.miniLabel);
|
||||||
|
EditorGUILayout.LabelField($"{recorder.allocatedSizeInBytes / 1024} kb allocated", EditorStyles.miniLabel);
|
||||||
|
|
||||||
|
if (recorder.capture != null)
|
||||||
|
{
|
||||||
|
var devices = recorder.capture.deviceInfos;
|
||||||
|
if (devices.Count > 0)
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField(m_DevicesText, EditorStyles.miniBoldLabel);
|
||||||
|
using (new EditorGUI.IndentLevelScope())
|
||||||
|
{
|
||||||
|
foreach (var device in devices)
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField(device.layout, EditorStyles.miniLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool m_AllInput;
|
||||||
|
private SerializedProperty m_DevicePathProperty;
|
||||||
|
private SerializedProperty m_RecordButtonPath;
|
||||||
|
private SerializedProperty m_PlayButtonPathProperty;
|
||||||
|
private SerializedProperty m_RecordFramesProperty;
|
||||||
|
private SerializedProperty m_RecordStateEventsOnlyProperty;
|
||||||
|
private SerializedProperty m_ReplayOnNewDevicesProperty;
|
||||||
|
private SerializedProperty m_SimulateTimingOnReplayProperty;
|
||||||
|
private SerializedProperty m_CaptureMemoryDefaultSizeProperty;
|
||||||
|
private SerializedProperty m_CaptureMemoryMaxSizeProperty;
|
||||||
|
private SerializedProperty m_StartRecordingWhenEnabledProperty;
|
||||||
|
private UnityAction<InputRecorder.Change> m_OnRecordEvent;
|
||||||
|
|
||||||
|
private GUIContent m_RecordButtonText = new GUIContent("Record Button", "If set, this button will start and stop capture in play mode.");
|
||||||
|
private GUIContent m_PlayButtonText = new GUIContent("Play Button", "If set, this button will start and stop replay of the current capture in play mode.");
|
||||||
|
private GUIContent m_RecordWhenEnabledText = new GUIContent("Capture When Enabled", "If true, recording will start immediately when the component is enabled in play mode.");
|
||||||
|
private GUIContent m_DevicesText = new GUIContent("Devices");
|
||||||
|
private GUIContent m_AllInputText = new GUIContent("All Input", "Whether to record input from all devices or from just specific devices.");
|
||||||
|
private GUIContent m_DeviceText = new GUIContent("Device", "Type of device to record input from.");
|
||||||
|
private GUIContent m_InfoText = new GUIContent("Info:");
|
||||||
|
private GUIContent m_DefaultSizeText = new GUIContent("Default Size (MB)", "Memory allocate for capture by default. Will automatically grow up to max memory.");
|
||||||
|
private GUIContent m_MaxSizeText = new GUIContent("Max Size (MB)", "Maximum memory allocated for capture. Once a capture reaches this limit, new events will start overwriting old ones.");
|
||||||
|
private GUIContent m_PlayText;
|
||||||
|
private GUIContent m_PauseText;
|
||||||
|
private GUIContent m_ResumeText;
|
||||||
|
private GUIContent m_StepForwardText;
|
||||||
|
private GUIContent m_StepBackwardText;
|
||||||
|
private GUIContent m_StopText;
|
||||||
|
private GUIContent m_RecordText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0bb726b4db024f738c493b8132730571
|
||||||
|
timeCreated: 1574087726
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: da1a360c18546f34fac39b2c305ac2c9
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,3 @@
|
||||||
|
This sample is both a demonstration for how to use [`InputEventTrace`](https://docs.unity3d.com/Packages/com.unity.inputsystem@latest/index.html?subfolder=/api/UnityEngine.InputSystem.LowLevel.InputEventTrace.html) as well as a useful tool by itself in the form of the [`InputRecorder`](./InputRecorder.cs) reusable `MonoBehaviour` component.
|
||||||
|
|
||||||
|
One possible way in which you can use this facility, for example, is to record input, save it to disk, and then replay the same input in automation (e.g. in tests or when recording short video snippets of preset gameplay sequences for manual proofing).
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c7efc8d27cb74c05a04933d66f7f8062
|
||||||
|
timeCreated: 1574262802
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "Unity.InputSystem.Recorder",
|
||||||
|
"references": [
|
||||||
|
"GUID:75469ad4d38634e559750d17036d5f7c"
|
||||||
|
],
|
||||||
|
"optionalUnityReferences": [],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": true,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": []
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 157556569ac93a94fb6a763f50476339
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -38,7 +38,7 @@ RenderSettings:
|
||||||
m_ReflectionIntensity: 1
|
m_ReflectionIntensity: 1
|
||||||
m_CustomReflection: {fileID: 0}
|
m_CustomReflection: {fileID: 0}
|
||||||
m_Sun: {fileID: 0}
|
m_Sun: {fileID: 0}
|
||||||
m_IndirectSpecularColor: {r: 0.21890283, g: 0.16790575, b: 0.67034173, a: 1}
|
m_IndirectSpecularColor: {r: 0.21890257, g: 0.16790554, b: 0.67034054, a: 1}
|
||||||
m_UseRadianceAmbientProbe: 0
|
m_UseRadianceAmbientProbe: 0
|
||||||
--- !u!157 &3
|
--- !u!157 &3
|
||||||
LightmapSettings:
|
LightmapSettings:
|
||||||
|
@ -132,7 +132,7 @@ PrefabInstance:
|
||||||
m_Modifications:
|
m_Modifications:
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_RootOrder
|
propertyPath: m_RootOrder
|
||||||
value: 8
|
value: 9
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
|
@ -236,7 +236,7 @@ PrefabInstance:
|
||||||
m_Modifications:
|
m_Modifications:
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_RootOrder
|
propertyPath: m_RootOrder
|
||||||
value: 7
|
value: 8
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
|
@ -472,7 +472,7 @@ PrefabInstance:
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 8944336655422409503, guid: f6d148d888ffbf54b9afe9936dfaec1f, type: 3}
|
- target: {fileID: 8944336655422409503, guid: f6d148d888ffbf54b9afe9936dfaec1f, type: 3}
|
||||||
propertyPath: m_RootOrder
|
propertyPath: m_RootOrder
|
||||||
value: 17
|
value: 18
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 8944336655422409503, guid: f6d148d888ffbf54b9afe9936dfaec1f, type: 3}
|
- target: {fileID: 8944336655422409503, guid: f6d148d888ffbf54b9afe9936dfaec1f, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
|
@ -516,6 +516,50 @@ PrefabInstance:
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
m_RemovedComponents: []
|
m_RemovedComponents: []
|
||||||
m_SourcePrefab: {fileID: 100100000, guid: f6d148d888ffbf54b9afe9936dfaec1f, type: 3}
|
m_SourcePrefab: {fileID: 100100000, guid: f6d148d888ffbf54b9afe9936dfaec1f, type: 3}
|
||||||
|
--- !u!1 &728378993
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 728378995}
|
||||||
|
- component: {fileID: 728378994}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: ApplicationController
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &728378994
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 728378993}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 6ae728ca564c42b8a6a2ee763f7a2741, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
--- !u!4 &728378995
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 728378993}
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 6.0964007, y: -0.3416402, z: -0.77773607}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_RootOrder: 0
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1 &796975752
|
--- !u!1 &796975752
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
@ -556,7 +600,7 @@ PrefabInstance:
|
||||||
m_Modifications:
|
m_Modifications:
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_RootOrder
|
propertyPath: m_RootOrder
|
||||||
value: 6
|
value: 7
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
|
@ -665,7 +709,7 @@ Transform:
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_RootOrder: 12
|
m_RootOrder: 13
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1 &986711052 stripped
|
--- !u!1 &986711052 stripped
|
||||||
GameObject:
|
GameObject:
|
||||||
|
@ -732,7 +776,7 @@ Transform:
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_RootOrder: 1
|
m_RootOrder: 2
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!114 &1038740740
|
--- !u!114 &1038740740
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
|
@ -861,7 +905,7 @@ Transform:
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_RootOrder: 0
|
m_RootOrder: 1
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1 &1265101692
|
--- !u!1 &1265101692
|
||||||
GameObject:
|
GameObject:
|
||||||
|
@ -976,7 +1020,7 @@ Transform:
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_RootOrder: 15
|
m_RootOrder: 16
|
||||||
m_LocalEulerAnglesHint: {x: 41.363, y: 45, z: 0}
|
m_LocalEulerAnglesHint: {x: 41.363, y: 45, z: 0}
|
||||||
--- !u!1 &1299635453
|
--- !u!1 &1299635453
|
||||||
GameObject:
|
GameObject:
|
||||||
|
@ -1037,7 +1081,7 @@ Transform:
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_RootOrder: 4
|
m_RootOrder: 5
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1 &1368949943 stripped
|
--- !u!1 &1368949943 stripped
|
||||||
GameObject:
|
GameObject:
|
||||||
|
@ -1071,7 +1115,7 @@ PrefabInstance:
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 2070925441746177912, guid: a1a802ecaf6775746bb2a929fb554ad8, type: 3}
|
- target: {fileID: 2070925441746177912, guid: a1a802ecaf6775746bb2a929fb554ad8, type: 3}
|
||||||
propertyPath: m_RootOrder
|
propertyPath: m_RootOrder
|
||||||
value: 18
|
value: 19
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 2070925441746177912, guid: a1a802ecaf6775746bb2a929fb554ad8, type: 3}
|
- target: {fileID: 2070925441746177912, guid: a1a802ecaf6775746bb2a929fb554ad8, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
|
@ -1186,7 +1230,7 @@ PrefabInstance:
|
||||||
m_Modifications:
|
m_Modifications:
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_RootOrder
|
propertyPath: m_RootOrder
|
||||||
value: 9
|
value: 10
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
|
@ -1343,7 +1387,7 @@ Transform:
|
||||||
- {fileID: 1997149794}
|
- {fileID: 1997149794}
|
||||||
- {fileID: 474653828}
|
- {fileID: 474653828}
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_RootOrder: 16
|
m_RootOrder: 17
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1001 &1869134603
|
--- !u!1001 &1869134603
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
|
@ -1354,7 +1398,7 @@ PrefabInstance:
|
||||||
m_Modifications:
|
m_Modifications:
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_RootOrder
|
propertyPath: m_RootOrder
|
||||||
value: 11
|
value: 12
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
|
@ -1415,7 +1459,7 @@ PrefabInstance:
|
||||||
m_Modifications:
|
m_Modifications:
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_RootOrder
|
propertyPath: m_RootOrder
|
||||||
value: 10
|
value: 11
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
|
@ -1553,7 +1597,7 @@ Transform:
|
||||||
- {fileID: 1991538230}
|
- {fileID: 1991538230}
|
||||||
- {fileID: 1773263767}
|
- {fileID: 1773263767}
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_RootOrder: 3
|
m_RootOrder: 4
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1 &1991538229
|
--- !u!1 &1991538229
|
||||||
GameObject:
|
GameObject:
|
||||||
|
@ -1706,7 +1750,7 @@ Transform:
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_RootOrder: 14
|
m_RootOrder: 15
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1 &2060465721
|
--- !u!1 &2060465721
|
||||||
GameObject:
|
GameObject:
|
||||||
|
@ -1779,7 +1823,7 @@ Transform:
|
||||||
- {fileID: 1422708176}
|
- {fileID: 1422708176}
|
||||||
- {fileID: 796975753}
|
- {fileID: 796975753}
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_RootOrder: 2
|
m_RootOrder: 3
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1001 &330446109092541879
|
--- !u!1001 &330446109092541879
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
|
@ -1838,7 +1882,7 @@ PrefabInstance:
|
||||||
objectReference: {fileID: 2100000, guid: 80a1e6a5d1689444c8e2994a51639e98, type: 2}
|
objectReference: {fileID: 2100000, guid: 80a1e6a5d1689444c8e2994a51639e98, type: 2}
|
||||||
- target: {fileID: 330446108690218393, guid: d793abe7ff9aa094eb534e73a82fdab5, type: 3}
|
- target: {fileID: 330446108690218393, guid: d793abe7ff9aa094eb534e73a82fdab5, type: 3}
|
||||||
propertyPath: m_RootOrder
|
propertyPath: m_RootOrder
|
||||||
value: 13
|
value: 14
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 330446108690218393, guid: d793abe7ff9aa094eb534e73a82fdab5, type: 3}
|
- target: {fileID: 330446108690218393, guid: d793abe7ff9aa094eb534e73a82fdab5, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
|
@ -2031,7 +2075,7 @@ PrefabInstance:
|
||||||
m_Modifications:
|
m_Modifications:
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_RootOrder
|
propertyPath: m_RootOrder
|
||||||
value: 5
|
value: 6
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
- target: {fileID: 4621998539424734916, guid: a6b33b41508134c09957e076f4d53415, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Samples
|
||||||
|
{
|
||||||
|
public class ApplicationController : MonoBehaviour
|
||||||
|
{
|
||||||
|
void Awake()
|
||||||
|
{
|
||||||
|
DontDestroyOnLoad(gameObject);
|
||||||
|
Application.runInBackground = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6ae728ca564c42b8a6a2ee763f7a2741
|
||||||
|
timeCreated: 1673282028
|
|
@ -17,7 +17,7 @@ MonoBehaviour:
|
||||||
m_MaxEventBytesPerUpdate: 5242880
|
m_MaxEventBytesPerUpdate: 5242880
|
||||||
m_MaxQueuedEventsPerUpdate: 1000
|
m_MaxQueuedEventsPerUpdate: 1000
|
||||||
m_CompensateForScreenOrientation: 0
|
m_CompensateForScreenOrientation: 0
|
||||||
m_BackgroundBehavior: 1
|
m_BackgroundBehavior: 2
|
||||||
m_EditorInputBehaviorInPlayMode: 0
|
m_EditorInputBehaviorInPlayMode: 0
|
||||||
m_DefaultDeadzoneMin: 0.125
|
m_DefaultDeadzoneMin: 0.125
|
||||||
m_DefaultDeadzoneMax: 0.925
|
m_DefaultDeadzoneMax: 0.925
|
||||||
|
@ -29,6 +29,7 @@ MonoBehaviour:
|
||||||
m_TapRadius: 5
|
m_TapRadius: 5
|
||||||
m_MultiTapDelayTime: 0.75
|
m_MultiTapDelayTime: 0.75
|
||||||
m_DisableRedundantEventsMerging: 0
|
m_DisableRedundantEventsMerging: 0
|
||||||
|
m_ShortcutKeysConsumeInputs: 0
|
||||||
m_iOSSettings:
|
m_iOSSettings:
|
||||||
m_MotionUsage:
|
m_MotionUsage:
|
||||||
m_Enabled: 0
|
m_Enabled: 0
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"MonoBehaviour": {
|
||||||
|
"Version": 4,
|
||||||
|
"EnableBurstCompilation": true,
|
||||||
|
"EnableOptimisations": true,
|
||||||
|
"EnableSafetyChecks": false,
|
||||||
|
"EnableDebugInAllBuilds": false,
|
||||||
|
"UsePlatformSDKLinker": false,
|
||||||
|
"CpuMinTargetX32": 0,
|
||||||
|
"CpuMaxTargetX32": 0,
|
||||||
|
"CpuMinTargetX64": 0,
|
||||||
|
"CpuMaxTargetX64": 0,
|
||||||
|
"CpuTargetsX64": 72,
|
||||||
|
"OptimizeFor": 0
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче