├── .gitignore ├── CHANGELOG.md ├── CHANGELOG.md.meta ├── Editor.meta ├── Editor ├── InputVCR-Editor.asmdef ├── InputVCR-Editor.asmdef.meta ├── InputVCRRecorderEditor.cs └── InputVCRRecorderEditor.cs.meta ├── LICENSE.md ├── LICENSE.md.meta ├── Runtime.meta ├── Runtime ├── Examples.meta ├── Examples │ ├── DemoSideToSideRecording.txt │ ├── DemoSideToSideRecording.txt.meta │ ├── InputVCR-Examples.asmdef │ ├── InputVCR-Examples.asmdef.meta │ ├── InputVCRExample.unity │ ├── InputVCRExample.unity.meta │ ├── Roboto-Regular.ttf │ ├── Roboto-Regular.ttf.meta │ ├── Scripts.meta │ └── Scripts │ │ ├── CharacterController2D.cs │ │ ├── CharacterController2D.cs.meta │ │ ├── VCRControls.cs │ │ └── VCRControls.cs.meta ├── Scripts.meta └── Scripts │ ├── InputVCR.asmdef │ ├── InputVCR.asmdef.meta │ ├── InputVCRRecorder.cs │ ├── InputVCRRecorder.cs.meta │ ├── InputVCRTextRecordingLoader.cs │ ├── InputVCRTextRecordingLoader.cs.meta │ ├── InputVCRTransformSyncer.cs │ ├── InputVCRTransformSyncer.cs.meta │ ├── KeycodeHelper.cs │ ├── KeycodeHelper.cs.meta │ ├── Recording.cs │ └── Recording.cs.meta ├── package.json ├── package.json.meta ├── readme.md └── readme.txt.meta /.gitignore: -------------------------------------------------------------------------------- 1 | Library 2 | *.sln 3 | *.csproj 4 | *.pidb 5 | *.unityproj 6 | Temp 7 | *.pdb 8 | Builds/OSX 9 | Builds/Win 10 | Asset Store 11 | Assets/AssetStoreTools 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Changelog 2 | 3 | 4 | - v1.0 5 | Initial release 6 | 7 | - v1.1 8 | Added option to sync position/rotation 9 | Added check to make sure recording stays close to realtime (in case recording/playback frame rates are different) 10 | More robust Recording format 11 | 12 | - v1.2 _MAJOR UPDATE. INCOMPATIBLE WITH OLDER VERSIONS! (but way easier to use) 13 | Switched to JSON storage and parsing. WAAAAAAAAAAAY more reliable than mine 14 | Recording and playback now completely framerate independent 15 | General cleanup of code + commenting and other help -------------------------------------------------------------------------------- /CHANGELOG.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ca0cc3e3e6f6543a8b881655c5f8e95b 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4c2aa0d7e891d4a46afb0e7127e022ee 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/InputVCR-Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "InputVCR-Editor", 3 | "references": [ 4 | "GUID:dab65278143d24f128fdccc4cf61752a" 5 | ], 6 | "includePlatforms": [], 7 | "excludePlatforms": [], 8 | "allowUnsafeCode": false, 9 | "overrideReferences": false, 10 | "precompiledReferences": [], 11 | "autoReferenced": true, 12 | "defineConstraints": [], 13 | "versionDefines": [] 14 | } -------------------------------------------------------------------------------- /Editor/InputVCR-Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 18bdea58642464e2ba9e76a756989662 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/InputVCRRecorderEditor.cs: -------------------------------------------------------------------------------- 1 | /* InputVCRRecorderEditor.cs 2 | * Copyright Eddie Cameron 2019 (See readme for licence) 3 | * ---------- 4 | */ 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using UnityEngine; 8 | using UnityEditor; 9 | using InputVCR; 10 | using UnityEngine.UIElements; 11 | using System; 12 | using System.IO; 13 | 14 | namespace InputVCREditor { 15 | [CustomEditor( typeof( InputVCRRecorder ) )] 16 | public class InputVCRRecorderEditor : Editor { 17 | 18 | public override void OnInspectorGUI() { 19 | base.OnInspectorGUI(); 20 | 21 | var recorder = (InputVCRRecorder)target; 22 | if ( EditorApplication.isPlaying ) { 23 | InputVCRMode recordMode = recorder.Mode; 24 | 25 | // record controls 26 | EditorGUILayout.LabelField( "Controls", EditorStyles.boldLabel ); 27 | 28 | // controls 29 | using ( var playGroup = new EditorGUILayout.HorizontalScope() ) { 30 | bool playEnabled = recordMode != InputVCRMode.Playback || recorder.IsPaused; 31 | GUI.enabled = playEnabled; 32 | if ( GUILayout.Button( "PLAY >", EditorStyles.miniButtonLeft ) ) 33 | recorder.Play(); 34 | 35 | bool pauseEnabled = recordMode != InputVCRMode.Passthru && !recorder.IsPaused; 36 | GUI.enabled = pauseEnabled; 37 | if ( GUILayout.Button( "PAUSE ||", EditorStyles.miniButtonMid ) ) { 38 | recorder.Pause(); 39 | } 40 | GUI.enabled = true; 41 | if ( GUILayout.Button( "REWIND <<", EditorStyles.miniButtonRight ) ) { 42 | recorder.RewindToStart(); 43 | } 44 | } 45 | 46 | using ( var recordGroup = new EditorGUILayout.HorizontalScope() ) { 47 | bool recordEnabled = recordMode != InputVCRMode.Record; 48 | GUI.enabled = recordEnabled; 49 | if ( GUILayout.Button( "RECORD O", EditorStyles.miniButtonLeft ) ) 50 | recorder.Record(); 51 | 52 | 53 | bool stopEnabled = recordMode != InputVCRMode.Passthru; 54 | GUI.enabled = stopEnabled; 55 | if ( GUILayout.Button( "STOP []", EditorStyles.miniButtonRight ) ) 56 | recorder.Stop(); 57 | } 58 | GUI.enabled = true; 59 | 60 | var currentRecording = recorder.CurrentRecording; 61 | if ( recorder.Mode == InputVCRMode.Record ) { 62 | EditorGUILayout.LabelField( "Recording", EditorStyles.boldLabel ); 63 | EditorGUILayout.LabelField( "Length: " + recorder.CurrentPlaybackTime.ToString( "f2" ) ); 64 | } 65 | else { 66 | if ( recorder.Mode == InputVCRMode.Playback ) 67 | EditorGUILayout.LabelField( "Playing", EditorStyles.boldLabel ); 68 | else 69 | EditorGUILayout.LabelField( "Stopped", EditorStyles.boldLabel ); 70 | 71 | float time = recorder.CurrentPlaybackTime; 72 | float length = currentRecording.Length; 73 | EditorGUILayout.LabelField( "Length: " + length.ToString( "f2" ) ); 74 | 75 | GUI.enabled = false; 76 | EditorGUILayout.Slider( time, 0, length, GUILayout.ExpandWidth( true ) ); 77 | GUI.enabled = true; 78 | } 79 | 80 | // recording save 81 | if ( currentRecording != null ) { 82 | if ( GUILayout.Button( "Save Recording" ) ) { 83 | string recordingName = $"VCRRecord_{DateTime.Now:yy-MM-dd_HHmmss}"; 84 | string path = EditorUtility.SaveFilePanelInProject( "Save Recording", recordingName, "txt", "Save current recording to disk as JSON" ); 85 | if ( !string.IsNullOrEmpty( path ) ) { 86 | string json = currentRecording.ToJson(); 87 | try { 88 | File.WriteAllText( path, json ); 89 | AssetDatabase.Refresh(); 90 | } 91 | catch ( Exception e ) { 92 | Exception error = new Exception( "Failed to write recording to disk", e ); 93 | Debug.LogException( e ); 94 | } 95 | } 96 | } 97 | } 98 | } 99 | 100 | // Recording load 101 | if ( GUILayout.Button( "Load Recording" ) ) { 102 | string jsonPath = EditorUtility.OpenFilePanel( "Load Recording", Application.dataPath, "txt" ); 103 | if ( !string.IsNullOrEmpty( jsonPath ) ) { 104 | try { 105 | string recordJson = File.ReadAllText( jsonPath ); 106 | Recording r = new Recording( recordJson ); 107 | recorder.LoadRecording( r ); 108 | } 109 | catch ( Exception e ) { 110 | Exception error = new Exception( "Failed to load recording from disk", e ); 111 | Debug.LogException( e ); 112 | } 113 | } 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Editor/InputVCRRecorderEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4dd679339d41b4fb7837e7a9c1957be9 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The InputVCR.cs & Recording.cs scripts are open source under the MIT licence. Do what you will with them! 2 | LitJson is in the public domain 3 | 4 | Copyright (C) 2013 Eddie Cameron 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 91e36be411c7942c6b74d4c713afac95 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 69c29564dcf584d7681c5b367864323c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Examples.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 79515638a07d5404c922acb4c0c862db 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Examples/DemoSideToSideRecording.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e1da68cf15726402e83c2844e721d634 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Examples/InputVCR-Examples.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "InputVCR-Examples", 3 | "references": [ 4 | "GUID:dab65278143d24f128fdccc4cf61752a" 5 | ], 6 | "includePlatforms": [], 7 | "excludePlatforms": [], 8 | "allowUnsafeCode": false, 9 | "overrideReferences": false, 10 | "precompiledReferences": [], 11 | "autoReferenced": false, 12 | "defineConstraints": [], 13 | "versionDefines": [] 14 | } -------------------------------------------------------------------------------- /Runtime/Examples/InputVCR-Examples.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1b4c781d93576435c8df516a06fc2cc5 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Examples/InputVCRExample.unity: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!29 &1 4 | OcclusionCullingSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_OcclusionBakeSettings: 8 | smallestOccluder: 5 9 | smallestHole: 0.25 10 | backfaceThreshold: 100 11 | m_SceneGUID: 00000000000000000000000000000000 12 | m_OcclusionCullingData: {fileID: 0} 13 | --- !u!104 &2 14 | RenderSettings: 15 | m_ObjectHideFlags: 0 16 | serializedVersion: 9 17 | m_Fog: 0 18 | m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} 19 | m_FogMode: 3 20 | m_FogDensity: 0.01 21 | m_LinearFogStart: 0 22 | m_LinearFogEnd: 300 23 | m_AmbientSkyColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} 24 | m_AmbientEquatorColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} 25 | m_AmbientGroundColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} 26 | m_AmbientIntensity: 1 27 | m_AmbientMode: 3 28 | m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} 29 | m_SkyboxMaterial: {fileID: 0} 30 | m_HaloStrength: 0.5 31 | m_FlareStrength: 1 32 | m_FlareFadeSpeed: 3 33 | m_HaloTexture: {fileID: 0} 34 | m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} 35 | m_DefaultReflectionMode: 0 36 | m_DefaultReflectionResolution: 128 37 | m_ReflectionBounces: 1 38 | m_ReflectionIntensity: 1 39 | m_CustomReflection: {fileID: 0} 40 | m_Sun: {fileID: 0} 41 | m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} 42 | m_UseRadianceAmbientProbe: 0 43 | --- !u!157 &4 44 | LightmapSettings: 45 | m_ObjectHideFlags: 0 46 | serializedVersion: 11 47 | m_GIWorkflowMode: 1 48 | m_GISettings: 49 | serializedVersion: 2 50 | m_BounceScale: 1 51 | m_IndirectOutputScale: 1 52 | m_AlbedoBoost: 1 53 | m_EnvironmentLightingMode: 0 54 | m_EnableBakedLightmaps: 1 55 | m_EnableRealtimeLightmaps: 0 56 | m_LightmapEditorSettings: 57 | serializedVersion: 12 58 | m_Resolution: 1 59 | m_BakeResolution: 50 60 | m_AtlasSize: 1024 61 | m_AO: 1 62 | m_AOMaxDistance: 1 63 | m_CompAOExponent: 1 64 | m_CompAOExponentDirect: 0 65 | m_ExtractAmbientOcclusion: 0 66 | m_Padding: 2 67 | m_LightmapParameters: {fileID: 0} 68 | m_LightmapsBakeMode: 1 69 | m_TextureCompression: 0 70 | m_FinalGather: 0 71 | m_FinalGatherFiltering: 1 72 | m_FinalGatherRayCount: 256 73 | m_ReflectionCompression: 2 74 | m_MixedBakeMode: 1 75 | m_BakeBackend: 0 76 | m_PVRSampling: 1 77 | m_PVRDirectSampleCount: 32 78 | m_PVRSampleCount: 512 79 | m_PVRBounces: 2 80 | m_PVREnvironmentSampleCount: 512 81 | m_PVREnvironmentReferencePointCount: 2048 82 | m_PVRFilteringMode: 0 83 | m_PVRDenoiserTypeDirect: 0 84 | m_PVRDenoiserTypeIndirect: 0 85 | m_PVRDenoiserTypeAO: 0 86 | m_PVRFilterTypeDirect: 0 87 | m_PVRFilterTypeIndirect: 0 88 | m_PVRFilterTypeAO: 0 89 | m_PVREnvironmentMIS: 0 90 | m_PVRCulling: 1 91 | m_PVRFilteringGaussRadiusDirect: 1 92 | m_PVRFilteringGaussRadiusIndirect: 5 93 | m_PVRFilteringGaussRadiusAO: 2 94 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5 95 | m_PVRFilteringAtrousPositionSigmaIndirect: 2 96 | m_PVRFilteringAtrousPositionSigmaAO: 1 97 | m_ExportTrainingData: 0 98 | m_TrainingDataDestination: TrainingData 99 | m_LightingDataAsset: {fileID: 0} 100 | m_UseShadowmask: 0 101 | --- !u!196 &5 102 | NavMeshSettings: 103 | serializedVersion: 2 104 | m_ObjectHideFlags: 0 105 | m_BuildSettings: 106 | serializedVersion: 2 107 | agentTypeID: 0 108 | agentRadius: 0.5 109 | agentHeight: 2 110 | agentSlope: 45 111 | agentClimb: 0.4 112 | ledgeDropHeight: 0 113 | maxJumpAcrossDistance: 0 114 | minRegionArea: 2 115 | manualCellSize: 0 116 | cellSize: 0.16666666 117 | manualTileSize: 0 118 | tileSize: 256 119 | accuratePlacement: 0 120 | debug: 121 | m_Flags: 0 122 | m_NavMeshData: {fileID: 0} 123 | --- !u!1 &91943494 124 | GameObject: 125 | m_ObjectHideFlags: 0 126 | m_CorrespondingSourceObject: {fileID: 0} 127 | m_PrefabInstance: {fileID: 0} 128 | m_PrefabAsset: {fileID: 0} 129 | serializedVersion: 6 130 | m_Component: 131 | - component: {fileID: 91943495} 132 | - component: {fileID: 91943497} 133 | - component: {fileID: 91943496} 134 | m_Layer: 5 135 | m_Name: TimeText 136 | m_TagString: Untagged 137 | m_Icon: {fileID: 0} 138 | m_NavMeshLayer: 0 139 | m_StaticEditorFlags: 0 140 | m_IsActive: 1 141 | --- !u!224 &91943495 142 | RectTransform: 143 | m_ObjectHideFlags: 0 144 | m_CorrespondingSourceObject: {fileID: 0} 145 | m_PrefabInstance: {fileID: 0} 146 | m_PrefabAsset: {fileID: 0} 147 | m_GameObject: {fileID: 91943494} 148 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 149 | m_LocalPosition: {x: 0, y: 0, z: 0} 150 | m_LocalScale: {x: 1, y: 1, z: 1} 151 | m_Children: [] 152 | m_Father: {fileID: 174072919} 153 | m_RootOrder: 4 154 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 155 | m_AnchorMin: {x: 1, y: 0} 156 | m_AnchorMax: {x: 1, y: 0} 157 | m_AnchoredPosition: {x: 0, y: 0} 158 | m_SizeDelta: {x: 165.05, y: 62} 159 | m_Pivot: {x: 1, y: 0} 160 | --- !u!114 &91943496 161 | MonoBehaviour: 162 | m_ObjectHideFlags: 0 163 | m_CorrespondingSourceObject: {fileID: 0} 164 | m_PrefabInstance: {fileID: 0} 165 | m_PrefabAsset: {fileID: 0} 166 | m_GameObject: {fileID: 91943494} 167 | m_Enabled: 1 168 | m_EditorHideFlags: 0 169 | m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} 170 | m_Name: 171 | m_EditorClassIdentifier: 172 | m_Material: {fileID: 0} 173 | m_Color: {r: 0.12259702, g: 0.12885468, b: 0.1792453, a: 1} 174 | m_RaycastTarget: 1 175 | m_OnCullStateChanged: 176 | m_PersistentCalls: 177 | m_Calls: [] 178 | m_FontData: 179 | m_Font: {fileID: 12800000, guid: e506d2e857d294d99966099453ec9116, type: 3} 180 | m_FontSize: 24 181 | m_FontStyle: 0 182 | m_BestFit: 0 183 | m_MinSize: 2 184 | m_MaxSize: 40 185 | m_Alignment: 0 186 | m_AlignByGeometry: 0 187 | m_RichText: 1 188 | m_HorizontalOverflow: 0 189 | m_VerticalOverflow: 0 190 | m_LineSpacing: 1 191 | m_Text: 88:88/88:88 192 | --- !u!222 &91943497 193 | CanvasRenderer: 194 | m_ObjectHideFlags: 0 195 | m_CorrespondingSourceObject: {fileID: 0} 196 | m_PrefabInstance: {fileID: 0} 197 | m_PrefabAsset: {fileID: 0} 198 | m_GameObject: {fileID: 91943494} 199 | m_CullTransparentMesh: 0 200 | --- !u!1 &141714613 201 | GameObject: 202 | m_ObjectHideFlags: 0 203 | m_CorrespondingSourceObject: {fileID: 0} 204 | m_PrefabInstance: {fileID: 0} 205 | m_PrefabAsset: {fileID: 0} 206 | serializedVersion: 6 207 | m_Component: 208 | - component: {fileID: 141714614} 209 | - component: {fileID: 141714617} 210 | - component: {fileID: 141714616} 211 | - component: {fileID: 141714615} 212 | m_Layer: 5 213 | m_Name: PlayButton 214 | m_TagString: Untagged 215 | m_Icon: {fileID: 0} 216 | m_NavMeshLayer: 0 217 | m_StaticEditorFlags: 0 218 | m_IsActive: 1 219 | --- !u!224 &141714614 220 | RectTransform: 221 | m_ObjectHideFlags: 0 222 | m_CorrespondingSourceObject: {fileID: 0} 223 | m_PrefabInstance: {fileID: 0} 224 | m_PrefabAsset: {fileID: 0} 225 | m_GameObject: {fileID: 141714613} 226 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 227 | m_LocalPosition: {x: 0, y: 0, z: 0} 228 | m_LocalScale: {x: 1, y: 1, z: 1} 229 | m_Children: 230 | - {fileID: 1318596641} 231 | - {fileID: 268464351} 232 | m_Father: {fileID: 174072919} 233 | m_RootOrder: 1 234 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 235 | m_AnchorMin: {x: 0.5, y: 0.5} 236 | m_AnchorMax: {x: 0.5, y: 0.5} 237 | m_AnchoredPosition: {x: -0.8999939, y: 0} 238 | m_SizeDelta: {x: 100, y: 100} 239 | m_Pivot: {x: 0.5, y: 0.5} 240 | --- !u!114 &141714615 241 | MonoBehaviour: 242 | m_ObjectHideFlags: 0 243 | m_CorrespondingSourceObject: {fileID: 0} 244 | m_PrefabInstance: {fileID: 0} 245 | m_PrefabAsset: {fileID: 0} 246 | m_GameObject: {fileID: 141714613} 247 | m_Enabled: 1 248 | m_EditorHideFlags: 0 249 | m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} 250 | m_Name: 251 | m_EditorClassIdentifier: 252 | m_Navigation: 253 | m_Mode: 3 254 | m_SelectOnUp: {fileID: 0} 255 | m_SelectOnDown: {fileID: 0} 256 | m_SelectOnLeft: {fileID: 0} 257 | m_SelectOnRight: {fileID: 0} 258 | m_Transition: 1 259 | m_Colors: 260 | m_NormalColor: {r: 1, g: 1, b: 1, a: 1} 261 | m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 262 | m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} 263 | m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 264 | m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} 265 | m_ColorMultiplier: 1 266 | m_FadeDuration: 0.1 267 | m_SpriteState: 268 | m_HighlightedSprite: {fileID: 0} 269 | m_PressedSprite: {fileID: 0} 270 | m_SelectedSprite: {fileID: 0} 271 | m_DisabledSprite: {fileID: 0} 272 | m_AnimationTriggers: 273 | m_NormalTrigger: Normal 274 | m_HighlightedTrigger: Highlighted 275 | m_PressedTrigger: Pressed 276 | m_SelectedTrigger: Selected 277 | m_DisabledTrigger: Disabled 278 | m_Interactable: 1 279 | m_TargetGraphic: {fileID: 141714616} 280 | m_OnClick: 281 | m_PersistentCalls: 282 | m_Calls: 283 | - m_Target: {fileID: 1869404003} 284 | m_MethodName: Play 285 | m_Mode: 1 286 | m_Arguments: 287 | m_ObjectArgument: {fileID: 0} 288 | m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine 289 | m_IntArgument: 0 290 | m_FloatArgument: 0 291 | m_StringArgument: 292 | m_BoolArgument: 0 293 | m_CallState: 2 294 | --- !u!114 &141714616 295 | MonoBehaviour: 296 | m_ObjectHideFlags: 0 297 | m_CorrespondingSourceObject: {fileID: 0} 298 | m_PrefabInstance: {fileID: 0} 299 | m_PrefabAsset: {fileID: 0} 300 | m_GameObject: {fileID: 141714613} 301 | m_Enabled: 1 302 | m_EditorHideFlags: 0 303 | m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} 304 | m_Name: 305 | m_EditorClassIdentifier: 306 | m_Material: {fileID: 0} 307 | m_Color: {r: 0.12259702, g: 0.12885468, b: 0.1792453, a: 1} 308 | m_RaycastTarget: 1 309 | m_OnCullStateChanged: 310 | m_PersistentCalls: 311 | m_Calls: [] 312 | m_Sprite: {fileID: 0} 313 | m_Type: 0 314 | m_PreserveAspect: 0 315 | m_FillCenter: 1 316 | m_FillMethod: 4 317 | m_FillAmount: 1 318 | m_FillClockwise: 1 319 | m_FillOrigin: 0 320 | m_UseSpriteMesh: 0 321 | m_PixelsPerUnitMultiplier: 1 322 | --- !u!222 &141714617 323 | CanvasRenderer: 324 | m_ObjectHideFlags: 0 325 | m_CorrespondingSourceObject: {fileID: 0} 326 | m_PrefabInstance: {fileID: 0} 327 | m_PrefabAsset: {fileID: 0} 328 | m_GameObject: {fileID: 141714613} 329 | m_CullTransparentMesh: 0 330 | --- !u!1 &174072918 331 | GameObject: 332 | m_ObjectHideFlags: 0 333 | m_CorrespondingSourceObject: {fileID: 0} 334 | m_PrefabInstance: {fileID: 0} 335 | m_PrefabAsset: {fileID: 0} 336 | serializedVersion: 6 337 | m_Component: 338 | - component: {fileID: 174072919} 339 | - component: {fileID: 174072921} 340 | - component: {fileID: 174072920} 341 | m_Layer: 5 342 | m_Name: Controls 343 | m_TagString: Untagged 344 | m_Icon: {fileID: 0} 345 | m_NavMeshLayer: 0 346 | m_StaticEditorFlags: 0 347 | m_IsActive: 1 348 | --- !u!224 &174072919 349 | RectTransform: 350 | m_ObjectHideFlags: 0 351 | m_CorrespondingSourceObject: {fileID: 0} 352 | m_PrefabInstance: {fileID: 0} 353 | m_PrefabAsset: {fileID: 0} 354 | m_GameObject: {fileID: 174072918} 355 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 356 | m_LocalPosition: {x: 0, y: 0, z: 0} 357 | m_LocalScale: {x: 1, y: 1, z: 1} 358 | m_Children: 359 | - {fileID: 1968714628} 360 | - {fileID: 141714614} 361 | - {fileID: 275241080} 362 | - {fileID: 2093230659} 363 | - {fileID: 91943495} 364 | m_Father: {fileID: 1995536890} 365 | m_RootOrder: 1 366 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 367 | m_AnchorMin: {x: 0, y: 0} 368 | m_AnchorMax: {x: 1, y: 0} 369 | m_AnchoredPosition: {x: 0, y: 0} 370 | m_SizeDelta: {x: 0, y: 175.90002} 371 | m_Pivot: {x: 0.5, y: 0} 372 | --- !u!114 &174072920 373 | MonoBehaviour: 374 | m_ObjectHideFlags: 0 375 | m_CorrespondingSourceObject: {fileID: 0} 376 | m_PrefabInstance: {fileID: 0} 377 | m_PrefabAsset: {fileID: 0} 378 | m_GameObject: {fileID: 174072918} 379 | m_Enabled: 1 380 | m_EditorHideFlags: 0 381 | m_Script: {fileID: 11500000, guid: 5352d274212da42bc895a9f371a01eda, type: 3} 382 | m_Name: 383 | m_EditorClassIdentifier: 384 | recordButton: {fileID: 1968714629} 385 | playButton: {fileID: 141714615} 386 | pauseButton: {fileID: 275241081} 387 | rewindButton: {fileID: 2093230660} 388 | timeText: {fileID: 91943496} 389 | vcr: {fileID: 1869404003} 390 | --- !u!222 &174072921 391 | CanvasRenderer: 392 | m_ObjectHideFlags: 0 393 | m_CorrespondingSourceObject: {fileID: 0} 394 | m_PrefabInstance: {fileID: 0} 395 | m_PrefabAsset: {fileID: 0} 396 | m_GameObject: {fileID: 174072918} 397 | m_CullTransparentMesh: 0 398 | --- !u!1 &268464350 399 | GameObject: 400 | m_ObjectHideFlags: 0 401 | m_CorrespondingSourceObject: {fileID: 0} 402 | m_PrefabInstance: {fileID: 0} 403 | m_PrefabAsset: {fileID: 0} 404 | serializedVersion: 6 405 | m_Component: 406 | - component: {fileID: 268464351} 407 | - component: {fileID: 268464353} 408 | - component: {fileID: 268464352} 409 | m_Layer: 5 410 | m_Name: Text (1) 411 | m_TagString: Untagged 412 | m_Icon: {fileID: 0} 413 | m_NavMeshLayer: 0 414 | m_StaticEditorFlags: 0 415 | m_IsActive: 1 416 | --- !u!224 &268464351 417 | RectTransform: 418 | m_ObjectHideFlags: 0 419 | m_CorrespondingSourceObject: {fileID: 0} 420 | m_PrefabInstance: {fileID: 0} 421 | m_PrefabAsset: {fileID: 0} 422 | m_GameObject: {fileID: 268464350} 423 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 424 | m_LocalPosition: {x: 0, y: 0, z: 0} 425 | m_LocalScale: {x: 1, y: 1, z: 1} 426 | m_Children: [] 427 | m_Father: {fileID: 141714614} 428 | m_RootOrder: 1 429 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 430 | m_AnchorMin: {x: 0.5, y: 0.5} 431 | m_AnchorMax: {x: 0.5, y: 0.5} 432 | m_AnchoredPosition: {x: 0, y: 3.83} 433 | m_SizeDelta: {x: 100, y: 92.34} 434 | m_Pivot: {x: 0.5, y: 0.5} 435 | --- !u!114 &268464352 436 | MonoBehaviour: 437 | m_ObjectHideFlags: 0 438 | m_CorrespondingSourceObject: {fileID: 0} 439 | m_PrefabInstance: {fileID: 0} 440 | m_PrefabAsset: {fileID: 0} 441 | m_GameObject: {fileID: 268464350} 442 | m_Enabled: 1 443 | m_EditorHideFlags: 0 444 | m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} 445 | m_Name: 446 | m_EditorClassIdentifier: 447 | m_Material: {fileID: 0} 448 | m_Color: {r: 1, g: 1, b: 1, a: 1} 449 | m_RaycastTarget: 1 450 | m_OnCullStateChanged: 451 | m_PersistentCalls: 452 | m_Calls: [] 453 | m_FontData: 454 | m_Font: {fileID: 12800000, guid: e506d2e857d294d99966099453ec9116, type: 3} 455 | m_FontSize: 24 456 | m_FontStyle: 0 457 | m_BestFit: 0 458 | m_MinSize: 2 459 | m_MaxSize: 50 460 | m_Alignment: 7 461 | m_AlignByGeometry: 0 462 | m_RichText: 1 463 | m_HorizontalOverflow: 0 464 | m_VerticalOverflow: 0 465 | m_LineSpacing: 1 466 | m_Text: play 467 | --- !u!222 &268464353 468 | CanvasRenderer: 469 | m_ObjectHideFlags: 0 470 | m_CorrespondingSourceObject: {fileID: 0} 471 | m_PrefabInstance: {fileID: 0} 472 | m_PrefabAsset: {fileID: 0} 473 | m_GameObject: {fileID: 268464350} 474 | m_CullTransparentMesh: 0 475 | --- !u!1 &275241079 476 | GameObject: 477 | m_ObjectHideFlags: 0 478 | m_CorrespondingSourceObject: {fileID: 0} 479 | m_PrefabInstance: {fileID: 0} 480 | m_PrefabAsset: {fileID: 0} 481 | serializedVersion: 6 482 | m_Component: 483 | - component: {fileID: 275241080} 484 | - component: {fileID: 275241083} 485 | - component: {fileID: 275241082} 486 | - component: {fileID: 275241081} 487 | m_Layer: 5 488 | m_Name: PauseButton 489 | m_TagString: Untagged 490 | m_Icon: {fileID: 0} 491 | m_NavMeshLayer: 0 492 | m_StaticEditorFlags: 0 493 | m_IsActive: 1 494 | --- !u!224 &275241080 495 | RectTransform: 496 | m_ObjectHideFlags: 0 497 | m_CorrespondingSourceObject: {fileID: 0} 498 | m_PrefabInstance: {fileID: 0} 499 | m_PrefabAsset: {fileID: 0} 500 | m_GameObject: {fileID: 275241079} 501 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 502 | m_LocalPosition: {x: 0, y: 0, z: 0} 503 | m_LocalScale: {x: 1, y: 1, z: 1} 504 | m_Children: 505 | - {fileID: 309340981} 506 | - {fileID: 2137327215} 507 | m_Father: {fileID: 174072919} 508 | m_RootOrder: 2 509 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 510 | m_AnchorMin: {x: 0.5, y: 0.5} 511 | m_AnchorMax: {x: 0.5, y: 0.5} 512 | m_AnchoredPosition: {x: -0.8999939, y: 0} 513 | m_SizeDelta: {x: 100, y: 100} 514 | m_Pivot: {x: 0.5, y: 0.5} 515 | --- !u!114 &275241081 516 | MonoBehaviour: 517 | m_ObjectHideFlags: 0 518 | m_CorrespondingSourceObject: {fileID: 0} 519 | m_PrefabInstance: {fileID: 0} 520 | m_PrefabAsset: {fileID: 0} 521 | m_GameObject: {fileID: 275241079} 522 | m_Enabled: 1 523 | m_EditorHideFlags: 0 524 | m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} 525 | m_Name: 526 | m_EditorClassIdentifier: 527 | m_Navigation: 528 | m_Mode: 3 529 | m_SelectOnUp: {fileID: 0} 530 | m_SelectOnDown: {fileID: 0} 531 | m_SelectOnLeft: {fileID: 0} 532 | m_SelectOnRight: {fileID: 0} 533 | m_Transition: 1 534 | m_Colors: 535 | m_NormalColor: {r: 1, g: 1, b: 1, a: 1} 536 | m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 537 | m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} 538 | m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 539 | m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} 540 | m_ColorMultiplier: 1 541 | m_FadeDuration: 0.1 542 | m_SpriteState: 543 | m_HighlightedSprite: {fileID: 0} 544 | m_PressedSprite: {fileID: 0} 545 | m_SelectedSprite: {fileID: 0} 546 | m_DisabledSprite: {fileID: 0} 547 | m_AnimationTriggers: 548 | m_NormalTrigger: Normal 549 | m_HighlightedTrigger: Highlighted 550 | m_PressedTrigger: Pressed 551 | m_SelectedTrigger: Selected 552 | m_DisabledTrigger: Disabled 553 | m_Interactable: 1 554 | m_TargetGraphic: {fileID: 275241082} 555 | m_OnClick: 556 | m_PersistentCalls: 557 | m_Calls: 558 | - m_Target: {fileID: 1869404003} 559 | m_MethodName: Pause 560 | m_Mode: 1 561 | m_Arguments: 562 | m_ObjectArgument: {fileID: 0} 563 | m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine 564 | m_IntArgument: 0 565 | m_FloatArgument: 0 566 | m_StringArgument: 567 | m_BoolArgument: 0 568 | m_CallState: 2 569 | --- !u!114 &275241082 570 | MonoBehaviour: 571 | m_ObjectHideFlags: 0 572 | m_CorrespondingSourceObject: {fileID: 0} 573 | m_PrefabInstance: {fileID: 0} 574 | m_PrefabAsset: {fileID: 0} 575 | m_GameObject: {fileID: 275241079} 576 | m_Enabled: 1 577 | m_EditorHideFlags: 0 578 | m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} 579 | m_Name: 580 | m_EditorClassIdentifier: 581 | m_Material: {fileID: 0} 582 | m_Color: {r: 0.12259702, g: 0.12885468, b: 0.1792453, a: 1} 583 | m_RaycastTarget: 1 584 | m_OnCullStateChanged: 585 | m_PersistentCalls: 586 | m_Calls: [] 587 | m_Sprite: {fileID: 0} 588 | m_Type: 0 589 | m_PreserveAspect: 0 590 | m_FillCenter: 1 591 | m_FillMethod: 4 592 | m_FillAmount: 1 593 | m_FillClockwise: 1 594 | m_FillOrigin: 0 595 | m_UseSpriteMesh: 0 596 | m_PixelsPerUnitMultiplier: 1 597 | --- !u!222 &275241083 598 | CanvasRenderer: 599 | m_ObjectHideFlags: 0 600 | m_CorrespondingSourceObject: {fileID: 0} 601 | m_PrefabInstance: {fileID: 0} 602 | m_PrefabAsset: {fileID: 0} 603 | m_GameObject: {fileID: 275241079} 604 | m_CullTransparentMesh: 0 605 | --- !u!1 &309340980 606 | GameObject: 607 | m_ObjectHideFlags: 0 608 | m_CorrespondingSourceObject: {fileID: 0} 609 | m_PrefabInstance: {fileID: 0} 610 | m_PrefabAsset: {fileID: 0} 611 | serializedVersion: 6 612 | m_Component: 613 | - component: {fileID: 309340981} 614 | - component: {fileID: 309340983} 615 | - component: {fileID: 309340982} 616 | m_Layer: 5 617 | m_Name: Text 618 | m_TagString: Untagged 619 | m_Icon: {fileID: 0} 620 | m_NavMeshLayer: 0 621 | m_StaticEditorFlags: 0 622 | m_IsActive: 1 623 | --- !u!224 &309340981 624 | RectTransform: 625 | m_ObjectHideFlags: 0 626 | m_CorrespondingSourceObject: {fileID: 0} 627 | m_PrefabInstance: {fileID: 0} 628 | m_PrefabAsset: {fileID: 0} 629 | m_GameObject: {fileID: 309340980} 630 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 631 | m_LocalPosition: {x: 0, y: 0, z: 0} 632 | m_LocalScale: {x: 1, y: 1, z: 1} 633 | m_Children: [] 634 | m_Father: {fileID: 275241080} 635 | m_RootOrder: 0 636 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 637 | m_AnchorMin: {x: 0.5, y: 0.5} 638 | m_AnchorMax: {x: 0.5, y: 0.5} 639 | m_AnchoredPosition: {x: 0, y: -4.15} 640 | m_SizeDelta: {x: 100, y: 91.7} 641 | m_Pivot: {x: 0.5, y: 0.5} 642 | --- !u!114 &309340982 643 | MonoBehaviour: 644 | m_ObjectHideFlags: 0 645 | m_CorrespondingSourceObject: {fileID: 0} 646 | m_PrefabInstance: {fileID: 0} 647 | m_PrefabAsset: {fileID: 0} 648 | m_GameObject: {fileID: 309340980} 649 | m_Enabled: 1 650 | m_EditorHideFlags: 0 651 | m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} 652 | m_Name: 653 | m_EditorClassIdentifier: 654 | m_Material: {fileID: 0} 655 | m_Color: {r: 1, g: 1, b: 1, a: 1} 656 | m_RaycastTarget: 1 657 | m_OnCullStateChanged: 658 | m_PersistentCalls: 659 | m_Calls: [] 660 | m_FontData: 661 | m_Font: {fileID: 12800000, guid: e506d2e857d294d99966099453ec9116, type: 3} 662 | m_FontSize: 48 663 | m_FontStyle: 0 664 | m_BestFit: 0 665 | m_MinSize: 4 666 | m_MaxSize: 50 667 | m_Alignment: 1 668 | m_AlignByGeometry: 0 669 | m_RichText: 1 670 | m_HorizontalOverflow: 0 671 | m_VerticalOverflow: 0 672 | m_LineSpacing: 1 673 | m_Text: '||' 674 | --- !u!222 &309340983 675 | CanvasRenderer: 676 | m_ObjectHideFlags: 0 677 | m_CorrespondingSourceObject: {fileID: 0} 678 | m_PrefabInstance: {fileID: 0} 679 | m_PrefabAsset: {fileID: 0} 680 | m_GameObject: {fileID: 309340980} 681 | m_CullTransparentMesh: 0 682 | --- !u!1 &393972364 683 | GameObject: 684 | m_ObjectHideFlags: 0 685 | m_CorrespondingSourceObject: {fileID: 0} 686 | m_PrefabInstance: {fileID: 0} 687 | m_PrefabAsset: {fileID: 0} 688 | serializedVersion: 6 689 | m_Component: 690 | - component: {fileID: 393972365} 691 | - component: {fileID: 393972367} 692 | - component: {fileID: 393972366} 693 | m_Layer: 5 694 | m_Name: Text (1) 695 | m_TagString: Untagged 696 | m_Icon: {fileID: 0} 697 | m_NavMeshLayer: 0 698 | m_StaticEditorFlags: 0 699 | m_IsActive: 1 700 | --- !u!224 &393972365 701 | RectTransform: 702 | m_ObjectHideFlags: 0 703 | m_CorrespondingSourceObject: {fileID: 0} 704 | m_PrefabInstance: {fileID: 0} 705 | m_PrefabAsset: {fileID: 0} 706 | m_GameObject: {fileID: 393972364} 707 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 708 | m_LocalPosition: {x: 0, y: 0, z: 0} 709 | m_LocalScale: {x: 1, y: 1, z: 1} 710 | m_Children: [] 711 | m_Father: {fileID: 2093230659} 712 | m_RootOrder: 1 713 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 714 | m_AnchorMin: {x: 0.5, y: 0.5} 715 | m_AnchorMax: {x: 0.5, y: 0.5} 716 | m_AnchoredPosition: {x: 0, y: 3.83} 717 | m_SizeDelta: {x: 100, y: 92.34} 718 | m_Pivot: {x: 0.5, y: 0.5} 719 | --- !u!114 &393972366 720 | MonoBehaviour: 721 | m_ObjectHideFlags: 0 722 | m_CorrespondingSourceObject: {fileID: 0} 723 | m_PrefabInstance: {fileID: 0} 724 | m_PrefabAsset: {fileID: 0} 725 | m_GameObject: {fileID: 393972364} 726 | m_Enabled: 1 727 | m_EditorHideFlags: 0 728 | m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} 729 | m_Name: 730 | m_EditorClassIdentifier: 731 | m_Material: {fileID: 0} 732 | m_Color: {r: 1, g: 1, b: 1, a: 1} 733 | m_RaycastTarget: 1 734 | m_OnCullStateChanged: 735 | m_PersistentCalls: 736 | m_Calls: [] 737 | m_FontData: 738 | m_Font: {fileID: 12800000, guid: e506d2e857d294d99966099453ec9116, type: 3} 739 | m_FontSize: 24 740 | m_FontStyle: 0 741 | m_BestFit: 0 742 | m_MinSize: 2 743 | m_MaxSize: 50 744 | m_Alignment: 7 745 | m_AlignByGeometry: 0 746 | m_RichText: 1 747 | m_HorizontalOverflow: 0 748 | m_VerticalOverflow: 0 749 | m_LineSpacing: 1 750 | m_Text: rewind 751 | --- !u!222 &393972367 752 | CanvasRenderer: 753 | m_ObjectHideFlags: 0 754 | m_CorrespondingSourceObject: {fileID: 0} 755 | m_PrefabInstance: {fileID: 0} 756 | m_PrefabAsset: {fileID: 0} 757 | m_GameObject: {fileID: 393972364} 758 | m_CullTransparentMesh: 0 759 | --- !u!1 &426411485 760 | GameObject: 761 | m_ObjectHideFlags: 0 762 | m_CorrespondingSourceObject: {fileID: 0} 763 | m_PrefabInstance: {fileID: 0} 764 | m_PrefabAsset: {fileID: 0} 765 | serializedVersion: 6 766 | m_Component: 767 | - component: {fileID: 426411486} 768 | - component: {fileID: 426411489} 769 | - component: {fileID: 426411488} 770 | - component: {fileID: 426411487} 771 | m_Layer: 0 772 | m_Name: Cube 773 | m_TagString: Untagged 774 | m_Icon: {fileID: 0} 775 | m_NavMeshLayer: 0 776 | m_StaticEditorFlags: 0 777 | m_IsActive: 1 778 | --- !u!4 &426411486 779 | Transform: 780 | m_ObjectHideFlags: 0 781 | m_CorrespondingSourceObject: {fileID: 0} 782 | m_PrefabInstance: {fileID: 0} 783 | m_PrefabAsset: {fileID: 0} 784 | m_GameObject: {fileID: 426411485} 785 | m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} 786 | m_LocalPosition: {x: 0, y: 0, z: 0} 787 | m_LocalScale: {x: 0.2, y: 0.2, z: 1} 788 | m_Children: [] 789 | m_Father: {fileID: 1282038635} 790 | m_RootOrder: 0 791 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 792 | --- !u!61 &426411487 793 | BoxCollider2D: 794 | m_ObjectHideFlags: 0 795 | m_CorrespondingSourceObject: {fileID: 0} 796 | m_PrefabInstance: {fileID: 0} 797 | m_PrefabAsset: {fileID: 0} 798 | m_GameObject: {fileID: 426411485} 799 | m_Enabled: 1 800 | m_Density: 1 801 | m_Material: {fileID: 0} 802 | m_IsTrigger: 0 803 | m_UsedByEffector: 0 804 | m_UsedByComposite: 0 805 | m_Offset: {x: 0, y: 0} 806 | m_SpriteTilingProperty: 807 | border: {x: 0, y: 0, z: 0, w: 0} 808 | pivot: {x: 0, y: 0} 809 | oldSize: {x: 0, y: 0} 810 | newSize: {x: 0, y: 0} 811 | adaptiveTilingThreshold: 0 812 | drawMode: 0 813 | adaptiveTiling: 0 814 | m_AutoTiling: 0 815 | serializedVersion: 2 816 | m_Size: {x: 1, y: 1} 817 | m_EdgeRadius: 0 818 | --- !u!23 &426411488 819 | MeshRenderer: 820 | m_ObjectHideFlags: 0 821 | m_CorrespondingSourceObject: {fileID: 0} 822 | m_PrefabInstance: {fileID: 0} 823 | m_PrefabAsset: {fileID: 0} 824 | m_GameObject: {fileID: 426411485} 825 | m_Enabled: 1 826 | m_CastShadows: 1 827 | m_ReceiveShadows: 1 828 | m_DynamicOccludee: 1 829 | m_MotionVectors: 1 830 | m_LightProbeUsage: 1 831 | m_ReflectionProbeUsage: 1 832 | m_RenderingLayerMask: 1 833 | m_RendererPriority: 0 834 | m_Materials: 835 | - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} 836 | m_StaticBatchInfo: 837 | firstSubMesh: 0 838 | subMeshCount: 0 839 | m_StaticBatchRoot: {fileID: 0} 840 | m_ProbeAnchor: {fileID: 0} 841 | m_LightProbeVolumeOverride: {fileID: 0} 842 | m_ScaleInLightmap: 1 843 | m_ReceiveGI: 1 844 | m_PreserveUVs: 0 845 | m_IgnoreNormalsForChartDetection: 0 846 | m_ImportantGI: 0 847 | m_StitchLightmapSeams: 1 848 | m_SelectedEditorRenderState: 3 849 | m_MinimumChartSize: 4 850 | m_AutoUVMaxDistance: 0.5 851 | m_AutoUVMaxAngle: 89 852 | m_LightmapParameters: {fileID: 0} 853 | m_SortingLayerID: 0 854 | m_SortingLayer: 0 855 | m_SortingOrder: 0 856 | --- !u!33 &426411489 857 | MeshFilter: 858 | m_ObjectHideFlags: 0 859 | m_CorrespondingSourceObject: {fileID: 0} 860 | m_PrefabInstance: {fileID: 0} 861 | m_PrefabAsset: {fileID: 0} 862 | m_GameObject: {fileID: 426411485} 863 | m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} 864 | --- !u!1 &515249586 865 | GameObject: 866 | m_ObjectHideFlags: 0 867 | m_CorrespondingSourceObject: {fileID: 0} 868 | m_PrefabInstance: {fileID: 0} 869 | m_PrefabAsset: {fileID: 0} 870 | serializedVersion: 6 871 | m_Component: 872 | - component: {fileID: 515249587} 873 | - component: {fileID: 515249589} 874 | - component: {fileID: 515249588} 875 | m_Layer: 5 876 | m_Name: Text 877 | m_TagString: Untagged 878 | m_Icon: {fileID: 0} 879 | m_NavMeshLayer: 0 880 | m_StaticEditorFlags: 0 881 | m_IsActive: 1 882 | --- !u!224 &515249587 883 | RectTransform: 884 | m_ObjectHideFlags: 0 885 | m_CorrespondingSourceObject: {fileID: 0} 886 | m_PrefabInstance: {fileID: 0} 887 | m_PrefabAsset: {fileID: 0} 888 | m_GameObject: {fileID: 515249586} 889 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 890 | m_LocalPosition: {x: 0, y: 0, z: 0} 891 | m_LocalScale: {x: 1, y: 1, z: 1} 892 | m_Children: [] 893 | m_Father: {fileID: 2093230659} 894 | m_RootOrder: 0 895 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 896 | m_AnchorMin: {x: 0.5, y: 0.5} 897 | m_AnchorMax: {x: 0.5, y: 0.5} 898 | m_AnchoredPosition: {x: 0, y: -4.15} 899 | m_SizeDelta: {x: 100, y: 91.7} 900 | m_Pivot: {x: 0.5, y: 0.5} 901 | --- !u!114 &515249588 902 | MonoBehaviour: 903 | m_ObjectHideFlags: 0 904 | m_CorrespondingSourceObject: {fileID: 0} 905 | m_PrefabInstance: {fileID: 0} 906 | m_PrefabAsset: {fileID: 0} 907 | m_GameObject: {fileID: 515249586} 908 | m_Enabled: 1 909 | m_EditorHideFlags: 0 910 | m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} 911 | m_Name: 912 | m_EditorClassIdentifier: 913 | m_Material: {fileID: 0} 914 | m_Color: {r: 1, g: 1, b: 1, a: 1} 915 | m_RaycastTarget: 1 916 | m_OnCullStateChanged: 917 | m_PersistentCalls: 918 | m_Calls: [] 919 | m_FontData: 920 | m_Font: {fileID: 12800000, guid: e506d2e857d294d99966099453ec9116, type: 3} 921 | m_FontSize: 48 922 | m_FontStyle: 0 923 | m_BestFit: 0 924 | m_MinSize: 4 925 | m_MaxSize: 50 926 | m_Alignment: 1 927 | m_AlignByGeometry: 0 928 | m_RichText: 1 929 | m_HorizontalOverflow: 0 930 | m_VerticalOverflow: 0 931 | m_LineSpacing: 1 932 | m_Text: << 933 | --- !u!222 &515249589 934 | CanvasRenderer: 935 | m_ObjectHideFlags: 0 936 | m_CorrespondingSourceObject: {fileID: 0} 937 | m_PrefabInstance: {fileID: 0} 938 | m_PrefabAsset: {fileID: 0} 939 | m_GameObject: {fileID: 515249586} 940 | m_CullTransparentMesh: 0 941 | --- !u!1 &1091692662 942 | GameObject: 943 | m_ObjectHideFlags: 0 944 | m_CorrespondingSourceObject: {fileID: 0} 945 | m_PrefabInstance: {fileID: 0} 946 | m_PrefabAsset: {fileID: 0} 947 | serializedVersion: 6 948 | m_Component: 949 | - component: {fileID: 1091692663} 950 | - component: {fileID: 1091692665} 951 | - component: {fileID: 1091692664} 952 | m_Layer: 5 953 | m_Name: Instructions 954 | m_TagString: Untagged 955 | m_Icon: {fileID: 0} 956 | m_NavMeshLayer: 0 957 | m_StaticEditorFlags: 0 958 | m_IsActive: 1 959 | --- !u!224 &1091692663 960 | RectTransform: 961 | m_ObjectHideFlags: 0 962 | m_CorrespondingSourceObject: {fileID: 0} 963 | m_PrefabInstance: {fileID: 0} 964 | m_PrefabAsset: {fileID: 0} 965 | m_GameObject: {fileID: 1091692662} 966 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 967 | m_LocalPosition: {x: 0, y: 0, z: 0} 968 | m_LocalScale: {x: 1, y: 1, z: 1} 969 | m_Children: [] 970 | m_Father: {fileID: 1995536890} 971 | m_RootOrder: 0 972 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 973 | m_AnchorMin: {x: 0, y: 1} 974 | m_AnchorMax: {x: 1, y: 1} 975 | m_AnchoredPosition: {x: 0, y: 0} 976 | m_SizeDelta: {x: 0, y: 175.9} 977 | m_Pivot: {x: 0.5, y: 1} 978 | --- !u!114 &1091692664 979 | MonoBehaviour: 980 | m_ObjectHideFlags: 0 981 | m_CorrespondingSourceObject: {fileID: 0} 982 | m_PrefabInstance: {fileID: 0} 983 | m_PrefabAsset: {fileID: 0} 984 | m_GameObject: {fileID: 1091692662} 985 | m_Enabled: 1 986 | m_EditorHideFlags: 0 987 | m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} 988 | m_Name: 989 | m_EditorClassIdentifier: 990 | m_Material: {fileID: 0} 991 | m_Color: {r: 0.12259702, g: 0.12885468, b: 0.1792453, a: 1} 992 | m_RaycastTarget: 1 993 | m_OnCullStateChanged: 994 | m_PersistentCalls: 995 | m_Calls: [] 996 | m_FontData: 997 | m_Font: {fileID: 12800000, guid: e506d2e857d294d99966099453ec9116, type: 3} 998 | m_FontSize: 18 999 | m_FontStyle: 0 1000 | m_BestFit: 0 1001 | m_MinSize: 1 1002 | m_MaxSize: 40 1003 | m_Alignment: 1 1004 | m_AlignByGeometry: 0 1005 | m_RichText: 1 1006 | m_HorizontalOverflow: 0 1007 | m_VerticalOverflow: 0 1008 | m_LineSpacing: 1 1009 | m_Text: 'InputVCR Basic Record Example 1010 | 1011 | 1012 | WASD to move square 1013 | 1014 | (RECORD) to start recording 1015 | 1016 | (REWIND) to go back to start of recording 1017 | 1018 | (PLAY) to play back 1019 | 1020 | 1021 | See the Player and VCR GameObjects for more options' 1022 | --- !u!222 &1091692665 1023 | CanvasRenderer: 1024 | m_ObjectHideFlags: 0 1025 | m_CorrespondingSourceObject: {fileID: 0} 1026 | m_PrefabInstance: {fileID: 0} 1027 | m_PrefabAsset: {fileID: 0} 1028 | m_GameObject: {fileID: 1091692662} 1029 | m_CullTransparentMesh: 0 1030 | --- !u!1 &1241268861 1031 | GameObject: 1032 | m_ObjectHideFlags: 0 1033 | m_CorrespondingSourceObject: {fileID: 0} 1034 | m_PrefabInstance: {fileID: 0} 1035 | m_PrefabAsset: {fileID: 0} 1036 | serializedVersion: 6 1037 | m_Component: 1038 | - component: {fileID: 1241268864} 1039 | - component: {fileID: 1241268863} 1040 | - component: {fileID: 1241268862} 1041 | m_Layer: 0 1042 | m_Name: EventSystem 1043 | m_TagString: Untagged 1044 | m_Icon: {fileID: 0} 1045 | m_NavMeshLayer: 0 1046 | m_StaticEditorFlags: 0 1047 | m_IsActive: 1 1048 | --- !u!114 &1241268862 1049 | MonoBehaviour: 1050 | m_ObjectHideFlags: 0 1051 | m_CorrespondingSourceObject: {fileID: 0} 1052 | m_PrefabInstance: {fileID: 0} 1053 | m_PrefabAsset: {fileID: 0} 1054 | m_GameObject: {fileID: 1241268861} 1055 | m_Enabled: 1 1056 | m_EditorHideFlags: 0 1057 | m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} 1058 | m_Name: 1059 | m_EditorClassIdentifier: 1060 | m_HorizontalAxis: Horizontal 1061 | m_VerticalAxis: Vertical 1062 | m_SubmitButton: Submit 1063 | m_CancelButton: Cancel 1064 | m_InputActionsPerSecond: 10 1065 | m_RepeatDelay: 0.5 1066 | m_ForceModuleActive: 0 1067 | --- !u!114 &1241268863 1068 | MonoBehaviour: 1069 | m_ObjectHideFlags: 0 1070 | m_CorrespondingSourceObject: {fileID: 0} 1071 | m_PrefabInstance: {fileID: 0} 1072 | m_PrefabAsset: {fileID: 0} 1073 | m_GameObject: {fileID: 1241268861} 1074 | m_Enabled: 1 1075 | m_EditorHideFlags: 0 1076 | m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} 1077 | m_Name: 1078 | m_EditorClassIdentifier: 1079 | m_FirstSelected: {fileID: 0} 1080 | m_sendNavigationEvents: 1 1081 | m_DragThreshold: 10 1082 | --- !u!4 &1241268864 1083 | Transform: 1084 | m_ObjectHideFlags: 0 1085 | m_CorrespondingSourceObject: {fileID: 0} 1086 | m_PrefabInstance: {fileID: 0} 1087 | m_PrefabAsset: {fileID: 0} 1088 | m_GameObject: {fileID: 1241268861} 1089 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 1090 | m_LocalPosition: {x: 0, y: 0, z: 0} 1091 | m_LocalScale: {x: 1, y: 1, z: 1} 1092 | m_Children: [] 1093 | m_Father: {fileID: 0} 1094 | m_RootOrder: 2 1095 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 1096 | --- !u!1 &1263009178 1097 | GameObject: 1098 | m_ObjectHideFlags: 0 1099 | m_CorrespondingSourceObject: {fileID: 0} 1100 | m_PrefabInstance: {fileID: 0} 1101 | m_PrefabAsset: {fileID: 0} 1102 | serializedVersion: 6 1103 | m_Component: 1104 | - component: {fileID: 1263009179} 1105 | - component: {fileID: 1263009181} 1106 | - component: {fileID: 1263009180} 1107 | m_Layer: 5 1108 | m_Name: Text 1109 | m_TagString: Untagged 1110 | m_Icon: {fileID: 0} 1111 | m_NavMeshLayer: 0 1112 | m_StaticEditorFlags: 0 1113 | m_IsActive: 1 1114 | --- !u!224 &1263009179 1115 | RectTransform: 1116 | m_ObjectHideFlags: 0 1117 | m_CorrespondingSourceObject: {fileID: 0} 1118 | m_PrefabInstance: {fileID: 0} 1119 | m_PrefabAsset: {fileID: 0} 1120 | m_GameObject: {fileID: 1263009178} 1121 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 1122 | m_LocalPosition: {x: 0, y: 0, z: 0} 1123 | m_LocalScale: {x: 1, y: 1, z: 1} 1124 | m_Children: [] 1125 | m_Father: {fileID: 1968714628} 1126 | m_RootOrder: 0 1127 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 1128 | m_AnchorMin: {x: 0.5, y: 0.5} 1129 | m_AnchorMax: {x: 0.5, y: 0.5} 1130 | m_AnchoredPosition: {x: 0, y: -4.15} 1131 | m_SizeDelta: {x: 100, y: 91.7} 1132 | m_Pivot: {x: 0.5, y: 0.5} 1133 | --- !u!114 &1263009180 1134 | MonoBehaviour: 1135 | m_ObjectHideFlags: 0 1136 | m_CorrespondingSourceObject: {fileID: 0} 1137 | m_PrefabInstance: {fileID: 0} 1138 | m_PrefabAsset: {fileID: 0} 1139 | m_GameObject: {fileID: 1263009178} 1140 | m_Enabled: 1 1141 | m_EditorHideFlags: 0 1142 | m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} 1143 | m_Name: 1144 | m_EditorClassIdentifier: 1145 | m_Material: {fileID: 0} 1146 | m_Color: {r: 1, g: 1, b: 1, a: 1} 1147 | m_RaycastTarget: 1 1148 | m_OnCullStateChanged: 1149 | m_PersistentCalls: 1150 | m_Calls: [] 1151 | m_FontData: 1152 | m_Font: {fileID: 12800000, guid: e506d2e857d294d99966099453ec9116, type: 3} 1153 | m_FontSize: 48 1154 | m_FontStyle: 0 1155 | m_BestFit: 0 1156 | m_MinSize: 4 1157 | m_MaxSize: 50 1158 | m_Alignment: 1 1159 | m_AlignByGeometry: 0 1160 | m_RichText: 1 1161 | m_HorizontalOverflow: 0 1162 | m_VerticalOverflow: 0 1163 | m_LineSpacing: 1 1164 | m_Text: O 1165 | --- !u!222 &1263009181 1166 | CanvasRenderer: 1167 | m_ObjectHideFlags: 0 1168 | m_CorrespondingSourceObject: {fileID: 0} 1169 | m_PrefabInstance: {fileID: 0} 1170 | m_PrefabAsset: {fileID: 0} 1171 | m_GameObject: {fileID: 1263009178} 1172 | m_CullTransparentMesh: 0 1173 | --- !u!1 &1282038632 1174 | GameObject: 1175 | m_ObjectHideFlags: 0 1176 | m_CorrespondingSourceObject: {fileID: 0} 1177 | m_PrefabInstance: {fileID: 0} 1178 | m_PrefabAsset: {fileID: 0} 1179 | serializedVersion: 6 1180 | m_Component: 1181 | - component: {fileID: 1282038635} 1182 | - component: {fileID: 1282038634} 1183 | - component: {fileID: 1282038633} 1184 | m_Layer: 0 1185 | m_Name: Player 1186 | m_TagString: Untagged 1187 | m_Icon: {fileID: 0} 1188 | m_NavMeshLayer: 0 1189 | m_StaticEditorFlags: 0 1190 | m_IsActive: 1 1191 | --- !u!114 &1282038633 1192 | MonoBehaviour: 1193 | m_ObjectHideFlags: 0 1194 | m_CorrespondingSourceObject: {fileID: 0} 1195 | m_PrefabInstance: {fileID: 0} 1196 | m_PrefabAsset: {fileID: 0} 1197 | m_GameObject: {fileID: 1282038632} 1198 | m_Enabled: 1 1199 | m_EditorHideFlags: 0 1200 | m_Script: {fileID: 11500000, guid: f95ed2210b66949619f8ed1ab67ddbdc, type: 3} 1201 | m_Name: 1202 | m_EditorClassIdentifier: 1203 | recorderToSyncTo: {fileID: 1869404003} 1204 | customTag: 1205 | syncPosition: 1 1206 | syncRotation: 1 1207 | syncScale: 1 1208 | --- !u!114 &1282038634 1209 | MonoBehaviour: 1210 | m_ObjectHideFlags: 0 1211 | m_CorrespondingSourceObject: {fileID: 0} 1212 | m_PrefabInstance: {fileID: 0} 1213 | m_PrefabAsset: {fileID: 0} 1214 | m_GameObject: {fileID: 1282038632} 1215 | m_Enabled: 1 1216 | m_EditorHideFlags: 0 1217 | m_Script: {fileID: 11500000, guid: bccf992011caf4fd4b18fa1a9defa80b, type: 3} 1218 | m_Name: 1219 | m_EditorClassIdentifier: 1220 | recorder: {fileID: 1869404003} 1221 | maxSpeed: 4 1222 | damping: 7 1223 | --- !u!4 &1282038635 1224 | Transform: 1225 | m_ObjectHideFlags: 0 1226 | m_CorrespondingSourceObject: {fileID: 0} 1227 | m_PrefabInstance: {fileID: 0} 1228 | m_PrefabAsset: {fileID: 0} 1229 | m_GameObject: {fileID: 1282038632} 1230 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 1231 | m_LocalPosition: {x: 0, y: 0, z: 0} 1232 | m_LocalScale: {x: 1, y: 1, z: 1} 1233 | m_Children: 1234 | - {fileID: 426411486} 1235 | m_Father: {fileID: 0} 1236 | m_RootOrder: 3 1237 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 1238 | --- !u!1 &1318596640 1239 | GameObject: 1240 | m_ObjectHideFlags: 0 1241 | m_CorrespondingSourceObject: {fileID: 0} 1242 | m_PrefabInstance: {fileID: 0} 1243 | m_PrefabAsset: {fileID: 0} 1244 | serializedVersion: 6 1245 | m_Component: 1246 | - component: {fileID: 1318596641} 1247 | - component: {fileID: 1318596643} 1248 | - component: {fileID: 1318596642} 1249 | m_Layer: 5 1250 | m_Name: Text 1251 | m_TagString: Untagged 1252 | m_Icon: {fileID: 0} 1253 | m_NavMeshLayer: 0 1254 | m_StaticEditorFlags: 0 1255 | m_IsActive: 1 1256 | --- !u!224 &1318596641 1257 | RectTransform: 1258 | m_ObjectHideFlags: 0 1259 | m_CorrespondingSourceObject: {fileID: 0} 1260 | m_PrefabInstance: {fileID: 0} 1261 | m_PrefabAsset: {fileID: 0} 1262 | m_GameObject: {fileID: 1318596640} 1263 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 1264 | m_LocalPosition: {x: 0, y: 0, z: 0} 1265 | m_LocalScale: {x: 1, y: 1, z: 1} 1266 | m_Children: [] 1267 | m_Father: {fileID: 141714614} 1268 | m_RootOrder: 0 1269 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 1270 | m_AnchorMin: {x: 0.5, y: 0.5} 1271 | m_AnchorMax: {x: 0.5, y: 0.5} 1272 | m_AnchoredPosition: {x: 0, y: -4.15} 1273 | m_SizeDelta: {x: 100, y: 91.7} 1274 | m_Pivot: {x: 0.5, y: 0.5} 1275 | --- !u!114 &1318596642 1276 | MonoBehaviour: 1277 | m_ObjectHideFlags: 0 1278 | m_CorrespondingSourceObject: {fileID: 0} 1279 | m_PrefabInstance: {fileID: 0} 1280 | m_PrefabAsset: {fileID: 0} 1281 | m_GameObject: {fileID: 1318596640} 1282 | m_Enabled: 1 1283 | m_EditorHideFlags: 0 1284 | m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} 1285 | m_Name: 1286 | m_EditorClassIdentifier: 1287 | m_Material: {fileID: 0} 1288 | m_Color: {r: 1, g: 1, b: 1, a: 1} 1289 | m_RaycastTarget: 1 1290 | m_OnCullStateChanged: 1291 | m_PersistentCalls: 1292 | m_Calls: [] 1293 | m_FontData: 1294 | m_Font: {fileID: 12800000, guid: e506d2e857d294d99966099453ec9116, type: 3} 1295 | m_FontSize: 48 1296 | m_FontStyle: 0 1297 | m_BestFit: 0 1298 | m_MinSize: 4 1299 | m_MaxSize: 50 1300 | m_Alignment: 1 1301 | m_AlignByGeometry: 0 1302 | m_RichText: 1 1303 | m_HorizontalOverflow: 0 1304 | m_VerticalOverflow: 0 1305 | m_LineSpacing: 1 1306 | m_Text: '>' 1307 | --- !u!222 &1318596643 1308 | CanvasRenderer: 1309 | m_ObjectHideFlags: 0 1310 | m_CorrespondingSourceObject: {fileID: 0} 1311 | m_PrefabInstance: {fileID: 0} 1312 | m_PrefabAsset: {fileID: 0} 1313 | m_GameObject: {fileID: 1318596640} 1314 | m_CullTransparentMesh: 0 1315 | --- !u!1 &1472278084 1316 | GameObject: 1317 | m_ObjectHideFlags: 0 1318 | m_CorrespondingSourceObject: {fileID: 0} 1319 | m_PrefabInstance: {fileID: 0} 1320 | m_PrefabAsset: {fileID: 0} 1321 | serializedVersion: 6 1322 | m_Component: 1323 | - component: {fileID: 1472278087} 1324 | - component: {fileID: 1472278086} 1325 | - component: {fileID: 1472278085} 1326 | m_Layer: 0 1327 | m_Name: Camera 1328 | m_TagString: Untagged 1329 | m_Icon: {fileID: 0} 1330 | m_NavMeshLayer: 0 1331 | m_StaticEditorFlags: 0 1332 | m_IsActive: 1 1333 | --- !u!81 &1472278085 1334 | AudioListener: 1335 | m_ObjectHideFlags: 0 1336 | m_CorrespondingSourceObject: {fileID: 0} 1337 | m_PrefabInstance: {fileID: 0} 1338 | m_PrefabAsset: {fileID: 0} 1339 | m_GameObject: {fileID: 1472278084} 1340 | m_Enabled: 1 1341 | --- !u!20 &1472278086 1342 | Camera: 1343 | m_ObjectHideFlags: 0 1344 | m_CorrespondingSourceObject: {fileID: 0} 1345 | m_PrefabInstance: {fileID: 0} 1346 | m_PrefabAsset: {fileID: 0} 1347 | m_GameObject: {fileID: 1472278084} 1348 | m_Enabled: 1 1349 | serializedVersion: 2 1350 | m_ClearFlags: 2 1351 | m_BackGroundColor: {r: 0.7224546, g: 0.7533449, b: 0.8018868, a: 0} 1352 | m_projectionMatrixMode: 1 1353 | m_GateFitMode: 2 1354 | m_FOVAxisMode: 0 1355 | m_SensorSize: {x: 36, y: 24} 1356 | m_LensShift: {x: 0, y: 0} 1357 | m_FocalLength: 50 1358 | m_NormalizedViewPortRect: 1359 | serializedVersion: 2 1360 | x: 0 1361 | y: 0 1362 | width: 1 1363 | height: 1 1364 | near clip plane: 0.3 1365 | far clip plane: 100 1366 | field of view: 60 1367 | orthographic: 1 1368 | orthographic size: 5 1369 | m_Depth: 0 1370 | m_CullingMask: 1371 | serializedVersion: 2 1372 | m_Bits: 4294967295 1373 | m_RenderingPath: -1 1374 | m_TargetTexture: {fileID: 0} 1375 | m_TargetDisplay: 0 1376 | m_TargetEye: 3 1377 | m_HDR: 1 1378 | m_AllowMSAA: 1 1379 | m_AllowDynamicResolution: 0 1380 | m_ForceIntoRT: 0 1381 | m_OcclusionCulling: 1 1382 | m_StereoConvergence: 10 1383 | m_StereoSeparation: 0.022 1384 | --- !u!4 &1472278087 1385 | Transform: 1386 | m_ObjectHideFlags: 0 1387 | m_CorrespondingSourceObject: {fileID: 0} 1388 | m_PrefabInstance: {fileID: 0} 1389 | m_PrefabAsset: {fileID: 0} 1390 | m_GameObject: {fileID: 1472278084} 1391 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 1392 | m_LocalPosition: {x: 0, y: 0, z: -10} 1393 | m_LocalScale: {x: 1, y: 1, z: 1} 1394 | m_Children: [] 1395 | m_Father: {fileID: 0} 1396 | m_RootOrder: 0 1397 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 1398 | --- !u!1 &1869404001 1399 | GameObject: 1400 | m_ObjectHideFlags: 0 1401 | m_CorrespondingSourceObject: {fileID: 0} 1402 | m_PrefabInstance: {fileID: 0} 1403 | m_PrefabAsset: {fileID: 0} 1404 | serializedVersion: 6 1405 | m_Component: 1406 | - component: {fileID: 1869404004} 1407 | - component: {fileID: 1869404003} 1408 | - component: {fileID: 1869404005} 1409 | m_Layer: 0 1410 | m_Name: VCR 1411 | m_TagString: Untagged 1412 | m_Icon: {fileID: 0} 1413 | m_NavMeshLayer: 0 1414 | m_StaticEditorFlags: 0 1415 | m_IsActive: 1 1416 | --- !u!114 &1869404003 1417 | MonoBehaviour: 1418 | m_ObjectHideFlags: 0 1419 | m_CorrespondingSourceObject: {fileID: 0} 1420 | m_PrefabInstance: {fileID: 0} 1421 | m_PrefabAsset: {fileID: 0} 1422 | m_GameObject: {fileID: 1869404001} 1423 | m_Enabled: 1 1424 | m_EditorHideFlags: 0 1425 | m_Script: {fileID: 11500000, guid: d3fd95aeca4ea44a2a903192b3e7ba21, type: 3} 1426 | m_Name: 1427 | m_EditorClassIdentifier: 1428 | _recordedButtons: [] 1429 | _recordedAxes: [] 1430 | _recordedKeys: 77000000610000007300000064000000 1431 | recordMouseEvents: 0 1432 | _mode: 0 1433 | --- !u!4 &1869404004 1434 | Transform: 1435 | m_ObjectHideFlags: 0 1436 | m_CorrespondingSourceObject: {fileID: 0} 1437 | m_PrefabInstance: {fileID: 0} 1438 | m_PrefabAsset: {fileID: 0} 1439 | m_GameObject: {fileID: 1869404001} 1440 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 1441 | m_LocalPosition: {x: 0, y: 0, z: 0} 1442 | m_LocalScale: {x: 1, y: 1, z: 1} 1443 | m_Children: [] 1444 | m_Father: {fileID: 0} 1445 | m_RootOrder: 4 1446 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 1447 | --- !u!114 &1869404005 1448 | MonoBehaviour: 1449 | m_ObjectHideFlags: 0 1450 | m_CorrespondingSourceObject: {fileID: 0} 1451 | m_PrefabInstance: {fileID: 0} 1452 | m_PrefabAsset: {fileID: 0} 1453 | m_GameObject: {fileID: 1869404001} 1454 | m_Enabled: 1 1455 | m_EditorHideFlags: 0 1456 | m_Script: {fileID: 11500000, guid: ac1813ea1d3d94e98acb43afab3697dc, type: 3} 1457 | m_Name: 1458 | m_EditorClassIdentifier: 1459 | loadRecordingOnStart: {fileID: 4900000, guid: e1da68cf15726402e83c2844e721d634, 1460 | type: 3} 1461 | playRecordingOnStart: 0 1462 | --- !u!1 &1891684388 1463 | GameObject: 1464 | m_ObjectHideFlags: 0 1465 | m_CorrespondingSourceObject: {fileID: 0} 1466 | m_PrefabInstance: {fileID: 0} 1467 | m_PrefabAsset: {fileID: 0} 1468 | serializedVersion: 6 1469 | m_Component: 1470 | - component: {fileID: 1891684389} 1471 | - component: {fileID: 1891684391} 1472 | - component: {fileID: 1891684390} 1473 | m_Layer: 5 1474 | m_Name: Text (1) 1475 | m_TagString: Untagged 1476 | m_Icon: {fileID: 0} 1477 | m_NavMeshLayer: 0 1478 | m_StaticEditorFlags: 0 1479 | m_IsActive: 1 1480 | --- !u!224 &1891684389 1481 | RectTransform: 1482 | m_ObjectHideFlags: 0 1483 | m_CorrespondingSourceObject: {fileID: 0} 1484 | m_PrefabInstance: {fileID: 0} 1485 | m_PrefabAsset: {fileID: 0} 1486 | m_GameObject: {fileID: 1891684388} 1487 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 1488 | m_LocalPosition: {x: 0, y: 0, z: 0} 1489 | m_LocalScale: {x: 1, y: 1, z: 1} 1490 | m_Children: [] 1491 | m_Father: {fileID: 1968714628} 1492 | m_RootOrder: 1 1493 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 1494 | m_AnchorMin: {x: 0.5, y: 0.5} 1495 | m_AnchorMax: {x: 0.5, y: 0.5} 1496 | m_AnchoredPosition: {x: 0, y: 3.83} 1497 | m_SizeDelta: {x: 100, y: 92.34} 1498 | m_Pivot: {x: 0.5, y: 0.5} 1499 | --- !u!114 &1891684390 1500 | MonoBehaviour: 1501 | m_ObjectHideFlags: 0 1502 | m_CorrespondingSourceObject: {fileID: 0} 1503 | m_PrefabInstance: {fileID: 0} 1504 | m_PrefabAsset: {fileID: 0} 1505 | m_GameObject: {fileID: 1891684388} 1506 | m_Enabled: 1 1507 | m_EditorHideFlags: 0 1508 | m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} 1509 | m_Name: 1510 | m_EditorClassIdentifier: 1511 | m_Material: {fileID: 0} 1512 | m_Color: {r: 1, g: 1, b: 1, a: 1} 1513 | m_RaycastTarget: 1 1514 | m_OnCullStateChanged: 1515 | m_PersistentCalls: 1516 | m_Calls: [] 1517 | m_FontData: 1518 | m_Font: {fileID: 12800000, guid: e506d2e857d294d99966099453ec9116, type: 3} 1519 | m_FontSize: 24 1520 | m_FontStyle: 0 1521 | m_BestFit: 0 1522 | m_MinSize: 2 1523 | m_MaxSize: 50 1524 | m_Alignment: 7 1525 | m_AlignByGeometry: 0 1526 | m_RichText: 1 1527 | m_HorizontalOverflow: 0 1528 | m_VerticalOverflow: 0 1529 | m_LineSpacing: 1 1530 | m_Text: record 1531 | --- !u!222 &1891684391 1532 | CanvasRenderer: 1533 | m_ObjectHideFlags: 0 1534 | m_CorrespondingSourceObject: {fileID: 0} 1535 | m_PrefabInstance: {fileID: 0} 1536 | m_PrefabAsset: {fileID: 0} 1537 | m_GameObject: {fileID: 1891684388} 1538 | m_CullTransparentMesh: 0 1539 | --- !u!1 &1968714627 1540 | GameObject: 1541 | m_ObjectHideFlags: 0 1542 | m_CorrespondingSourceObject: {fileID: 0} 1543 | m_PrefabInstance: {fileID: 0} 1544 | m_PrefabAsset: {fileID: 0} 1545 | serializedVersion: 6 1546 | m_Component: 1547 | - component: {fileID: 1968714628} 1548 | - component: {fileID: 1968714631} 1549 | - component: {fileID: 1968714630} 1550 | - component: {fileID: 1968714629} 1551 | m_Layer: 5 1552 | m_Name: RecordButton 1553 | m_TagString: Untagged 1554 | m_Icon: {fileID: 0} 1555 | m_NavMeshLayer: 0 1556 | m_StaticEditorFlags: 0 1557 | m_IsActive: 1 1558 | --- !u!224 &1968714628 1559 | RectTransform: 1560 | m_ObjectHideFlags: 0 1561 | m_CorrespondingSourceObject: {fileID: 0} 1562 | m_PrefabInstance: {fileID: 0} 1563 | m_PrefabAsset: {fileID: 0} 1564 | m_GameObject: {fileID: 1968714627} 1565 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 1566 | m_LocalPosition: {x: 0, y: 0, z: 0} 1567 | m_LocalScale: {x: 1, y: 1, z: 1} 1568 | m_Children: 1569 | - {fileID: 1263009179} 1570 | - {fileID: 1891684389} 1571 | m_Father: {fileID: 174072919} 1572 | m_RootOrder: 0 1573 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 1574 | m_AnchorMin: {x: 0.5, y: 0.5} 1575 | m_AnchorMax: {x: 0.5, y: 0.5} 1576 | m_AnchoredPosition: {x: -140, y: 0} 1577 | m_SizeDelta: {x: 100, y: 100} 1578 | m_Pivot: {x: 0.5, y: 0.5} 1579 | --- !u!114 &1968714629 1580 | MonoBehaviour: 1581 | m_ObjectHideFlags: 0 1582 | m_CorrespondingSourceObject: {fileID: 0} 1583 | m_PrefabInstance: {fileID: 0} 1584 | m_PrefabAsset: {fileID: 0} 1585 | m_GameObject: {fileID: 1968714627} 1586 | m_Enabled: 1 1587 | m_EditorHideFlags: 0 1588 | m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} 1589 | m_Name: 1590 | m_EditorClassIdentifier: 1591 | m_Navigation: 1592 | m_Mode: 3 1593 | m_SelectOnUp: {fileID: 0} 1594 | m_SelectOnDown: {fileID: 0} 1595 | m_SelectOnLeft: {fileID: 0} 1596 | m_SelectOnRight: {fileID: 0} 1597 | m_Transition: 1 1598 | m_Colors: 1599 | m_NormalColor: {r: 1, g: 1, b: 1, a: 1} 1600 | m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 1601 | m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} 1602 | m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 1603 | m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} 1604 | m_ColorMultiplier: 1 1605 | m_FadeDuration: 0.1 1606 | m_SpriteState: 1607 | m_HighlightedSprite: {fileID: 0} 1608 | m_PressedSprite: {fileID: 0} 1609 | m_SelectedSprite: {fileID: 0} 1610 | m_DisabledSprite: {fileID: 0} 1611 | m_AnimationTriggers: 1612 | m_NormalTrigger: Normal 1613 | m_HighlightedTrigger: Highlighted 1614 | m_PressedTrigger: Pressed 1615 | m_SelectedTrigger: Selected 1616 | m_DisabledTrigger: Disabled 1617 | m_Interactable: 1 1618 | m_TargetGraphic: {fileID: 1968714630} 1619 | m_OnClick: 1620 | m_PersistentCalls: 1621 | m_Calls: 1622 | - m_Target: {fileID: 1869404003} 1623 | m_MethodName: Record 1624 | m_Mode: 1 1625 | m_Arguments: 1626 | m_ObjectArgument: {fileID: 0} 1627 | m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine 1628 | m_IntArgument: 0 1629 | m_FloatArgument: 0 1630 | m_StringArgument: 1631 | m_BoolArgument: 0 1632 | m_CallState: 2 1633 | --- !u!114 &1968714630 1634 | MonoBehaviour: 1635 | m_ObjectHideFlags: 0 1636 | m_CorrespondingSourceObject: {fileID: 0} 1637 | m_PrefabInstance: {fileID: 0} 1638 | m_PrefabAsset: {fileID: 0} 1639 | m_GameObject: {fileID: 1968714627} 1640 | m_Enabled: 1 1641 | m_EditorHideFlags: 0 1642 | m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} 1643 | m_Name: 1644 | m_EditorClassIdentifier: 1645 | m_Material: {fileID: 0} 1646 | m_Color: {r: 0.12259702, g: 0.12885468, b: 0.1792453, a: 1} 1647 | m_RaycastTarget: 1 1648 | m_OnCullStateChanged: 1649 | m_PersistentCalls: 1650 | m_Calls: [] 1651 | m_Sprite: {fileID: 0} 1652 | m_Type: 0 1653 | m_PreserveAspect: 0 1654 | m_FillCenter: 1 1655 | m_FillMethod: 4 1656 | m_FillAmount: 1 1657 | m_FillClockwise: 1 1658 | m_FillOrigin: 0 1659 | m_UseSpriteMesh: 0 1660 | m_PixelsPerUnitMultiplier: 1 1661 | --- !u!222 &1968714631 1662 | CanvasRenderer: 1663 | m_ObjectHideFlags: 0 1664 | m_CorrespondingSourceObject: {fileID: 0} 1665 | m_PrefabInstance: {fileID: 0} 1666 | m_PrefabAsset: {fileID: 0} 1667 | m_GameObject: {fileID: 1968714627} 1668 | m_CullTransparentMesh: 0 1669 | --- !u!1 &1995536886 1670 | GameObject: 1671 | m_ObjectHideFlags: 0 1672 | m_CorrespondingSourceObject: {fileID: 0} 1673 | m_PrefabInstance: {fileID: 0} 1674 | m_PrefabAsset: {fileID: 0} 1675 | serializedVersion: 6 1676 | m_Component: 1677 | - component: {fileID: 1995536890} 1678 | - component: {fileID: 1995536889} 1679 | - component: {fileID: 1995536888} 1680 | - component: {fileID: 1995536887} 1681 | m_Layer: 5 1682 | m_Name: Canvas 1683 | m_TagString: Untagged 1684 | m_Icon: {fileID: 0} 1685 | m_NavMeshLayer: 0 1686 | m_StaticEditorFlags: 0 1687 | m_IsActive: 1 1688 | --- !u!114 &1995536887 1689 | MonoBehaviour: 1690 | m_ObjectHideFlags: 0 1691 | m_CorrespondingSourceObject: {fileID: 0} 1692 | m_PrefabInstance: {fileID: 0} 1693 | m_PrefabAsset: {fileID: 0} 1694 | m_GameObject: {fileID: 1995536886} 1695 | m_Enabled: 1 1696 | m_EditorHideFlags: 0 1697 | m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} 1698 | m_Name: 1699 | m_EditorClassIdentifier: 1700 | m_IgnoreReversedGraphics: 1 1701 | m_BlockingObjects: 0 1702 | m_BlockingMask: 1703 | serializedVersion: 2 1704 | m_Bits: 4294967295 1705 | --- !u!114 &1995536888 1706 | MonoBehaviour: 1707 | m_ObjectHideFlags: 0 1708 | m_CorrespondingSourceObject: {fileID: 0} 1709 | m_PrefabInstance: {fileID: 0} 1710 | m_PrefabAsset: {fileID: 0} 1711 | m_GameObject: {fileID: 1995536886} 1712 | m_Enabled: 1 1713 | m_EditorHideFlags: 0 1714 | m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} 1715 | m_Name: 1716 | m_EditorClassIdentifier: 1717 | m_UiScaleMode: 1 1718 | m_ReferencePixelsPerUnit: 100 1719 | m_ScaleFactor: 1 1720 | m_ReferenceResolution: {x: 1024, y: 768} 1721 | m_ScreenMatchMode: 0 1722 | m_MatchWidthOrHeight: 1 1723 | m_PhysicalUnit: 3 1724 | m_FallbackScreenDPI: 96 1725 | m_DefaultSpriteDPI: 96 1726 | m_DynamicPixelsPerUnit: 1 1727 | --- !u!223 &1995536889 1728 | Canvas: 1729 | m_ObjectHideFlags: 0 1730 | m_CorrespondingSourceObject: {fileID: 0} 1731 | m_PrefabInstance: {fileID: 0} 1732 | m_PrefabAsset: {fileID: 0} 1733 | m_GameObject: {fileID: 1995536886} 1734 | m_Enabled: 1 1735 | serializedVersion: 3 1736 | m_RenderMode: 0 1737 | m_Camera: {fileID: 0} 1738 | m_PlaneDistance: 100 1739 | m_PixelPerfect: 0 1740 | m_ReceivesEvents: 1 1741 | m_OverrideSorting: 0 1742 | m_OverridePixelPerfect: 0 1743 | m_SortingBucketNormalizedSize: 0 1744 | m_AdditionalShaderChannelsFlag: 0 1745 | m_SortingLayerID: 0 1746 | m_SortingOrder: 0 1747 | m_TargetDisplay: 0 1748 | --- !u!224 &1995536890 1749 | RectTransform: 1750 | m_ObjectHideFlags: 0 1751 | m_CorrespondingSourceObject: {fileID: 0} 1752 | m_PrefabInstance: {fileID: 0} 1753 | m_PrefabAsset: {fileID: 0} 1754 | m_GameObject: {fileID: 1995536886} 1755 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 1756 | m_LocalPosition: {x: 0, y: 0, z: 0} 1757 | m_LocalScale: {x: 0, y: 0, z: 0} 1758 | m_Children: 1759 | - {fileID: 1091692663} 1760 | - {fileID: 174072919} 1761 | m_Father: {fileID: 0} 1762 | m_RootOrder: 1 1763 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 1764 | m_AnchorMin: {x: 0, y: 0} 1765 | m_AnchorMax: {x: 0, y: 0} 1766 | m_AnchoredPosition: {x: 0, y: 0} 1767 | m_SizeDelta: {x: 0, y: 0} 1768 | m_Pivot: {x: 0, y: 0} 1769 | --- !u!1 &2093230658 1770 | GameObject: 1771 | m_ObjectHideFlags: 0 1772 | m_CorrespondingSourceObject: {fileID: 0} 1773 | m_PrefabInstance: {fileID: 0} 1774 | m_PrefabAsset: {fileID: 0} 1775 | serializedVersion: 6 1776 | m_Component: 1777 | - component: {fileID: 2093230659} 1778 | - component: {fileID: 2093230662} 1779 | - component: {fileID: 2093230661} 1780 | - component: {fileID: 2093230660} 1781 | m_Layer: 5 1782 | m_Name: RewindButton 1783 | m_TagString: Untagged 1784 | m_Icon: {fileID: 0} 1785 | m_NavMeshLayer: 0 1786 | m_StaticEditorFlags: 0 1787 | m_IsActive: 1 1788 | --- !u!224 &2093230659 1789 | RectTransform: 1790 | m_ObjectHideFlags: 0 1791 | m_CorrespondingSourceObject: {fileID: 0} 1792 | m_PrefabInstance: {fileID: 0} 1793 | m_PrefabAsset: {fileID: 0} 1794 | m_GameObject: {fileID: 2093230658} 1795 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 1796 | m_LocalPosition: {x: 0, y: 0, z: 0} 1797 | m_LocalScale: {x: 1, y: 1, z: 1} 1798 | m_Children: 1799 | - {fileID: 515249587} 1800 | - {fileID: 393972365} 1801 | m_Father: {fileID: 174072919} 1802 | m_RootOrder: 3 1803 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 1804 | m_AnchorMin: {x: 0.5, y: 0.5} 1805 | m_AnchorMax: {x: 0.5, y: 0.5} 1806 | m_AnchoredPosition: {x: 138.90001, y: 0} 1807 | m_SizeDelta: {x: 100, y: 100} 1808 | m_Pivot: {x: 0.5, y: 0.5} 1809 | --- !u!114 &2093230660 1810 | MonoBehaviour: 1811 | m_ObjectHideFlags: 0 1812 | m_CorrespondingSourceObject: {fileID: 0} 1813 | m_PrefabInstance: {fileID: 0} 1814 | m_PrefabAsset: {fileID: 0} 1815 | m_GameObject: {fileID: 2093230658} 1816 | m_Enabled: 1 1817 | m_EditorHideFlags: 0 1818 | m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} 1819 | m_Name: 1820 | m_EditorClassIdentifier: 1821 | m_Navigation: 1822 | m_Mode: 3 1823 | m_SelectOnUp: {fileID: 0} 1824 | m_SelectOnDown: {fileID: 0} 1825 | m_SelectOnLeft: {fileID: 0} 1826 | m_SelectOnRight: {fileID: 0} 1827 | m_Transition: 1 1828 | m_Colors: 1829 | m_NormalColor: {r: 1, g: 1, b: 1, a: 1} 1830 | m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 1831 | m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} 1832 | m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} 1833 | m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} 1834 | m_ColorMultiplier: 1 1835 | m_FadeDuration: 0.1 1836 | m_SpriteState: 1837 | m_HighlightedSprite: {fileID: 0} 1838 | m_PressedSprite: {fileID: 0} 1839 | m_SelectedSprite: {fileID: 0} 1840 | m_DisabledSprite: {fileID: 0} 1841 | m_AnimationTriggers: 1842 | m_NormalTrigger: Normal 1843 | m_HighlightedTrigger: Highlighted 1844 | m_PressedTrigger: Pressed 1845 | m_SelectedTrigger: Selected 1846 | m_DisabledTrigger: Disabled 1847 | m_Interactable: 1 1848 | m_TargetGraphic: {fileID: 2093230661} 1849 | m_OnClick: 1850 | m_PersistentCalls: 1851 | m_Calls: 1852 | - m_Target: {fileID: 1869404003} 1853 | m_MethodName: RewindToStart 1854 | m_Mode: 1 1855 | m_Arguments: 1856 | m_ObjectArgument: {fileID: 0} 1857 | m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine 1858 | m_IntArgument: 0 1859 | m_FloatArgument: 0 1860 | m_StringArgument: 1861 | m_BoolArgument: 0 1862 | m_CallState: 2 1863 | --- !u!114 &2093230661 1864 | MonoBehaviour: 1865 | m_ObjectHideFlags: 0 1866 | m_CorrespondingSourceObject: {fileID: 0} 1867 | m_PrefabInstance: {fileID: 0} 1868 | m_PrefabAsset: {fileID: 0} 1869 | m_GameObject: {fileID: 2093230658} 1870 | m_Enabled: 1 1871 | m_EditorHideFlags: 0 1872 | m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} 1873 | m_Name: 1874 | m_EditorClassIdentifier: 1875 | m_Material: {fileID: 0} 1876 | m_Color: {r: 0.12259702, g: 0.12885468, b: 0.1792453, a: 1} 1877 | m_RaycastTarget: 1 1878 | m_OnCullStateChanged: 1879 | m_PersistentCalls: 1880 | m_Calls: [] 1881 | m_Sprite: {fileID: 0} 1882 | m_Type: 0 1883 | m_PreserveAspect: 0 1884 | m_FillCenter: 1 1885 | m_FillMethod: 4 1886 | m_FillAmount: 1 1887 | m_FillClockwise: 1 1888 | m_FillOrigin: 0 1889 | m_UseSpriteMesh: 0 1890 | m_PixelsPerUnitMultiplier: 1 1891 | --- !u!222 &2093230662 1892 | CanvasRenderer: 1893 | m_ObjectHideFlags: 0 1894 | m_CorrespondingSourceObject: {fileID: 0} 1895 | m_PrefabInstance: {fileID: 0} 1896 | m_PrefabAsset: {fileID: 0} 1897 | m_GameObject: {fileID: 2093230658} 1898 | m_CullTransparentMesh: 0 1899 | --- !u!1 &2137327214 1900 | GameObject: 1901 | m_ObjectHideFlags: 0 1902 | m_CorrespondingSourceObject: {fileID: 0} 1903 | m_PrefabInstance: {fileID: 0} 1904 | m_PrefabAsset: {fileID: 0} 1905 | serializedVersion: 6 1906 | m_Component: 1907 | - component: {fileID: 2137327215} 1908 | - component: {fileID: 2137327217} 1909 | - component: {fileID: 2137327216} 1910 | m_Layer: 5 1911 | m_Name: Text (1) 1912 | m_TagString: Untagged 1913 | m_Icon: {fileID: 0} 1914 | m_NavMeshLayer: 0 1915 | m_StaticEditorFlags: 0 1916 | m_IsActive: 1 1917 | --- !u!224 &2137327215 1918 | RectTransform: 1919 | m_ObjectHideFlags: 0 1920 | m_CorrespondingSourceObject: {fileID: 0} 1921 | m_PrefabInstance: {fileID: 0} 1922 | m_PrefabAsset: {fileID: 0} 1923 | m_GameObject: {fileID: 2137327214} 1924 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 1925 | m_LocalPosition: {x: 0, y: 0, z: 0} 1926 | m_LocalScale: {x: 1, y: 1, z: 1} 1927 | m_Children: [] 1928 | m_Father: {fileID: 275241080} 1929 | m_RootOrder: 1 1930 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 1931 | m_AnchorMin: {x: 0.5, y: 0.5} 1932 | m_AnchorMax: {x: 0.5, y: 0.5} 1933 | m_AnchoredPosition: {x: 0, y: 3.83} 1934 | m_SizeDelta: {x: 100, y: 92.34} 1935 | m_Pivot: {x: 0.5, y: 0.5} 1936 | --- !u!114 &2137327216 1937 | MonoBehaviour: 1938 | m_ObjectHideFlags: 0 1939 | m_CorrespondingSourceObject: {fileID: 0} 1940 | m_PrefabInstance: {fileID: 0} 1941 | m_PrefabAsset: {fileID: 0} 1942 | m_GameObject: {fileID: 2137327214} 1943 | m_Enabled: 1 1944 | m_EditorHideFlags: 0 1945 | m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} 1946 | m_Name: 1947 | m_EditorClassIdentifier: 1948 | m_Material: {fileID: 0} 1949 | m_Color: {r: 1, g: 1, b: 1, a: 1} 1950 | m_RaycastTarget: 1 1951 | m_OnCullStateChanged: 1952 | m_PersistentCalls: 1953 | m_Calls: [] 1954 | m_FontData: 1955 | m_Font: {fileID: 12800000, guid: e506d2e857d294d99966099453ec9116, type: 3} 1956 | m_FontSize: 24 1957 | m_FontStyle: 0 1958 | m_BestFit: 0 1959 | m_MinSize: 2 1960 | m_MaxSize: 50 1961 | m_Alignment: 7 1962 | m_AlignByGeometry: 0 1963 | m_RichText: 1 1964 | m_HorizontalOverflow: 0 1965 | m_VerticalOverflow: 0 1966 | m_LineSpacing: 1 1967 | m_Text: pause 1968 | --- !u!222 &2137327217 1969 | CanvasRenderer: 1970 | m_ObjectHideFlags: 0 1971 | m_CorrespondingSourceObject: {fileID: 0} 1972 | m_PrefabInstance: {fileID: 0} 1973 | m_PrefabAsset: {fileID: 0} 1974 | m_GameObject: {fileID: 2137327214} 1975 | m_CullTransparentMesh: 0 1976 | -------------------------------------------------------------------------------- /Runtime/Examples/InputVCRExample.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4a3535f2b9f044553bb238e3d233775a 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Examples/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EddieCameron/InputVCR/cfaf1b003520d2b15772badbf1f1af2444d8ba93/Runtime/Examples/Roboto-Regular.ttf -------------------------------------------------------------------------------- /Runtime/Examples/Roboto-Regular.ttf.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e506d2e857d294d99966099453ec9116 3 | TrueTypeFontImporter: 4 | externalObjects: {} 5 | serializedVersion: 4 6 | fontSize: 16 7 | forceTextureCase: -2 8 | characterSpacing: 0 9 | characterPadding: 1 10 | includeFontData: 1 11 | fontName: Roboto 12 | fontNames: 13 | - Roboto 14 | fallbackFontReferences: [] 15 | customCharacters: 16 | fontRenderingMode: 0 17 | ascentCalculationMode: 1 18 | useLegacyBoundsCalculation: 0 19 | shouldRoundAdvanceValue: 1 20 | userData: 21 | assetBundleName: 22 | assetBundleVariant: 23 | -------------------------------------------------------------------------------- /Runtime/Examples/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 854336c053cd04898bc44475ccf7cd93 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Examples/Scripts/CharacterController2D.cs: -------------------------------------------------------------------------------- 1 | /* CharacterController2D.cs 2 | * Copyright Eddie Cameron 2019 (See readme for licence) 3 | * ---------- 4 | */ 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using UnityEngine; 8 | using InputVCR; 9 | 10 | namespace InputVCRExamples { 11 | public class CharacterController2D : MonoBehaviour { 12 | public InputVCRRecorder recorder; 13 | 14 | public float maxSpeed; 15 | public float damping; 16 | 17 | Vector2 velocity; 18 | void Update() { 19 | // Get input from recorder 20 | // Note this only doesn't use InputManager Axes/Button names because your project might have changed the default settings 21 | Vector2 inputDirection = Vector2.zero; 22 | if ( recorder.GetKey( KeyCode.W ) ) 23 | inputDirection.y += 1f; 24 | if ( recorder.GetKey( KeyCode.S ) ) 25 | inputDirection.y -= 1f; 26 | if ( recorder.GetKey( KeyCode.D ) ) 27 | inputDirection.x += 1f; 28 | if ( recorder.GetKey( KeyCode.A ) ) 29 | inputDirection.x -= 1f; 30 | 31 | if ( inputDirection.sqrMagnitude > 1 ) 32 | inputDirection.Normalize(); 33 | 34 | velocity = Vector2.Lerp( velocity, inputDirection * maxSpeed, damping * Time.deltaTime ); 35 | transform.localPosition = transform.localPosition + (Vector3)velocity * Time.deltaTime; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Runtime/Examples/Scripts/CharacterController2D.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bccf992011caf4fd4b18fa1a9defa80b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Examples/Scripts/VCRControls.cs: -------------------------------------------------------------------------------- 1 | /* VCRControls.cs 2 | * Copyright Eddie Cameron 2019 (See readme for licence) 3 | * ---------- 4 | */ 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using InputVCR; 8 | using UnityEngine; 9 | using UnityEngine.UI; 10 | 11 | namespace InputVCRExamples { 12 | public class VCRControls : MonoBehaviour { 13 | 14 | public Button recordButton, playButton, pauseButton, rewindButton; 15 | public Text timeText; 16 | 17 | public InputVCRRecorder vcr; 18 | 19 | // Start is called before the first frame update 20 | void Start() { 21 | 22 | } 23 | 24 | // Update is called once per frame 25 | void Update() { 26 | bool recordEnabled = vcr.Mode != InputVCRMode.Record; 27 | recordButton.interactable = recordEnabled; 28 | 29 | bool playEnabled = vcr.Mode == InputVCRMode.Passthru || vcr.IsPaused; 30 | playButton.gameObject.SetActive( playEnabled ); 31 | pauseButton.gameObject.SetActive( !playEnabled ); 32 | 33 | if ( vcr.CurrentRecording == null ) 34 | timeText.text = "no recording"; 35 | else 36 | timeText.text = $"{vcr.CurrentPlaybackTime:f2}/{vcr.CurrentRecording.Length:f2}"; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Runtime/Examples/Scripts/VCRControls.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5352d274212da42bc895a9f371a01eda 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 509914d1c430a49949fd013727f9a6cb 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Scripts/InputVCR.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "InputVCR" 3 | } 4 | -------------------------------------------------------------------------------- /Runtime/Scripts/InputVCR.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dab65278143d24f128fdccc4cf61752a 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Scripts/InputVCRRecorder.cs: -------------------------------------------------------------------------------- 1 | /* InputVCR.cs 2 | * Copyright Eddie Cameron 2019 (See readme for licence) 3 | * ---------- 4 | * Place on any object you wish to use to record or playback any inputs for 5 | * Use Record() and Play() to create and play back Recording objects 6 | * ----------- 7 | * CurrentRecording holds a reference to the Recording last recorded to or played back from. 8 | * Create a clone with new Recording( recordingToClone ) on it to create a C# copy that can later be played in this or another Recorder 9 | * or, Call ToJson() on it to get a serialized string that can be written to disk. 10 | * (note, these recordings can get quite large, so I suggest either compression or adding binary serialization for long recordings) 11 | * ----------- 12 | * To record, add a list of button name strings from Input Manager, or a list of Keyboard/controller keys to save. 13 | * ----------- 14 | * Scripts that need to listen to playback need to call Input methods on this recorder instead of Input. ie: call thisVcrRecorder.GetButton( "Jump" ) instead of Input.GetButton( "Jump" ). 15 | * When recording or not playing, these methods will just return whatever the Input methods would return, while when playing they will return whatever state was recorded 16 | * ----------- 17 | * You can also record arbitrary values to the recording, eg: save the position of an object in each frame. 18 | * To do this, call SetProperty() with a tag and the value of the property (in string form), while recording. 19 | * During playback, call TryGetProperty() with the same tag, and if successful, you will get the recorded value back 20 | * Easy! 21 | * ------------- 22 | * See the Examples folder for usage examples 23 | * 24 | * More information and tools at eddiecameron.fun, or twitter @eddiecameron 25 | * 26 | * This script is open source under the MIT licence. Do what you will with it! 27 | */ 28 | using UnityEngine; 29 | using System.Collections; 30 | using System.Text; 31 | using System.IO; 32 | using System.Collections.Generic; 33 | using System; 34 | 35 | namespace InputVCR { 36 | public class InputVCRRecorder : MonoBehaviour { 37 | #region Inspector properties 38 | [Header( "Recorded Inputs" )] 39 | [Tooltip( "Button names (from Input manager) that should be recorded" )] 40 | [SerializeField] 41 | List _recordedButtons = new List(); 42 | 43 | [Tooltip( "Axis names (from Input manager) that should be recorded" )] 44 | [SerializeField] 45 | List _recordedAxes = new List(); 46 | 47 | [Tooltip( "Key buttons that should be recorded" )] 48 | [SerializeField] 49 | List _recordedKeys = new List(); 50 | 51 | [Tooltip( "Whether mouse position/button states should be recorded each frame (mouse axes are separate from this)" )] 52 | public bool recordMouseEvents; 53 | 54 | [SerializeField] 55 | [HideInInspector] 56 | private InputVCRMode _mode = InputVCRMode.Passthru; // initial mode that vcr is operating in 57 | /// 58 | /// Is this VCR: 59 | /// - Recording: Taking live input and writing to a Recording and passing it on to callers 60 | /// - Playback: Replaying input from a previous Recording 61 | /// - Passthrough: Taking live input and passing on to callers 62 | /// 63 | public InputVCRMode Mode { 64 | get { return _mode; } 65 | private set { _mode = value; } 66 | } 67 | 68 | /// 69 | /// Whether current playback or recording is paused. If there is no current recording or playback, Input is not affected 70 | /// 71 | /// 72 | public bool IsPaused { get; set; } 73 | #endregion 74 | 75 | /// 76 | /// Holds info about the playback/record state. eg: playback time 77 | /// 78 | RecordingState recordingState = new RecordingState( new Recording() ); // the recording currently in the VCR. Copy or ToString() this to save. 79 | public Recording CurrentRecording => recordingState.targetRecording; 80 | public float CurrentPlaybackTime { 81 | get { 82 | return recordingState.Time; 83 | } 84 | set { 85 | recordingState.SkipToTime( value ); 86 | if ( Mode == InputVCRMode.Record ) { 87 | // wipe any recording after current time 88 | recordingState.targetRecording.ClearFrames( recordingState.FrameIdx ); 89 | } 90 | } 91 | } 92 | 93 | Queue nextPropertiesToRecord = new Queue(); // if SyncLocation or SyncProperty are called, this will hold their results until the recordstring is next written to 94 | 95 | Dictionary lastFrameInputs = new Dictionary(); // list of inputs from last frame (for seeing what buttons have changed state) 96 | Dictionary thisFrameInputs = new Dictionary(); 97 | Dictionary thisFrameProperties = new Dictionary(); 98 | 99 | public event System.Action finishedPlayback; // sent when playback finishes 100 | 101 | /// 102 | /// Start recording. Will append to current Recording, wiping all data after current playback time 103 | /// 104 | public void Record() { 105 | Record( forceNewRecording: false ); 106 | } 107 | 108 | /// 109 | /// Start recording onto the provided Recording. Any frames after the given startRecordingFromTime will be wiped 110 | /// 111 | /// 112 | /// 113 | public void Record( Recording appendRecording, float startRecordingFromTime ) { 114 | recordingState = new RecordingState( appendRecording ); 115 | recordingState.SkipToTime( startRecordingFromTime ); 116 | 117 | Record( forceNewRecording: false ); 118 | } 119 | 120 | /// 121 | /// Start recording, any current Recording will be wiped and a new recording begun 122 | /// 123 | public void RecordNew() { 124 | Record( forceNewRecording: true ); 125 | } 126 | 127 | void Record( bool forceNewRecording ) { 128 | if ( forceNewRecording ) { 129 | recordingState = new RecordingState( new Recording() ); 130 | nextPropertiesToRecord.Clear(); 131 | } 132 | else { 133 | recordingState.targetRecording.ClearFrames( recordingState.FrameIdx + 1 ); 134 | } 135 | 136 | Mode = InputVCRMode.Record; 137 | IsPaused = false; 138 | ClearInput(); 139 | } 140 | 141 | /// 142 | /// Start or resume playing back the current Recording, if present. 143 | /// 144 | public void Play() { 145 | if ( Mode != InputVCRMode.Playback ) { 146 | ClearInput(); // dont' clear if just resuming playback 147 | Mode = InputVCRMode.Playback; 148 | } 149 | IsPaused = false; 150 | } 151 | 152 | /// 153 | /// Play the specified Recording, from optional specified time 154 | /// 155 | /// 156 | /// Recording. 157 | /// 158 | /// 159 | /// 160 | public void Play( Recording recording, float startPlaybackFromTime = 0 ) { 161 | recordingState = new RecordingState( recording ); 162 | recordingState.SkipToTime( startPlaybackFromTime ); 163 | 164 | ClearInput(); 165 | 166 | Mode = InputVCRMode.Playback; 167 | } 168 | 169 | /// 170 | /// Pause recording or playback 171 | /// 172 | public void Pause() { 173 | IsPaused = true; 174 | } 175 | 176 | /// 177 | /// Set the current recording's (if present) playback time to the beginning 178 | /// 179 | public void RewindToStart() { 180 | if ( Mode == InputVCRMode.Record ) 181 | Stop(); 182 | CurrentPlaybackTime = 0; 183 | } 184 | 185 | /// 186 | /// Stop recording or playback. Live input will be passed through 187 | /// 188 | public void Stop() { 189 | Mode = InputVCRMode.Passthru; 190 | ClearInput(); 191 | } 192 | 193 | /// 194 | /// Stop playback or recording, if present, and load a recording 195 | /// 196 | /// 197 | public void LoadRecording( Recording newRecording ) { 198 | Stop(); 199 | recordingState = new RecordingState( newRecording ); 200 | } 201 | 202 | /// 203 | /// Adds a custom property to the recording, so you can sync other (non-input) events as well. 204 | /// eg: doors opening, enemy spawning, etc 205 | /// 206 | /// 207 | /// Property name. 208 | /// 209 | /// 210 | /// Property value. 211 | /// 212 | public void SaveProperty( string propertyName, string propertyValue ) { 213 | // duplicates dealt with when recorded 214 | var frameProp = new Recording.FrameProperty( propertyName, propertyValue ); 215 | if ( !nextPropertiesToRecord.Contains( frameProp ) ) 216 | nextPropertiesToRecord.Enqueue( frameProp ); 217 | } 218 | 219 | #region Recording & Playback 220 | void Update() { 221 | if ( IsPaused ) 222 | return; 223 | 224 | if ( Mode == InputVCRMode.Playback ) { 225 | AdvancePlayback( Time.deltaTime ); 226 | } 227 | else if ( Mode == InputVCRMode.Record ) { 228 | RecordCurrentFrame(); 229 | } 230 | } 231 | 232 | List _inputStateCache = new List(); 233 | List _propCache = new List(); 234 | void AdvancePlayback( float delta ) { 235 | int lastFrameIdx = recordingState.FrameIdx; 236 | recordingState.AdvanceByTime( delta ); 237 | if ( recordingState.Time > recordingState.targetRecording.Length ) { 238 | // reached end of recording 239 | finishedPlayback?.Invoke(); 240 | Stop(); 241 | return; 242 | } 243 | 244 | if ( recordingState.FrameIdx == lastFrameIdx ) 245 | return; // no new playback data TODO clear deltas if saved 246 | 247 | // duplicate this input to previous frame 248 | lastFrameInputs.Clear(); 249 | foreach ( var input in thisFrameInputs ) { 250 | lastFrameInputs[input.Key] = input.Value; 251 | } 252 | 253 | // update inputs with changes from each playback frame since last real frame 254 | for ( int checkFrame = lastFrameIdx + 1; checkFrame <= recordingState.FrameIdx; checkFrame++ ) { 255 | // Inputs 256 | _inputStateCache.Clear(); 257 | recordingState.targetRecording.GetInputs( checkFrame, _inputStateCache ); 258 | foreach ( var input in _inputStateCache ) { 259 | // if a button was pressed and released within a single playback frame, key up/down flags could be missed 260 | // TODO fix by either using events to signal key up/downs (not ideal) or buffering up/downs over subsequent frames (less ideal) 261 | thisFrameInputs[input.inputId] = input; 262 | } 263 | 264 | // Properties 265 | _propCache.Clear(); 266 | recordingState.targetRecording.GetProperties( checkFrame, _propCache ); 267 | foreach ( var prop in _propCache ) { 268 | thisFrameProperties[prop.name] = prop; 269 | } 270 | } 271 | } 272 | 273 | void RecordCurrentFrame() { 274 | recordingState.AppendNewRecordingFrame( Time.deltaTime ); 275 | 276 | // mouse 277 | if ( recordMouseEvents ) { 278 | recordingState.AddInputToCurrentFrame( new Recording.InputState( GetMouseButtonId( 0 ), Input.GetMouseButton( 0 ) ) ); 279 | recordingState.AddInputToCurrentFrame( new Recording.InputState( GetMouseButtonId( 1 ), Input.GetMouseButton( 1 ) ) ); 280 | recordingState.AddInputToCurrentFrame( new Recording.InputState( GetMouseButtonId( 2 ), Input.GetMouseButton( 2 ) ) ); 281 | recordingState.AddInputToCurrentFrame( new Recording.InputState( _MOUSE_POS_X_ID, Input.mousePosition.x ) ); 282 | recordingState.AddInputToCurrentFrame( new Recording.InputState( _MOUSE_POS_Y_ID, Input.mousePosition.y ) ); 283 | } 284 | 285 | // buttons 286 | foreach ( var buttonName in _recordedButtons ) { 287 | recordingState.AddInputToCurrentFrame( new Recording.InputState( buttonName, Input.GetButton( buttonName ) ) ); 288 | } 289 | 290 | // axes 291 | foreach ( var axisName in _recordedAxes ) { 292 | recordingState.AddInputToCurrentFrame( new Recording.InputState( axisName, Input.GetAxis( axisName ) ) ); 293 | } 294 | 295 | // keys 296 | foreach ( var keyCode in _recordedKeys ) { 297 | recordingState.AddInputToCurrentFrame( new Recording.InputState( GetKeyCodeId( keyCode ), Input.GetKey( keyCode ) ) ); 298 | } 299 | 300 | // properties 301 | while ( nextPropertiesToRecord.Count > 0 ) { 302 | recordingState.AddPropertyToCurrentFrame( nextPropertiesToRecord.Dequeue() ); 303 | } 304 | } 305 | 306 | 307 | #endregion 308 | /// 309 | /// Wipe current playback data 310 | /// 311 | void ClearInput() { 312 | thisFrameInputs.Clear(); 313 | lastFrameInputs.Clear(); 314 | thisFrameProperties.Clear(); 315 | } 316 | 317 | // These methods replace those in Input, so that this object can ignore whether it is record 318 | #region Input replacements 319 | public bool GetKey( KeyCode key ) => GetKey( GetKeyCodeId( key ) ); 320 | 321 | public bool GetKey( string keyName ) { 322 | if ( Mode == InputVCRMode.Playback ) { 323 | return thisFrameInputs.TryGetValue( keyName, out Recording.InputState thisFrameState ) && thisFrameState.buttonState; 324 | } 325 | 326 | return Input.GetKey( keyName ); 327 | } 328 | 329 | public bool GetKeyDown( KeyCode key ) => GetKeyDown( GetKeyCodeId( key ) ); 330 | public bool GetKeyDown( string keyName ) { 331 | if ( Mode == InputVCRMode.Playback ) { 332 | bool lastFrameButtonDown = lastFrameInputs.TryGetValue( keyName, out Recording.InputState lastFrameState ) && lastFrameState.buttonState; 333 | bool thisFrameButtonDown = thisFrameInputs.TryGetValue( keyName, out Recording.InputState thisFrameState ) && thisFrameState.buttonState; 334 | return thisFrameButtonDown && !lastFrameButtonDown; 335 | } 336 | 337 | return Input.GetKeyDown( keyName ); 338 | } 339 | 340 | public bool GetKeyUp( KeyCode key ) => GetKeyUp( GetKeyCodeId( key ) ); 341 | public bool GetKeyUp( string keyName ) { 342 | if ( Mode == InputVCRMode.Playback ) { 343 | bool lastFrameButtonDown = lastFrameInputs.TryGetValue( keyName, out Recording.InputState lastFrameState ) && lastFrameState.buttonState; 344 | bool thisFrameButtonDown = thisFrameInputs.TryGetValue( keyName, out Recording.InputState thisFrameState ) && thisFrameState.buttonState; 345 | return !thisFrameButtonDown && lastFrameButtonDown; 346 | } 347 | 348 | return Input.GetKeyUp( keyName ); 349 | } 350 | 351 | public bool GetButton( string buttonName ) { 352 | if ( Mode == InputVCRMode.Playback ) { 353 | return thisFrameInputs.TryGetValue( buttonName, out Recording.InputState thisFrameState ) && thisFrameState.buttonState; 354 | } 355 | 356 | return Input.GetButton( buttonName ); 357 | } 358 | 359 | public bool GetButtonDown( string buttonName ) { 360 | if ( Mode == InputVCRMode.Playback ) { 361 | bool lastFrameButtonDown = lastFrameInputs.TryGetValue( buttonName, out Recording.InputState lastFrameState ) && lastFrameState.buttonState; 362 | bool thisFrameButtonDown = thisFrameInputs.TryGetValue( buttonName, out Recording.InputState thisFrameState ) && thisFrameState.buttonState; 363 | return thisFrameButtonDown && !lastFrameButtonDown; 364 | } 365 | 366 | return Input.GetButtonDown( buttonName ); 367 | } 368 | 369 | public bool GetButtonUp( string buttonName ) { 370 | if ( Mode == InputVCRMode.Playback ) { 371 | bool lastFrameButtonDown = lastFrameInputs.TryGetValue( buttonName, out Recording.InputState lastFrameState ) && lastFrameState.buttonState; 372 | bool thisFrameButtonDown = thisFrameInputs.TryGetValue( buttonName, out Recording.InputState thisFrameState ) && thisFrameState.buttonState; 373 | return !thisFrameButtonDown && lastFrameButtonDown; 374 | } 375 | 376 | return Input.GetButtonUp( buttonName ); 377 | } 378 | 379 | public float GetAxis( string axisName ) { 380 | if ( Mode == InputVCRMode.Playback ) { 381 | if ( thisFrameInputs.TryGetValue( axisName, out Recording.InputState thisFrameState ) ) 382 | return thisFrameState.axisValue; 383 | else 384 | return 0; 385 | } 386 | 387 | return Input.GetAxis( axisName ); 388 | } 389 | 390 | public float GetAxisRaw( string axisName ) { 391 | if ( Mode == InputVCRMode.Playback ) { 392 | if ( thisFrameInputs.TryGetValue( axisName, out Recording.InputState thisFrameState ) ) 393 | return thisFrameState.axisValue; 394 | else 395 | return 0; 396 | } 397 | 398 | return Input.GetAxisRaw( axisName ); 399 | } 400 | 401 | public bool GetMouseButton( int buttonNum ) { 402 | if ( Mode == InputVCRMode.Playback ) { 403 | return thisFrameInputs.TryGetValue( GetMouseButtonId( buttonNum ), out Recording.InputState thisFrameState ) && thisFrameState.buttonState; 404 | } 405 | 406 | return Input.GetMouseButton( buttonNum ); 407 | } 408 | 409 | public bool GetMouseButtonDown( int buttonNum ) { 410 | if ( Mode == InputVCRMode.Playback ) { 411 | string buttonId = GetMouseButtonId( buttonNum ); 412 | bool lastFrameButtonDown = lastFrameInputs.TryGetValue( buttonId, out Recording.InputState lastFrameState ) && lastFrameState.buttonState; 413 | bool thisFrameButtonDown = thisFrameInputs.TryGetValue( buttonId, out Recording.InputState thisFrameState ) && thisFrameState.buttonState; 414 | return thisFrameButtonDown && !lastFrameButtonDown; 415 | } 416 | 417 | return Input.GetMouseButtonDown( buttonNum ); 418 | } 419 | 420 | public bool GetMouseButtonUp( int buttonNum ) { 421 | if ( Mode == InputVCRMode.Playback ) { 422 | string buttonId = GetMouseButtonId( buttonNum ); 423 | bool lastFrameButtonDown = lastFrameInputs.TryGetValue( buttonId, out Recording.InputState lastFrameState ) && lastFrameState.buttonState; 424 | bool thisFrameButtonDown = thisFrameInputs.TryGetValue( buttonId, out Recording.InputState thisFrameState ) && thisFrameState.buttonState; 425 | return !thisFrameButtonDown && lastFrameButtonDown; 426 | } 427 | 428 | return Input.GetMouseButtonUp( buttonNum ); 429 | } 430 | 431 | public Vector3 mousePosition { 432 | get { 433 | if ( Mode == InputVCRMode.Playback ) { 434 | float mouseX = thisFrameInputs.TryGetValue( _MOUSE_POS_X_ID, out Recording.InputState thisFrameXState ) ? thisFrameXState.axisValue : 0; 435 | float mouseY = thisFrameInputs.TryGetValue( _MOUSE_POS_Y_ID, out Recording.InputState thisFrameYState ) ? thisFrameYState.axisValue : 0; 436 | return new Vector3( mouseX, mouseY, 0 ); 437 | } 438 | 439 | return Input.mousePosition; 440 | } 441 | } 442 | 443 | public bool TryGetProperty( string propertyName, out string propertyValue ) { 444 | if ( thisFrameProperties.TryGetValue( propertyName, out Recording.FrameProperty frameProp ) ) { 445 | propertyValue = frameProp.value; 446 | return true; 447 | } 448 | else { 449 | propertyValue = string.Empty; 450 | return false; 451 | } 452 | } 453 | #endregion 454 | 455 | private const string _MOUSE_POS_X_ID = "MOUSE_POSITION_X"; 456 | private const string _MOUSE_POS_Y_ID = "MOUSE_POSITION_Y"; 457 | private const string _MOUSE_BUTTON_ID = "MOUSE_BUTTON_"; 458 | 459 | private static string GetMouseButtonId( int buttonNumber ) { 460 | return _MOUSE_BUTTON_ID + buttonNumber; 461 | } 462 | 463 | private static string GetKeyCodeId( KeyCode kc ) { 464 | return KeycodeHelper.KeycodeToKeyString( kc ); 465 | } 466 | 467 | #region IO Helpers 468 | public void PlayRecordingFromDisk( string path, float startPlaybackFromTime = 0 ) { 469 | string fileString = File.ReadAllText( path ); 470 | Recording r = new Recording( path ); 471 | Play( r, startPlaybackFromTime ); 472 | } 473 | 474 | public void WriteCurrentRecordToDisk( string path ) { 475 | if ( CurrentRecording == null ) { 476 | throw new InvalidDataException( "No record to save" ); 477 | } 478 | 479 | string recordJson = CurrentRecording.ToJson(); 480 | File.WriteAllText( path, recordJson ); 481 | } 482 | #endregion 483 | 484 | /// 485 | /// Holds playback/record state info for a Recording 486 | /// 487 | class RecordingState { 488 | public readonly Recording targetRecording; 489 | 490 | public float Time { get; private set; } 491 | 492 | public int FrameIdx { get; private set; } = -1; 493 | 494 | public RecordingState( Recording recording ) { 495 | targetRecording = recording; 496 | } 497 | 498 | public void SkipToTime( float newTime ) { 499 | this.Time = Mathf.Clamp( newTime, 0, targetRecording.Length ); 500 | this.FrameIdx = targetRecording.GetFrameForTime( Time ); 501 | } 502 | 503 | public void AdvanceByTime( float deltaTime ) { 504 | this.Time += deltaTime; 505 | this.FrameIdx = targetRecording.GetFrameForTime( Time ); 506 | } 507 | 508 | public void AppendNewRecordingFrame( float deltaTime ) { 509 | this.Time += deltaTime; 510 | targetRecording.AddFrame( this.Time ); 511 | this.FrameIdx = targetRecording.FrameCount - 1; 512 | } 513 | 514 | public void AddInputToCurrentFrame( Recording.InputState inputState ) { 515 | targetRecording.AddInput( this.FrameIdx, inputState ); 516 | } 517 | 518 | public void AddPropertyToCurrentFrame( Recording.FrameProperty frameProperty ) { 519 | targetRecording.AddProperty( this.FrameIdx, frameProperty ); 520 | } 521 | 522 | public void ClearRecordingAfterCurrentTime() { 523 | targetRecording.ClearFrames( this.FrameIdx ); 524 | } 525 | } 526 | } 527 | 528 | public enum InputVCRMode { 529 | Passthru, // normal input 530 | Record, // normal input & record it 531 | Playback, // all input comes from recording 532 | } 533 | } 534 | -------------------------------------------------------------------------------- /Runtime/Scripts/InputVCRRecorder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d3fd95aeca4ea44a2a903192b3e7ba21 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/InputVCRTextRecordingLoader.cs: -------------------------------------------------------------------------------- 1 | /* InputVCRTextRecordingLoader.cs 2 | * Copyright Eddie Cameron 2019 (See readme for licence) 3 | * ---------- 4 | */ 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using UnityEngine; 8 | 9 | namespace InputVCR { 10 | /// 11 | /// Helper to load and play VCR Recordings from text assets 12 | /// 13 | [RequireComponent( typeof( InputVCRRecorder ) )] 14 | public class InputVCRTextRecordingLoader : MonoBehaviour { 15 | private InputVCRRecorder _recorder; 16 | 17 | public TextAsset loadRecordingOnStart; 18 | public bool playRecordingOnStart; 19 | 20 | void Awake() { 21 | } 22 | 23 | void Start() { 24 | _recorder = GetComponent(); 25 | 26 | if ( loadRecordingOnStart != null ) { 27 | Recording recording = new Recording( loadRecordingOnStart.text ); 28 | _recorder.LoadRecording( recording ); 29 | 30 | if ( playRecordingOnStart ) 31 | _recorder.Play(); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Runtime/Scripts/InputVCRTextRecordingLoader.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ac1813ea1d3d94e98acb43afab3697dc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/InputVCRTransformSyncer.cs: -------------------------------------------------------------------------------- 1 | /* InputVCRTransformSyncer.cs 2 | * Copyright Eddie Cameron 2019 (See readme for licence) 3 | * ---------- 4 | */ 5 | using System; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | using UnityEngine; 9 | 10 | namespace InputVCR { 11 | /// 12 | /// Records the local position/rotation/scale of this transform to a VCR recording, and updates the transform to match when played back 13 | /// 14 | public class InputVCRTransformSyncer : MonoBehaviour { 15 | public InputVCRRecorder recorderToSyncTo; 16 | 17 | [Tooltip( "If left blank, GameObject's name will be used" )] 18 | public string customTag; 19 | public string RecordingTagPrefix => ( string.IsNullOrEmpty( customTag ) ? name : customTag ) + "_transform"; 20 | 21 | public bool syncPosition = true; 22 | public bool syncRotation = true; 23 | public bool syncScale = true; 24 | 25 | void Update() { 26 | if ( recorderToSyncTo == null ) 27 | return; 28 | 29 | if ( !syncPosition && !syncRotation && !syncScale ) 30 | return; 31 | 32 | if ( recorderToSyncTo.Mode == InputVCRMode.Record ) { 33 | RecordTransformState(); 34 | } 35 | else if ( recorderToSyncTo.Mode == InputVCRMode.Playback ) { 36 | MatchTransformToRecording(); 37 | } 38 | } 39 | 40 | /// 41 | /// Record this transforms state to the current recording in the VCR 42 | /// 43 | void RecordTransformState() { 44 | TransformState currentState = new TransformState( transform ); 45 | string stateString = JsonUtility.ToJson( currentState ); 46 | 47 | recorderToSyncTo.SaveProperty( RecordingTagPrefix, stateString ); 48 | } 49 | 50 | void MatchTransformToRecording() { 51 | if ( recorderToSyncTo.TryGetProperty( RecordingTagPrefix, out string stateString ) ) { 52 | TransformState recordedState = JsonUtility.FromJson( stateString ); 53 | 54 | if ( syncPosition ) 55 | transform.localPosition = recordedState.position; 56 | 57 | if ( syncRotation ) 58 | transform.localRotation = recordedState.rotation; 59 | 60 | if ( syncScale ) 61 | transform.localScale = recordedState.scale; 62 | } 63 | } 64 | 65 | [Serializable] 66 | public struct TransformState { 67 | public Vector3 position; 68 | public Quaternion rotation; 69 | public Vector3 scale; 70 | 71 | public TransformState( Transform t ) { 72 | position = t.localPosition; 73 | rotation = t.localRotation; 74 | scale = t.localScale; 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Runtime/Scripts/InputVCRTransformSyncer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f95ed2210b66949619f8ed1ab67ddbdc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/KeycodeHelper.cs: -------------------------------------------------------------------------------- 1 | /* KeycodeHelper.cs 2 | * Copyright Eddie Cameron 2019 (See readme for licence) 3 | * ---------- 4 | */ 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using UnityEngine; 8 | 9 | namespace InputVCR { 10 | public static class KeycodeHelper { 11 | /// 12 | /// Convert a KeyCode into the string that Unity uses in GetKeyString 13 | /// (ToString returns a different value -_-) 14 | /// 15 | /// 16 | /// 17 | public static string KeycodeToKeyString( KeyCode kc ) { 18 | switch ( kc ) { 19 | // numerals 20 | case KeyCode.Alpha0: 21 | return "0"; 22 | case KeyCode.Alpha1: 23 | return "1"; 24 | case KeyCode.Alpha2: 25 | return "2"; 26 | case KeyCode.Alpha3: 27 | return "3"; 28 | case KeyCode.Alpha4: 29 | return "4"; 30 | case KeyCode.Alpha5: 31 | return "5"; 32 | case KeyCode.Alpha6: 33 | return "6"; 34 | case KeyCode.Alpha7: 35 | return "7"; 36 | case KeyCode.Alpha8: 37 | return "8"; 38 | case KeyCode.Alpha9: 39 | return "9"; 40 | 41 | case KeyCode.Keypad0: 42 | return "[0]"; 43 | case KeyCode.Keypad1: 44 | return "[1]"; 45 | case KeyCode.Keypad2: 46 | return "[2]"; 47 | case KeyCode.Keypad3: 48 | return "[3]"; 49 | case KeyCode.Keypad4: 50 | return "[4]"; 51 | case KeyCode.Keypad5: 52 | return "[5]"; 53 | case KeyCode.Keypad6: 54 | return "[6]"; 55 | case KeyCode.Keypad7: 56 | return "[7]"; 57 | case KeyCode.Keypad8: 58 | return "[8]"; 59 | case KeyCode.Keypad9: 60 | return "[9]"; 61 | case KeyCode.KeypadDivide: 62 | return "[/]"; 63 | case KeyCode.KeypadEnter: 64 | return "enter"; 65 | case KeyCode.KeypadEquals: 66 | return "equals"; 67 | case KeyCode.KeypadMinus: 68 | return "[-]"; 69 | case KeyCode.KeypadMultiply: 70 | return "[*]"; 71 | case KeyCode.KeypadPeriod: 72 | return "[.]"; 73 | case KeyCode.KeypadPlus: 74 | return "[+]"; 75 | 76 | 77 | case KeyCode.UpArrow: 78 | return "up"; 79 | case KeyCode.DownArrow: 80 | return "down"; 81 | case KeyCode.LeftArrow: 82 | return "left"; 83 | case KeyCode.RightArrow: 84 | return "right"; 85 | 86 | case KeyCode.Mouse0: 87 | return "mouse 0"; 88 | case KeyCode.Mouse1: 89 | return "mouse 1"; 90 | case KeyCode.Mouse2: 91 | return "mouse 2"; 92 | case KeyCode.Mouse3: 93 | return "mouse 3"; 94 | case KeyCode.Mouse4: 95 | return "mouse 4"; 96 | case KeyCode.Mouse5: 97 | return "mouse 5"; 98 | case KeyCode.Mouse6: 99 | return "mouse 6"; 100 | 101 | #region Joysticks 102 | case KeyCode.JoystickButton0: 103 | return "joystick button 0"; 104 | case KeyCode.JoystickButton1: 105 | return "joystick button 1"; 106 | case KeyCode.JoystickButton2: 107 | return "joystick button 2"; 108 | case KeyCode.JoystickButton3: 109 | return "joystick button 3"; 110 | case KeyCode.JoystickButton4: 111 | return "joystick button 4"; 112 | case KeyCode.JoystickButton5: 113 | return "joystick button 5"; 114 | case KeyCode.JoystickButton6: 115 | return "joystick button 6"; 116 | case KeyCode.JoystickButton7: 117 | return "joystick button 7"; 118 | case KeyCode.JoystickButton8: 119 | return "joystick button 8"; 120 | case KeyCode.JoystickButton9: 121 | return "joystick button 9"; 122 | case KeyCode.JoystickButton10: 123 | return "joystick button 10"; 124 | case KeyCode.JoystickButton11: 125 | return "joystick button 11"; 126 | case KeyCode.JoystickButton12: 127 | return "joystick button 12"; 128 | case KeyCode.JoystickButton13: 129 | return "joystick button 13"; 130 | case KeyCode.JoystickButton14: 131 | return "joystick button 14"; 132 | case KeyCode.JoystickButton15: 133 | return "joystick button 15"; 134 | case KeyCode.JoystickButton16: 135 | return "joystick button 16"; 136 | case KeyCode.JoystickButton17: 137 | return "joystick button 17"; 138 | case KeyCode.JoystickButton18: 139 | return "joystick button 18"; 140 | case KeyCode.JoystickButton19: 141 | return "joystick button 19"; 142 | 143 | case KeyCode.Joystick1Button0: 144 | return "joystick 1 button 0"; 145 | case KeyCode.Joystick1Button1: 146 | return "joystick 1 button 1"; 147 | case KeyCode.Joystick1Button2: 148 | return "joystick 1 button 2"; 149 | case KeyCode.Joystick1Button3: 150 | return "joystick 1 button 3"; 151 | case KeyCode.Joystick1Button4: 152 | return "joystick 1 button 4"; 153 | case KeyCode.Joystick1Button5: 154 | return "joystick 1 button 5"; 155 | case KeyCode.Joystick1Button6: 156 | return "joystick 1 button 6"; 157 | case KeyCode.Joystick1Button7: 158 | return "joystick 1 button 7"; 159 | case KeyCode.Joystick1Button8: 160 | return "joystick 1 button 8"; 161 | case KeyCode.Joystick1Button9: 162 | return "joystick 1 button 9"; 163 | case KeyCode.Joystick1Button10: 164 | return "joystick 1 button 10"; 165 | case KeyCode.Joystick1Button11: 166 | return "joystick 1 button 11"; 167 | case KeyCode.Joystick1Button12: 168 | return "joystick 1 button 12"; 169 | case KeyCode.Joystick1Button13: 170 | return "joystick 1 button 13"; 171 | case KeyCode.Joystick1Button14: 172 | return "joystick 1 button 14"; 173 | case KeyCode.Joystick1Button15: 174 | return "joystick 1 button 15"; 175 | case KeyCode.Joystick1Button16: 176 | return "joystick 1 button 16"; 177 | case KeyCode.Joystick1Button17: 178 | return "joystick 1 button 17"; 179 | case KeyCode.Joystick1Button18: 180 | return "joystick 1 button 18"; 181 | case KeyCode.Joystick1Button19: 182 | return "joystick 1 button 19"; 183 | 184 | case KeyCode.Joystick2Button0: 185 | return "joystick 2 button 0"; 186 | case KeyCode.Joystick2Button1: 187 | return "joystick 2 button 1"; 188 | case KeyCode.Joystick2Button2: 189 | return "joystick 2 button 2"; 190 | case KeyCode.Joystick2Button3: 191 | return "joystick 2 button 3"; 192 | case KeyCode.Joystick2Button4: 193 | return "joystick 2 button 4"; 194 | case KeyCode.Joystick2Button5: 195 | return "joystick 2 button 5"; 196 | case KeyCode.Joystick2Button6: 197 | return "joystick 2 button 6"; 198 | case KeyCode.Joystick2Button7: 199 | return "joystick 2 button 7"; 200 | case KeyCode.Joystick2Button8: 201 | return "joystick 2 button 8"; 202 | case KeyCode.Joystick2Button9: 203 | return "joystick 2 button 9"; 204 | case KeyCode.Joystick2Button10: 205 | return "joystick 2 button 10"; 206 | case KeyCode.Joystick2Button11: 207 | return "joystick 2 button 11"; 208 | case KeyCode.Joystick2Button12: 209 | return "joystick 2 button 12"; 210 | case KeyCode.Joystick2Button13: 211 | return "joystick 2 button 13"; 212 | case KeyCode.Joystick2Button14: 213 | return "joystick 2 button 14"; 214 | case KeyCode.Joystick2Button15: 215 | return "joystick 2 button 15"; 216 | case KeyCode.Joystick2Button16: 217 | return "joystick 2 button 16"; 218 | case KeyCode.Joystick2Button17: 219 | return "joystick 2 button 17"; 220 | case KeyCode.Joystick2Button18: 221 | return "joystick 2 button 18"; 222 | case KeyCode.Joystick2Button19: 223 | return "joystick 2 button 19"; 224 | 225 | case KeyCode.Joystick3Button0: 226 | return "joystick 3 button 0"; 227 | case KeyCode.Joystick3Button1: 228 | return "joystick 3 button 1"; 229 | case KeyCode.Joystick3Button2: 230 | return "joystick 3 button 2"; 231 | case KeyCode.Joystick3Button3: 232 | return "joystick 3 button 3"; 233 | case KeyCode.Joystick3Button4: 234 | return "joystick 3 button 4"; 235 | case KeyCode.Joystick3Button5: 236 | return "joystick 3 button 5"; 237 | case KeyCode.Joystick3Button6: 238 | return "joystick 3 button 6"; 239 | case KeyCode.Joystick3Button7: 240 | return "joystick 3 button 7"; 241 | case KeyCode.Joystick3Button8: 242 | return "joystick 3 button 8"; 243 | case KeyCode.Joystick3Button9: 244 | return "joystick 3 button 9"; 245 | case KeyCode.Joystick3Button10: 246 | return "joystick 3 button 10"; 247 | case KeyCode.Joystick3Button11: 248 | return "joystick 3 button 11"; 249 | case KeyCode.Joystick3Button12: 250 | return "joystick 3 button 12"; 251 | case KeyCode.Joystick3Button13: 252 | return "joystick 3 button 13"; 253 | case KeyCode.Joystick3Button14: 254 | return "joystick 3 button 14"; 255 | case KeyCode.Joystick3Button15: 256 | return "joystick 3 button 15"; 257 | case KeyCode.Joystick3Button16: 258 | return "joystick 3 button 16"; 259 | case KeyCode.Joystick3Button17: 260 | return "joystick 3 button 17"; 261 | case KeyCode.Joystick3Button18: 262 | return "joystick 3 button 18"; 263 | case KeyCode.Joystick3Button19: 264 | return "joystick 3 button 19"; 265 | 266 | case KeyCode.Joystick4Button0: 267 | return "joystick 4 button 0"; 268 | case KeyCode.Joystick4Button1: 269 | return "joystick 4 button 1"; 270 | case KeyCode.Joystick4Button2: 271 | return "joystick 4 button 2"; 272 | case KeyCode.Joystick4Button3: 273 | return "joystick 4 button 3"; 274 | case KeyCode.Joystick4Button4: 275 | return "joystick 4 button 4"; 276 | case KeyCode.Joystick4Button5: 277 | return "joystick 4 button 5"; 278 | case KeyCode.Joystick4Button6: 279 | return "joystick 4 button 6"; 280 | case KeyCode.Joystick4Button7: 281 | return "joystick 4 button 7"; 282 | case KeyCode.Joystick4Button8: 283 | return "joystick 4 button 8"; 284 | case KeyCode.Joystick4Button9: 285 | return "joystick 4 button 9"; 286 | case KeyCode.Joystick4Button10: 287 | return "joystick 4 button 10"; 288 | case KeyCode.Joystick4Button11: 289 | return "joystick 4 button 11"; 290 | case KeyCode.Joystick4Button12: 291 | return "joystick 4 button 12"; 292 | case KeyCode.Joystick4Button13: 293 | return "joystick 4 button 13"; 294 | case KeyCode.Joystick4Button14: 295 | return "joystick 4 button 14"; 296 | case KeyCode.Joystick4Button15: 297 | return "joystick 4 button 15"; 298 | case KeyCode.Joystick4Button16: 299 | return "joystick 4 button 16"; 300 | case KeyCode.Joystick4Button17: 301 | return "joystick 4 button 17"; 302 | case KeyCode.Joystick4Button18: 303 | return "joystick 4 button 18"; 304 | case KeyCode.Joystick4Button19: 305 | return "joystick 4 button 19"; 306 | 307 | case KeyCode.Joystick5Button0: 308 | return "joystick 5 button 0"; 309 | case KeyCode.Joystick5Button1: 310 | return "joystick 5 button 1"; 311 | case KeyCode.Joystick5Button2: 312 | return "joystick 5 button 2"; 313 | case KeyCode.Joystick5Button3: 314 | return "joystick 5 button 3"; 315 | case KeyCode.Joystick5Button4: 316 | return "joystick 5 button 4"; 317 | case KeyCode.Joystick5Button5: 318 | return "joystick 5 button 5"; 319 | case KeyCode.Joystick5Button6: 320 | return "joystick 5 button 6"; 321 | case KeyCode.Joystick5Button7: 322 | return "joystick 5 button 7"; 323 | case KeyCode.Joystick5Button8: 324 | return "joystick 5 button 8"; 325 | case KeyCode.Joystick5Button9: 326 | return "joystick 5 button 9"; 327 | case KeyCode.Joystick5Button10: 328 | return "joystick 5 button 10"; 329 | case KeyCode.Joystick5Button11: 330 | return "joystick 5 button 11"; 331 | case KeyCode.Joystick5Button12: 332 | return "joystick 5 button 12"; 333 | case KeyCode.Joystick5Button13: 334 | return "joystick 5 button 13"; 335 | case KeyCode.Joystick5Button14: 336 | return "joystick 5 button 14"; 337 | case KeyCode.Joystick5Button15: 338 | return "joystick 5 button 15"; 339 | case KeyCode.Joystick5Button16: 340 | return "joystick 5 button 16"; 341 | case KeyCode.Joystick5Button17: 342 | return "joystick 5 button 17"; 343 | case KeyCode.Joystick5Button18: 344 | return "joystick 5 button 18"; 345 | case KeyCode.Joystick5Button19: 346 | return "joystick 5 button 19"; 347 | 348 | case KeyCode.Joystick6Button0: 349 | return "joystick 6 button 0"; 350 | case KeyCode.Joystick6Button1: 351 | return "joystick 6 button 1"; 352 | case KeyCode.Joystick6Button2: 353 | return "joystick 6 button 2"; 354 | case KeyCode.Joystick6Button3: 355 | return "joystick 6 button 3"; 356 | case KeyCode.Joystick6Button4: 357 | return "joystick 6 button 4"; 358 | case KeyCode.Joystick6Button5: 359 | return "joystick 6 button 5"; 360 | case KeyCode.Joystick6Button6: 361 | return "joystick 6 button 6"; 362 | case KeyCode.Joystick6Button7: 363 | return "joystick 6 button 7"; 364 | case KeyCode.Joystick6Button8: 365 | return "joystick 6 button 8"; 366 | case KeyCode.Joystick6Button9: 367 | return "joystick 6 button 9"; 368 | case KeyCode.Joystick6Button10: 369 | return "joystick 6 button 10"; 370 | case KeyCode.Joystick6Button11: 371 | return "joystick 6 button 11"; 372 | case KeyCode.Joystick6Button12: 373 | return "joystick 6 button 12"; 374 | case KeyCode.Joystick6Button13: 375 | return "joystick 6 button 13"; 376 | case KeyCode.Joystick6Button14: 377 | return "joystick 6 button 14"; 378 | case KeyCode.Joystick6Button15: 379 | return "joystick 6 button 15"; 380 | case KeyCode.Joystick6Button16: 381 | return "joystick 6 button 16"; 382 | case KeyCode.Joystick6Button17: 383 | return "joystick 6 button 17"; 384 | case KeyCode.Joystick6Button18: 385 | return "joystick 6 button 18"; 386 | case KeyCode.Joystick6Button19: 387 | return "joystick 6 button 19"; 388 | 389 | case KeyCode.Joystick7Button0: 390 | return "joystick 7 button 0"; 391 | case KeyCode.Joystick7Button1: 392 | return "joystick 7 button 1"; 393 | case KeyCode.Joystick7Button2: 394 | return "joystick 7 button 2"; 395 | case KeyCode.Joystick7Button3: 396 | return "joystick 7 button 3"; 397 | case KeyCode.Joystick7Button4: 398 | return "joystick 7 button 4"; 399 | case KeyCode.Joystick7Button5: 400 | return "joystick 7 button 5"; 401 | case KeyCode.Joystick7Button6: 402 | return "joystick 7 button 6"; 403 | case KeyCode.Joystick7Button7: 404 | return "joystick 7 button 7"; 405 | case KeyCode.Joystick7Button8: 406 | return "joystick 7 button 8"; 407 | case KeyCode.Joystick7Button9: 408 | return "joystick 7 button 9"; 409 | case KeyCode.Joystick7Button10: 410 | return "joystick 7 button 10"; 411 | case KeyCode.Joystick7Button11: 412 | return "joystick 7 button 11"; 413 | case KeyCode.Joystick7Button12: 414 | return "joystick 7 button 12"; 415 | case KeyCode.Joystick7Button13: 416 | return "joystick 7 button 13"; 417 | case KeyCode.Joystick7Button14: 418 | return "joystick 7 button 14"; 419 | case KeyCode.Joystick7Button15: 420 | return "joystick 7 button 15"; 421 | case KeyCode.Joystick7Button16: 422 | return "joystick 7 button 16"; 423 | case KeyCode.Joystick7Button17: 424 | return "joystick 7 button 17"; 425 | case KeyCode.Joystick7Button18: 426 | return "joystick 7 button 18"; 427 | case KeyCode.Joystick7Button19: 428 | return "joystick 7 button 19"; 429 | 430 | case KeyCode.Joystick8Button0: 431 | return "joystick 8 button 0"; 432 | case KeyCode.Joystick8Button1: 433 | return "joystick 8 button 1"; 434 | case KeyCode.Joystick8Button2: 435 | return "joystick 8 button 2"; 436 | case KeyCode.Joystick8Button3: 437 | return "joystick 8 button 3"; 438 | case KeyCode.Joystick8Button4: 439 | return "joystick 8 button 4"; 440 | case KeyCode.Joystick8Button5: 441 | return "joystick 8 button 5"; 442 | case KeyCode.Joystick8Button6: 443 | return "joystick 8 button 6"; 444 | case KeyCode.Joystick8Button7: 445 | return "joystick 8 button 7"; 446 | case KeyCode.Joystick8Button8: 447 | return "joystick 8 button 8"; 448 | case KeyCode.Joystick8Button9: 449 | return "joystick 8 button 9"; 450 | case KeyCode.Joystick8Button10: 451 | return "joystick 8 button 10"; 452 | case KeyCode.Joystick8Button11: 453 | return "joystick 8 button 11"; 454 | case KeyCode.Joystick8Button12: 455 | return "joystick 8 button 12"; 456 | case KeyCode.Joystick8Button13: 457 | return "joystick 8 button 13"; 458 | case KeyCode.Joystick8Button14: 459 | return "joystick 8 button 14"; 460 | case KeyCode.Joystick8Button15: 461 | return "joystick 8 button 15"; 462 | case KeyCode.Joystick8Button16: 463 | return "joystick 8 button 16"; 464 | case KeyCode.Joystick8Button17: 465 | return "joystick 8 button 17"; 466 | case KeyCode.Joystick8Button18: 467 | return "joystick 8 button 18"; 468 | case KeyCode.Joystick8Button19: 469 | return "joystick 8 button 19"; 470 | #endregion 471 | 472 | case KeyCode.AltGr: 473 | return "alt gr"; 474 | case KeyCode.Ampersand: 475 | return "&"; 476 | case KeyCode.Asterisk: 477 | return "*"; 478 | case KeyCode.At: 479 | return "@"; 480 | case KeyCode.BackQuote: 481 | return "`"; 482 | case KeyCode.Backslash: 483 | return @"\"; 484 | case KeyCode.CapsLock: 485 | return "caps lock"; 486 | case KeyCode.Caret: 487 | return "^"; 488 | case KeyCode.Colon: 489 | return ":"; 490 | case KeyCode.Comma: 491 | return ","; 492 | case KeyCode.Dollar: 493 | return "$"; 494 | case KeyCode.DoubleQuote: 495 | return "\""; 496 | case KeyCode.Equals: 497 | return "="; 498 | case KeyCode.Exclaim: 499 | return "!"; 500 | case KeyCode.Greater: 501 | return ">"; 502 | case KeyCode.Hash: 503 | return "#"; 504 | case KeyCode.LeftAlt: 505 | return "left alt"; 506 | case KeyCode.LeftBracket: 507 | return "["; 508 | case KeyCode.LeftCommand: 509 | return "left cmd"; 510 | case KeyCode.LeftControl: 511 | return "left ctrl"; 512 | case KeyCode.LeftCurlyBracket: 513 | return "{"; 514 | case KeyCode.LeftParen: 515 | return "("; 516 | case KeyCode.LeftShift: 517 | return "left shift"; 518 | case KeyCode.Less: 519 | return "<"; 520 | case KeyCode.Minus: 521 | return "-"; 522 | case KeyCode.PageDown: 523 | return "page down"; 524 | case KeyCode.PageUp: 525 | return "page up"; 526 | case KeyCode.Percent: 527 | return "%"; 528 | case KeyCode.Period: 529 | return "."; 530 | case KeyCode.Pipe: 531 | return "|"; 532 | case KeyCode.Plus: 533 | return "+"; 534 | case KeyCode.Print: 535 | return "print screen"; 536 | case KeyCode.Question: 537 | return "?"; 538 | case KeyCode.Quote: 539 | return "'"; 540 | case KeyCode.RightAlt: 541 | return "right alt"; 542 | case KeyCode.RightBracket: 543 | return "]"; 544 | case KeyCode.RightCommand: 545 | return "right cmd"; 546 | case KeyCode.RightControl: 547 | return "right ctrl"; 548 | case KeyCode.RightCurlyBracket: 549 | return "}"; 550 | case KeyCode.RightParen: 551 | return ")"; 552 | case KeyCode.RightShift: 553 | return "right shift"; 554 | case KeyCode.ScrollLock: 555 | return "scroll lock"; 556 | case KeyCode.Semicolon: 557 | return ";"; 558 | case KeyCode.Slash: 559 | return "/"; 560 | case KeyCode.SysReq: 561 | return "sys req"; 562 | case KeyCode.Tilde: 563 | return "~"; 564 | case KeyCode.Underscore: 565 | return "_"; 566 | 567 | case KeyCode.LeftWindows: 568 | return "left super"; // guessing 569 | case KeyCode.RightWindows: 570 | return "right super"; 571 | 572 | default: 573 | return kc.ToString().ToLower(); 574 | } 575 | } 576 | } 577 | } 578 | -------------------------------------------------------------------------------- /Runtime/Scripts/KeycodeHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 00a28486e50e349b8a4bfd20fb5f0ab6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Scripts/Recording.cs: -------------------------------------------------------------------------------- 1 | /* Recording.cs 2 | * Copyright Eddie Cameron 2019 (See readme for licence) 3 | * ---------------------------- 4 | * Class for transferring and parsing InputVCR Recordings. Think of it as a VHS cassete, but fancier 5 | * 6 | */ 7 | 8 | using UnityEngine; 9 | using System.Collections; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | using System; 13 | 14 | namespace InputVCR { 15 | [Serializable] 16 | public class Recording : ISerializationCallbackReceiver { 17 | [SerializeField] 18 | private List frames = new List(); 19 | 20 | [SerializeField] 21 | private int _schemaVersion = _CURRENT_JSON_SCHEMA_VERSION; // make sure schema version is in the JSON 22 | private const int _CURRENT_JSON_SCHEMA_VERSION = 1; 23 | 24 | public int FrameCount => frames.Count; 25 | 26 | public float Length => frames.Count == 0 ? 0 : frames[frames.Count - 1].time; 27 | 28 | /// 29 | /// One recording frame's worth of input 30 | /// 31 | [System.Serializable] 32 | public struct Frame { 33 | public float time; 34 | public List inputManagerStates; 35 | public List syncedProperties; 36 | 37 | public Frame( float time ) { 38 | this.time = time; 39 | this.inputManagerStates = new List(); 40 | this.syncedProperties = new List(); 41 | } 42 | } 43 | 44 | /// 45 | /// Represents the state of a single Input source during a frame 46 | /// 47 | [System.Serializable] 48 | public struct InputState { 49 | public string inputId; 50 | 51 | public bool buttonState; 52 | 53 | public float axisValue; // not raw value 54 | 55 | public InputState( string inputId, bool buttonState ) { 56 | this.inputId = inputId; 57 | this.buttonState = buttonState; 58 | this.axisValue = 0; 59 | } 60 | 61 | public InputState( string inputId, float axisValue ) { 62 | this.inputId = inputId; 63 | this.buttonState = false; 64 | this.axisValue = axisValue; 65 | } 66 | } 67 | 68 | /// 69 | /// The value of an arbitrary property during this frame 70 | /// Use for adding additional info to the recording 71 | /// 72 | [System.Serializable] 73 | public struct FrameProperty { 74 | public string name; 75 | public string value; 76 | 77 | public FrameProperty( string name, string value ) { 78 | this.name = name; 79 | this.value = value; 80 | } 81 | } 82 | 83 | #region Constructors 84 | public Recording() { 85 | } 86 | 87 | public Recording( string jsonRecording ) { 88 | JsonUtility.FromJsonOverwrite( jsonRecording, this ); 89 | } 90 | 91 | /// 92 | /// Copies the data in oldRecoding to a new instance of the class. 93 | /// 94 | /// 95 | /// Recording to be copied 96 | /// 97 | public Recording( Recording oldRecording ) { 98 | if ( oldRecording == null ) 99 | return; 100 | 101 | frames.AddRange( oldRecording.frames ); 102 | } 103 | #endregion 104 | 105 | public int GetFrameForTime( float time ) { 106 | if ( FrameCount == 0 ) 107 | throw new InvalidDataException( "Can't get frame for empty recording" ); 108 | 109 | for ( int i = 1; i < frames.Count; i++ ) { 110 | if ( frames[i].time > time ) 111 | return i - 1; 112 | } 113 | 114 | return FrameCount - 1; // freeze on end of recording 115 | } 116 | 117 | public void ClearFrames( int startFrame = 0 ) { 118 | if ( startFrame <= 0 ) 119 | frames.Clear(); 120 | else if ( startFrame < frames.Count ) 121 | frames.RemoveRange( startFrame, frames.Count - startFrame ); 122 | } 123 | 124 | /// 125 | /// Append a new frame to the recording. 126 | /// Must be at a time after the last frame 127 | /// 128 | /// 129 | public void AddFrame( float atTime ) { 130 | if ( FrameCount > 0 ) { 131 | if ( atTime <= Length ) 132 | throw new ArgumentException( "Tried to add a frame at a time before the current end of the recording" ); 133 | } 134 | 135 | Frame newFrame = new Frame( atTime ); 136 | frames.Add( newFrame ); 137 | } 138 | 139 | /// 140 | /// Adds the supplied input info to given frame. 141 | /// Overwrites any existing state for the same input at this frame 142 | /// 143 | /// 144 | /// At frame. 145 | /// 146 | /// 147 | /// Input info. 148 | /// 149 | public void AddInput( int atFrame, InputState inputState ) { 150 | Frame frame = GetFrame( atFrame ); 151 | 152 | for ( int i = 0; i < frame.inputManagerStates.Count; i++ ) { 153 | // overwrite 154 | if ( frame.inputManagerStates[i].inputId == inputState.inputId ) { 155 | frame.inputManagerStates[i] = inputState; 156 | return; 157 | } 158 | } 159 | 160 | // new state 161 | frame.inputManagerStates.Add( inputState ); 162 | } 163 | 164 | /// 165 | /// Adds a custom property to a given frame 166 | /// Overwrites any existing value for the same property name 167 | /// 168 | /// 169 | /// At frame. 170 | /// 171 | /// 172 | /// Property name. 173 | /// 174 | /// 175 | /// Property value as string. 176 | /// 177 | public void AddProperty( int atFrame, string propertyName, string propertyValue ) => AddProperty( atFrame, new FrameProperty( propertyName, propertyValue ) ); 178 | 179 | /// 180 | /// Adds a custom property to a given frame 181 | /// Overwrites any existing value for the same property name 182 | /// 183 | /// 184 | /// At frame. 185 | /// 186 | /// 187 | /// Property name. 188 | /// 189 | /// 190 | /// Property value as string. 191 | /// 192 | public void AddProperty( int atFrame, FrameProperty frameProperty ) { 193 | Frame frame = GetFrame( atFrame ); 194 | 195 | for ( int i = 0; i < frame.syncedProperties.Count; i++ ) { 196 | // overwrite 197 | if ( frame.syncedProperties[i].name == frameProperty.name ) { 198 | frame.syncedProperties[i] = frameProperty; 199 | return; 200 | } 201 | } 202 | 203 | frame.syncedProperties.Add( frameProperty ); 204 | } 205 | 206 | /// 207 | /// Gets the input state at given frame. 208 | /// 209 | /// 210 | /// InputInfo 211 | /// 212 | /// 213 | /// At frame. 214 | /// 215 | /// 216 | /// Input name. 217 | /// 218 | public InputState GetInput( int atFrame, string inputName ) { 219 | Frame frame = GetFrame( atFrame ); 220 | for ( int i = 0; i < frame.inputManagerStates.Count; i++ ) { 221 | if ( frame.inputManagerStates[i].inputId == inputName ) 222 | return frame.inputManagerStates[i]; 223 | } 224 | 225 | Debug.LogWarning( "Input " + inputName + " not found in frame " + atFrame ); 226 | return default( InputState ); 227 | } 228 | 229 | /// 230 | /// Gets all inputs in a given frame. 231 | /// 232 | /// 233 | /// The inputs. 234 | /// 235 | /// 236 | /// At frame. 237 | /// 238 | public void GetInputs( int atFrame, List outStates ) { 239 | outStates.AddRange( GetFrame( atFrame ).inputManagerStates ); 240 | } 241 | 242 | /// 243 | /// Gets the given custom property if recorded in given frame 244 | /// 245 | /// 246 | /// The property. 247 | /// 248 | /// 249 | /// At frame. 250 | /// 251 | /// 252 | /// Property name. 253 | /// 254 | public string GetProperty( int atFrame, string propertyName ) { 255 | Frame frame = GetFrame( atFrame ); 256 | 257 | for ( int i = 0; i < frame.syncedProperties.Count; i++ ) { 258 | if ( frame.syncedProperties[i].name == propertyName ) 259 | return frame.syncedProperties[i].value; 260 | } 261 | 262 | Debug.LogWarning( "Property " + propertyName + " not found in frame " + atFrame ); 263 | return ""; 264 | } 265 | 266 | /// 267 | /// Get all properties recorded into this frame 268 | /// 269 | /// 270 | /// 271 | public void GetProperties( int atFrame, List outProps ) { 272 | outProps.AddRange( GetFrame( atFrame ).syncedProperties ); 273 | } 274 | 275 | /// 276 | /// Make sure this frame has an entry 277 | /// 278 | /// 279 | Frame GetFrame( int frameIdx ) { 280 | if ( frameIdx < 0 || frameIdx >= frames.Count ) 281 | throw new ArgumentException( "Tried to get frame outside recorded range" ); 282 | return frames[frameIdx]; 283 | } 284 | 285 | public string ToJson( bool prettyPrint = false ) { 286 | return JsonUtility.ToJson( this, prettyPrint ); 287 | } 288 | 289 | void ISerializationCallbackReceiver.OnBeforeSerialize() { 290 | _schemaVersion = _CURRENT_JSON_SCHEMA_VERSION; 291 | } 292 | 293 | void ISerializationCallbackReceiver.OnAfterDeserialize() { 294 | if ( _schemaVersion != _CURRENT_JSON_SCHEMA_VERSION ) { 295 | // TODO we need to ugprade! 296 | } 297 | } 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /Runtime/Scripts/Recording.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 38d4a37adf9174641b0f5836cafa3b5c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.eddiecameron.inputvcr", 3 | "version": "2.0.0", 4 | "displayName": "Input VCR", 5 | "description": "Record and playback player actions without the fuss.", 6 | "keywords": [ 7 | "Input", 8 | "Recording", 9 | "Playback" 10 | ], 11 | "author": { 12 | "name": "Eddie Cameron", 13 | "email": "eddie@eddiecameron.fun", 14 | "url": "https://eddiecameron.fun" 15 | } 16 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3684f30dd9e1b45f89b42d6f545d89b0 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # InputVCR README 2 | 3 | ## Record and playback player actions without the fuss. 4 | 5 | ### What 6 | 7 | Why write a custom recording class for your game, when much of it is identical? InputVCR records 8 | inputs you choose to text, then, when you want to replay, will automatically send these inputs to any 9 | Gameobject, without having to effect the others. 10 | You can use InputVCR to record player motion and any actions they take, for match replays, kill cams, 11 | puzzles, companions, and more. 12 | 13 | ### How 14 | 15 | #### InputVCRRecorder 16 | InputVCRRecorder is the main class of the package, think of it like a VHS player. 17 | 18 | Inputs that should be recorded should be added to the Recorded Buttons/Axes/Keys lists (Buttons and Axes use the names as defined in the Unity Input Manager) 19 | Mouse inputs (position + button states) can also be recorded, if the recordMouseEvents flag is true 20 | Touches coming soon maybe! 21 | 22 | To start recording, call Record() or RecordNew() 23 | While recording, input values are saved to a Recording object each frame. 24 | To save additional arbitrary properties to a frame, call SaveProperty, with a key and string value 25 | 26 | To stop recording, call Stop() or Rewind() 27 | 28 | To start playing back a recording, call Play(), if no recording is provided, the last used recording will be used. Note that recordings will start playing back from their current playback point unless otherwise specified. Be kind, rewind! 29 | 30 | Code that needs playback Input values needs to call Input methods via the VCRRecorder rather than Input directly: 31 | e.g: Change `Input.GetButtonDown( "Jump" )` to `myInputVCRRecorder.GetButtonDown( "Jump" )`. This function will return the live Input values when stopped or recording, but will return values as saved in the recording while in playback 32 | Saved property values can be retrieved with TryGetProperty 33 | 34 | #### Recording 35 | This is the VHS tape. It holds the recorded input values for each frame for the length of the recording. 36 | You shouldn't need to call any methods on this directly, although the Length property (in seconds) might be useful. 37 | This object can be passed around to be played back in other InputVCRRecorders, or saved for later. 38 | If you want to save a Recording when playback ends, it must first be Serialized so it can be saved to disk. Call .ToJson() to get a json string. 39 | The InputVCRRecorder class also provides utility methods to save/load the current recording to and from disk 40 | 41 | #### InputVCRTextRecordingLoader 42 | This is a helper class to automatically load and optionally start playing a Recording saved in Json form. 43 | 44 | #### InputVCRTransformSyncer 45 | Helper class to automatically save the transform state of an object to a recording, and to sync it up. This is to prevent "drift" of objects due to different frame times or physics. 46 | 47 | 48 | See the Examples folder for a demo, and check the code for more detailed documentation 49 | 50 | Easy! 51 | 52 | ### Info 53 | More information and tools at eddiecameron.fun, @eddiecameron, or support@grapefruitgames.com 54 | -------------------------------------------------------------------------------- /readme.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a945e11e7b756442c9c73ce960a09ca6 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------