private void buildAudioTracks(ProgressBar progress, PlayableDirector director, TimelineAsset timeline) { var audioData = GetComponentsInChildren <RecordedAudio>(includeInactive: true); var sourceToData = audioData.Query().ToDictionary(a => a.target, a => a); progress.Begin(sourceToData.Count, "", "Building Audio Track: ", () => { foreach (var pair in sourceToData) { var track = timeline.CreateTrack <AudioTrack>(null, pair.Value.name); director.SetGenericBinding(track.outputs.Query().First().sourceObject, pair.Key); progress.Begin(pair.Value.data.Count, "", "", () => { foreach (var clipData in pair.Value.data) { progress.Step(clipData.clip.name); var clip = track.CreateClip(clipData.clip); clip.start = clipData.startTime; clip.timeScale = clipData.pitch; clip.duration = clipData.clip.length; } }); } }); }
public static void SimpleTest() { var progressBar = new ProgressBar("Simple Test"); progressBar.Begin(5, "[5]"); for (int i = 0; i < 5; i++) { progressBar.Begin(4, "[4]"); for (int j = 0; j < 4; j++) { progressBar.Begin(3, "[3]"); for (int k = 0; k < 3; k++) { System.Threading.Thread.Sleep(100); if (progressBar.Update(1) == true) { break; } } progressBar.End(); progressBar.Update(1); } progressBar.End(); progressBar.Update(1); } progressBar.End(); }
private AnimationClip generateCompressedClip(ProgressBar progress) { var clip = new AnimationClip(); var bindingMap = new Dictionary <EditorCurveBinding, AnimationCurve>(); var recordings = GetComponentsInChildren <RecordedData>(includeInactive: true); progress.Begin(2, "", "", () => { progress.Begin(recordings.Length, "", "Compressing: ", () => { for (int i = 0; i < recordings.Length; i++) { progress.Begin(2, "", "", () => { var recordingData = recordings[i]; progress.Step(recordingData.name); var toCompress = new Dictionary <EditorCurveBinding, AnimationCurve>(); progress.Begin(recordingData.data.Count, "", "", () => { foreach (var bindingData in recordingData.data) { progress.Step(recordingData.name + " : " + bindingData.propertyName); Type type = recordingData.GetComponents <Component>(). Query(). Select(c => c.GetType()). Concat(typeof(GameObject)). FirstOrDefault(t => t.Name == bindingData.typeName); if (type == null) { //If could not find the type, the component must have been deleted continue; } var binding = EditorCurveBinding.FloatCurve(bindingData.path, type, bindingData.propertyName); toCompress[binding] = bindingData.curve; } }); doCompression(progress, recordingData, toCompress, bindingMap); }); } }); progress.Begin(bindingMap.Count, "", "Assigning Curves: ", () => { foreach (var binding in bindingMap) { progress.Step(binding.Key.propertyName); AnimationUtility.SetEditorCurve(clip, binding.Key, binding.Value); } }); }); return(clip); }
private void buildMethodRecordingTracks(ProgressBar progress, PlayableDirector director, TimelineAsset timeline) { var recordings = GetComponentsInChildren <MethodRecording>(); if (recordings.Length > 0) { progress.Begin(recordings.Length, "", "Building Method Tracks: ", () => { foreach (var recording in recordings) { progress.Step(recording.gameObject.name); try { var track = timeline.CreateTrack <MethodRecordingTrack>(null, recording.gameObject.name); director.SetGenericBinding(track.outputs.Query().First().sourceObject, recording); var clip = track.CreateClip <MethodRecordingClip>(); clip.duration = recording.GetDuration(); } catch (Exception e) { Debug.LogException(e); } } }); } }
/// <summary> /// Инициализация поля /// </summary> /// <param name="zoom">Зум поля</param> /// <param name="progress">Прогресс инициализации</param> /// <param name="input">Управление игроком</param> /// <param name="debug">Отладка</param> public void Init(ZoomControl zoom, ProgressBar progress, IPlayerInput input, IGameDebug debug) { // Подпишемся на изменения зума _zoom = zoom; _input = input; // Инициализируем случайные значения положения игрока на поле и его рейтинг _currentCellPosition = SettingsAccess.GetRandomFieldPosition(); _currentCellItemPosition = SettingsAccess.GetRandomCellPosition(_currentCellPosition); _playerRating = NoiseTool.GetRandomPlayerRating(); // Инициализируем коллекцию ячеек _cells = new CellCollection(_playerRating); // Инициализируем механизм поиска первых <see cref="SettingsAccess.MaxAdvancedVisiblePlanet"/> планет, ближайших по рейтингу к рейтингу игрока _sortedCellsVisitor = gameObject.AddComponent <SortedCellsVisitor>().Init(_cells, _playerRating, debug); _sortedCellsVisitor.OnProcessEnd += OnSearchTopRatingsEnd; string generationString; if (SystemInfo.supportsComputeShaders) { // Если поддерживаются Computed Shader _generator = gameObject.AddComponent <ComputedShaderNoiseGenerator>() .SetDebug(debug); generationString = "GENERATE COMPUTED SHADER"; } else { #if (UNITY_ANDROID) // По идее, должен поддерживаться большинством платформ, добавлять директивы препроцессора по мере тестирования _generator = gameObject.AddComponent <CustomRenderTextureNoiseGenerator>() .SetDebug(debug); generationString = "GENERATE RENDER TEXTURE"; #else // Для всех остальных платформ. _generator = gameObject.AddComponent <CpuNoiseGenerator>() .SetDebug(debug); generationString = "GENERATE CPU"; #endif } _generator.OnProcessEnd += OnGenerationEnd; // Добавим первое задание на генерацию всего поля _generator.AppendRect(SettingsAccess.GetFieldRectPx(_currentCellPosition)); // Отобразим прогресс progress.Begin(() => _generator.GetProgress(), generationString, OnEndInit); }
public IEnumerator AttackCoroutine() { progressBar.Begin(attackTime); ChangeState(State.attack); yield return(new WaitForSeconds(attackTime)); foreach (HitboxAttackManager hitbox in hitboxes) { hitbox.OnAttackTriggerReceived(); } ChangeState(State.stagger); yield return(new WaitForSeconds(attackCooldown)); ChangeState(State.idle); }
protected void finishRecording(ProgressBar progress) { string targetFolderPath = targetFolder.Path; if (targetFolderPath == null) { if (gameObject.scene.IsValid() && !string.IsNullOrEmpty(gameObject.scene.path)) { string sceneFolder = Path.GetDirectoryName(gameObject.scene.path); targetFolderPath = Path.Combine(sceneFolder, "Recordings"); } else { targetFolderPath = Path.Combine("Assets", "Recordings"); } } int folderSuffix = 1; string finalSubFolder; do { finalSubFolder = Path.Combine(targetFolderPath, recordingName + " " + folderSuffix.ToString().PadLeft(2, '0')); folderSuffix++; } while (Directory.Exists(finalSubFolder)); string dataDirectory = Path.Combine(finalSubFolder, "_Data"); Directory.CreateDirectory(dataDirectory); Directory.CreateDirectory(finalSubFolder); AssetDatabase.Refresh(); progress.Begin(6, "Saving Recording", "", () => { if (!_isRecording) { return; } _isRecording = false; //Turn on auto-pushing for all auto-proxy components foreach (var autoProxy in GetComponentsInChildren <AutoValueProxy>()) { autoProxy.autoPushingEnabled = true; } progress.Begin(3, "", "Reverting Scene State", () => { //For all of our transform data, revert to the first piece recorded progress.Begin(_transformData.Count, "", "", () => { foreach (var pair in _transformData) { progress.Step(); var transform = pair.Key; var data = pair.Value; if (transform == null || data.Count == 0) { continue; } data[0].ApplyTo(transform); } }); //For all recorded curves, revert to start of curve progress.Begin(_curves.Count, "", "", () => { AnimationClip tempClip = new AnimationClip(); foreach (var data in _curves) { progress.Step(); AnimationUtility.SetEditorCurve(tempClip, data.binding, data.curve); } tempClip.SampleAnimation(gameObject, 0); }); //For all non-transform components, revert to original serialized values progress.Begin(_initialComponentData.Count, "", "", () => { foreach (var pair in _initialComponentData) { progress.Step(); var component = pair.Key; var sobj = pair.Value; if (component == null || component is Transform) { continue; } //We don't want to revert method recordings! if (component is MethodRecording || component is RecordedAudio) { continue; } var flags = sobj.FindProperty("m_ObjectHideFlags"); if (flags == null) { Debug.LogError("Could not find hide flags for " + component); continue; } //We have to dirty the serialized object somehow //apparently there is no api to do this //all objects have hide flags so we just touch them and revert them int originalFlags = flags.intValue; flags.intValue = ~originalFlags; flags.intValue = originalFlags; try { //Applies previous state of entire component sobj.ApplyModifiedProperties(); } catch (Exception e) { Debug.LogError("Exception when trying to apply properties to " + component); Debug.LogException(e); } } }); }); progress.Begin(1, "", "Patching Materials: ", () => { GetComponentsInChildren(true, _recorders); foreach (var recorder in _recorders) { DestroyImmediate(recorder); } //Patch up renderer references to materials var allMaterials = Resources.FindObjectsOfTypeAll <Material>(). Query(). Where(AssetDatabase.IsMainAsset). ToList(); var renderers = GetComponentsInChildren <Renderer>(includeInactive: true); progress.Begin(renderers.Length, "", "", () => { foreach (var renderer in renderers) { progress.Step(renderer.name); var materials = renderer.sharedMaterials; for (int i = 0; i < materials.Length; i++) { var material = materials[i]; if (material == null) { continue; } if (!AssetDatabase.IsMainAsset(material)) { var matchingMaterial = allMaterials.Query().FirstOrDefault(m => material.name.Contains(m.name) && material.shader == m.shader); if (matchingMaterial != null) { materials[i] = matchingMaterial; } } } renderer.sharedMaterials = materials; } }); }); progress.Begin(_behaviourActivity.Count, "", "Converting Activity Data: ", () => { foreach (var pair in _behaviourActivity) { var targetBehaviour = pair.Key; var activityData = pair.Value; if (targetBehaviour == null) { continue; } progress.Step(targetBehaviour.name); string path = AnimationUtility.CalculateTransformPath(targetBehaviour.transform, transform); Type type = targetBehaviour.GetType(); string propertyName = "m_Enabled"; AnimationCurve curve = new AnimationCurve(); foreach (var dataPoint in activityData) { int index = curve.AddKey(dataPoint.time, dataPoint.enabled ? 1 : 0); AnimationUtility.SetKeyLeftTangentMode(curve, index, AnimationUtility.TangentMode.Constant); AnimationUtility.SetKeyRightTangentMode(curve, index, AnimationUtility.TangentMode.Constant); } if (curve.IsConstant()) { continue; } var binding = EditorCurveBinding.FloatCurve(path, type, propertyName); if (_curves.Query().Any(c => c.binding == binding)) { Debug.LogError("Binding already existed?"); Debug.LogError(binding.path + " : " + binding.propertyName); continue; } _curves.Add(new CurveData() { binding = binding, curve = curve }); } }); progress.Begin(_transformData.Count, "", "Converting Transform Data: ", () => { foreach (var pair in _transformData) { var targetTransform = pair.Key; var targetData = pair.Value; progress.Step(targetTransform.name); string path = AnimationUtility.CalculateTransformPath(targetTransform, transform); bool isActivityConstant = true; bool isPositionConstant = true; bool isRotationConstant = true; bool isScaleConstant = true; { bool startEnabled = targetData[0].enabled; Vector3 startPosition = targetData[0].localPosition; Quaternion startRotation = targetData[0].localRotation; Vector3 startScale = targetData[0].localScale; for (int i = 1; i < targetData.Count; i++) { isActivityConstant &= targetData[i].enabled == startEnabled; isPositionConstant &= targetData[i].localPosition == startPosition; isRotationConstant &= targetData[i].localRotation == startRotation; isScaleConstant &= targetData[i].localScale == startScale; } } for (int i = 0; i < TransformData.CURVE_COUNT; i++) { string propertyName = TransformData.GetName(i); Type type = typeof(Transform); AnimationCurve curve = new AnimationCurve(); var dataType = TransformData.GetDataType(i); switch (dataType) { case TransformDataType.Position: if (isPositionConstant) { continue; } break; case TransformDataType.Rotation: if (isRotationConstant) { continue; } break; case TransformDataType.Scale: if (isScaleConstant) { continue; } break; case TransformDataType.Activity: if (isActivityConstant) { continue; } type = typeof(GameObject); break; } for (int j = 0; j < targetData.Count; j++) { int index = curve.AddKey(targetData[j].time, targetData[j].GetFloat(i)); if (dataType == TransformDataType.Activity) { AnimationUtility.SetKeyLeftTangentMode(curve, index, AnimationUtility.TangentMode.Constant); AnimationUtility.SetKeyRightTangentMode(curve, index, AnimationUtility.TangentMode.Constant); } } var binding = EditorCurveBinding.FloatCurve(path, type, propertyName); if (_curves.Query().Any(c => c.binding == binding)) { Debug.LogError("Duplicate object was created??"); Debug.LogError("Named " + targetTransform.name + " : " + binding.path + " : " + binding.propertyName); } else { _curves.Add(new CurveData() { binding = binding, curve = curve }); } } } }); progress.Begin(_curves.Count, "", "Compressing Data: ", () => { _curves.Sort((a, b) => a.binding.propertyName.CompareTo(b.binding.propertyName)); foreach (var data in _curves) { using (new ProfilerSample("A")) { EditorCurveBinding binding = data.binding; AnimationCurve curve = data.curve; progress.Step(binding.propertyName); GameObject animationGameObject; { var animatedObj = AnimationUtility.GetAnimatedObject(gameObject, binding); if (animatedObj is GameObject) { animationGameObject = animatedObj as GameObject; } else { animationGameObject = (animatedObj as Component).gameObject; } } bool isMatBinding = binding.propertyName.StartsWith("material.") && binding.type.IsSubclassOf(typeof(Renderer)); //But if the curve is constant, just get rid of it! //Except for material curves, which we always need to keep if (curve.IsConstant() && !isMatBinding) { //Check to make sure there are no other matching curves that are //non constant. If X and Y are constant but Z is not, we need to //keep them all :( if (_curves.Query().Where(p => p.binding.path == binding.path && p.binding.type == binding.type && p.binding.propertyName.TrimEnd(2) == binding.propertyName.TrimEnd(2)). All(k => k.curve.IsConstant())) { continue; } } //First do a lossless compression using (new ProfilerSample("B")) { curve = AnimationCurveUtil.Compress(curve, Mathf.Epsilon, checkSteps: 3); } Transform targetTransform = null; var targetObj = AnimationUtility.GetAnimatedObject(gameObject, binding); if (targetObj is GameObject) { targetTransform = (targetObj as GameObject).transform; } else if (targetObj is Component) { targetTransform = (targetObj as Component).transform; } else { Debug.LogError("Target obj was of type " + targetObj.GetType().Name); } } } }); }); progress.Begin(4, "Finalizing Assets", "", () => { var postProcessComponent = gameObject.AddComponent <HierarchyPostProcess>(); GameObject myGameObject = gameObject; DestroyImmediate(this); //Create all the files for the method recording progress.Step("Creating Method Recording Files..."); var methodRecordings = myGameObject.GetComponentsInChildren <MethodRecording>(); for (int i = 0; i < methodRecordings.Length; i++) { var methodRecording = methodRecordings[i]; string fullPath = Path.Combine(finalSubFolder, "MethodRecording_" + i + ".asset"); methodRecording.ExitRecordingMode(fullPath); } postProcessComponent.dataFolder = new AssetFolder(dataDirectory); //Create the asset that holds all of the curve data progress.Begin(_curves.Count, "", "", () => { string curveFile = Path.Combine(dataDirectory, "Curves.data"); using (var writer = File.CreateText(curveFile)) { foreach (var data in _curves) { progress.Step(data.binding.propertyName); var bindingData = new EditorCurveBindingData() { path = data.binding.path, propertyName = data.binding.propertyName, typeName = data.binding.type.Name, curve = data.curve }; writer.WriteLine(JsonUtility.ToJson(bindingData)); } } }); //Create the asset that holds all of the leap data if (_leapData.Count > 0) { progress.Begin(_leapData.Count, "", "", () => { string leapFile = Path.Combine(dataDirectory, "Frames.data"); using (var writer = File.CreateText(leapFile)) { for (int i = 0; i < _leapData.Count; i++) { progress.Step("Frame " + i); writer.WriteLine(JsonUtility.ToJson(_leapData[i])); } } }); } progress.Step("Creating Final Prefab..."); //Init the post process component postProcessComponent.recordingName = recordingName; postProcessComponent.assetFolder = new AssetFolder(finalSubFolder); string prefabPath = Path.Combine(finalSubFolder, recordingName + " Raw.prefab"); PrefabUtility.CreatePrefab(prefabPath.Replace('\\', '/'), myGameObject); AssetDatabase.Refresh(); EditorApplication.isPlaying = false; }); }
public void BuildPlaybackPrefab(ProgressBar progress) { var timeline = ScriptableObject.CreateInstance <TimelineAsset>(); var animationTrack = timeline.CreateTrack <AnimationTrack>(null, "Playback Animation"); var clip = generateCompressedClip(progress); var playableAsset = ScriptableObject.CreateInstance <AnimationPlayableAsset>(); playableAsset.clip = clip; playableAsset.hideFlags = HideFlags.HideInInspector | HideFlags.HideInHierarchy; playableAsset.name = "Recorded Animation"; var timelineClip = animationTrack.CreateClip(clip); timelineClip.duration = clip.length; timelineClip.asset = playableAsset; timelineClip.displayName = "Recorded Animation"; //If a clip is not recordable, it will not show up as editable in the timeline view. //For whatever reason unity decided that imported clips are not recordable, so we hack a //private variable to force them to be! This seems to have no ill effects but if things go //wrong we can just revert this line timelineClip.GetType().GetField("m_Recordable", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(timelineClip, true); //Try to generate a leap recording if we have leap data RecordingTrack recordingTrack = null; LeapRecording leapRecording = null; string framesPath = Path.Combine(dataFolder.Path, "Frames.data"); if (File.Exists(framesPath)) { List <Frame> frames = new List <Frame>(); progress.Begin(1, "Loading Leap Data", "", () => { progress.Step(); using (var reader = File.OpenText(framesPath)) { while (true) { string line = reader.ReadLine(); if (string.IsNullOrEmpty(line)) { break; } frames.Add(JsonUtility.FromJson <Frame>(line)); } } }); leapRecording = ScriptableObject.CreateInstance(_leapRecordingType) as LeapRecording; if (leapRecording != null) { leapRecording.name = "Recorded Leap Data"; leapRecording.LoadFrames(frames); } else { Debug.LogError("Unable to create Leap recording: Invalid type specification for " + "LeapRecording implementation.", this); } } string assetPath = Path.Combine(assetFolder.Path, recordingName + ".asset"); AssetDatabase.CreateAsset(timeline, assetPath); AssetDatabase.AddObjectToAsset(playableAsset, timeline); AssetDatabase.AddObjectToAsset(animationTrack, timeline); AssetDatabase.AddObjectToAsset(clip, timeline); //If we do have a leap recording, create a recording track to house it if (leapRecording != null) { recordingTrack = timeline.CreateTrack <RecordingTrack>(null, "Leap Recording"); var recordingClip = recordingTrack.CreateDefaultClip(); recordingClip.duration = leapRecording.length; var recordingAsset = recordingClip.asset as RecordingClip; recordingAsset.recording = leapRecording; AssetDatabase.AddObjectToAsset(leapRecording, timeline); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); //Create the playable director and link it to the new timeline var director = gameObject.AddComponent <PlayableDirector>(); director.playableAsset = timeline; //Create the animator gameObject.AddComponent <Animator>(); //Link the animation track to the animator //(it likes to point to gameobject instead of the animator directly) director.SetGenericBinding(animationTrack.outputs.Query().First().sourceObject, gameObject); //Destroy existing provider var provider = gameObject.GetComponentInChildren <LeapProvider>(); if (provider != null) { GameObject providerObj = provider.gameObject; DestroyImmediate(provider); //If a leap recording track exists, spawn a playable provider and link it to the track if (recordingTrack != null) { var playableProvider = providerObj.AddComponent <LeapPlayableProvider>(); director.SetGenericBinding(recordingTrack.outputs.Query().First().sourceObject, playableProvider); } } buildAudioTracks(progress, director, timeline); buildMethodRecordingTracks(progress, director, timeline); progress.Begin(1, "", "Finalizing Prefab", () => { GameObject myGameObject = gameObject; DestroyImmediate(this); string prefabPath = Path.Combine(assetFolder.Path, recordingName + ".prefab"); PrefabUtility.SaveAsPrefabAsset(myGameObject, prefabPath.Replace('\\', '/')); }); }
private void doCompression(ProgressBar progress, UnityEngine.Object targetObject, Dictionary <EditorCurveBinding, AnimationCurve> toCompress, Dictionary <EditorCurveBinding, AnimationCurve> bindingMap) { var propertyToMaxError = calculatePropertyErrors(targetObject); List <EditorCurveBinding> bindings; progress.Begin(6, "", targetObject.name, () => { //First do rotations bindings = toCompress.Keys.Query().ToList(); progress.Begin(bindings.Count, "", "", () => { foreach (var wBinding in bindings) { progress.Step(); if (!wBinding.propertyName.EndsWith(".w")) { continue; } string property = wBinding.propertyName.Substring(0, wBinding.propertyName.Length - 2); string xProp = property + ".x"; string yProp = property + ".y"; string zProp = property + ".z"; var xMaybe = toCompress.Keys.Query().FirstOrNone(t => t.propertyName == xProp); var yMaybe = toCompress.Keys.Query().FirstOrNone(t => t.propertyName == yProp); var zMaybe = toCompress.Keys.Query().FirstOrNone(t => t.propertyName == zProp); Maybe.MatchAll(xMaybe, yMaybe, zMaybe, (xBinding, yBinding, zBinding) => { float maxAngleError; if (!propertyToMaxError.TryGetValue(property, out maxAngleError)) { maxAngleError = rotationMaxError; } AnimationCurve compressedX, compressedY, compressedZ, compressedW; AnimationCurveUtil.CompressRotations(toCompress[xBinding], toCompress[yBinding], toCompress[zBinding], toCompress[wBinding], out compressedX, out compressedY, out compressedZ, out compressedW, maxAngleError); bindingMap[xBinding] = compressedX; bindingMap[yBinding] = compressedY; bindingMap[zBinding] = compressedZ; bindingMap[wBinding] = compressedW; toCompress.Remove(xBinding); toCompress.Remove(yBinding); toCompress.Remove(zBinding); toCompress.Remove(wBinding); }); } }); //Next do scales bindings = toCompress.Keys.Query().ToList(); progress.Begin(bindings.Count, "", "", () => { foreach (var binding in bindings) { progress.Step(); if (!binding.propertyName.EndsWith(".x") && !binding.propertyName.EndsWith(".y") && !binding.propertyName.EndsWith(".z")) { continue; } if (!binding.propertyName.Contains("LocalScale")) { continue; } bindingMap[binding] = AnimationCurveUtil.CompressScale(toCompress[binding], scaleMaxError); toCompress.Remove(binding); } }); //Next do positions bindings = toCompress.Keys.Query().ToList(); progress.Begin(bindings.Count, "", "", () => { foreach (var xBinding in bindings) { progress.Step(); if (!xBinding.propertyName.EndsWith(".x")) { continue; } string property = xBinding.propertyName.Substring(0, xBinding.propertyName.Length - 2); string yProp = property + ".y"; string zProp = property + ".z"; var yMaybe = toCompress.Keys.Query().FirstOrNone(t => t.propertyName == yProp); var zMaybe = toCompress.Keys.Query().FirstOrNone(t => t.propertyName == zProp); Maybe.MatchAll(yMaybe, zMaybe, (yBinding, zBinding) => { float maxDistanceError; if (!propertyToMaxError.TryGetValue(property, out maxDistanceError)) { maxDistanceError = positionMaxError; } AnimationCurve compressedX, compressedY, compressedZ; AnimationCurveUtil.CompressPositions(toCompress[xBinding], toCompress[yBinding], toCompress[zBinding], out compressedX, out compressedY, out compressedZ, maxDistanceError); bindingMap[xBinding] = compressedX; bindingMap[yBinding] = compressedY; bindingMap[zBinding] = compressedZ; toCompress.Remove(xBinding); toCompress.Remove(yBinding); toCompress.Remove(zBinding); }); } }); //Next do colors bindings = toCompress.Keys.Query().ToList(); progress.Begin(bindings.Count, "", "", () => { foreach (var rBinding in bindings) { progress.Step(); if (!rBinding.propertyName.EndsWith(".r")) { continue; } string property = rBinding.propertyName.Substring(0, rBinding.propertyName.Length - 2); string gProp = property + ".g"; string bProp = property + ".b"; var gMaybe = toCompress.Keys.Query().FirstOrNone(t => t.propertyName == gProp); var bMaybe = toCompress.Keys.Query().FirstOrNone(t => t.propertyName == bProp); Maybe.MatchAll(gMaybe, bMaybe, (gBinding, bBinding) => { AnimationCurve compressedR, compressedG, compressedB; AnimationCurveUtil.CompressColorsHSV(toCompress[rBinding], toCompress[gBinding], toCompress[bBinding], out compressedR, out compressedG, out compressedB, colorHueMaxError, colorSaturationMaxError, colorValueMaxError); bindingMap[rBinding] = compressedR; bindingMap[gBinding] = compressedG; bindingMap[bBinding] = compressedB; }); } }); //Then do color alpha bindings = toCompress.Keys.Query().ToList(); progress.Begin(bindings.Count, "", "", () => { foreach (var aBinding in bindings) { progress.Step(); if (!aBinding.propertyName.EndsWith(".a")) { continue; } var compressedA = AnimationCurveUtil.Compress(toCompress[aBinding], colorAlphaMaxError); toCompress.Remove(aBinding); bindingMap[aBinding] = compressedA; } }); //Then everything else bindings = toCompress.Keys.Query().ToList(); progress.Begin(bindings.Count, "", "", () => { foreach (var binding in bindings) { progress.Step(); float maxError; if (!propertyToMaxError.TryGetValue(binding.propertyName, out maxError)) { maxError = genericMaxError; } var compressedCurve = AnimationCurveUtil.Compress(toCompress[binding], maxError); toCompress.Remove(binding); bindingMap[binding] = compressedCurve; } }); }); }
private AnimationClip generateCompressedClip(ProgressBar progress) { var clip = new AnimationClip(); clip.name = "Recorded Animation"; List <EditorCurveBindingData> curveData = new List <EditorCurveBindingData>(); progress.Begin(1, "Opening Curve Files...", "", () => { progress.Step(); using (var reader = File.OpenText(Path.Combine(dataFolder.Path, "Curves.data"))) { while (true) { string line = reader.ReadLine(); if (string.IsNullOrEmpty(line)) { break; } curveData.Add(JsonUtility.FromJson <EditorCurveBindingData>(line)); } } }); progress.Begin(2, "", "", () => { var bindingMap = new Dictionary <EditorCurveBinding, AnimationCurve>(); Dictionary <string, Type> nameToType = new Dictionary <string, Type>(); foreach (var component in GetComponentsInChildren <Component>()) { nameToType[component.GetType().Name] = component.GetType(); } nameToType[typeof(GameObject).Name] = typeof(GameObject); var toCompress = new Dictionary <EditorCurveBinding, AnimationCurve>(); var targetObjects = new HashSet <UnityEngine.Object>(); foreach (var data in curveData) { Type type; if (!nameToType.TryGetValue(data.typeName, out type)) { continue; } var binding = EditorCurveBinding.FloatCurve(data.path, type, data.propertyName); var targetObj = AnimationUtility.GetAnimatedObject(gameObject, binding); if (targetObj == null) { continue; } toCompress[binding] = data.curve; targetObjects.Add(targetObj); } progress.Begin(targetObjects.Count, "Compressing Curves", "", () => { foreach (var targetObj in targetObjects) { var filteredCurves = new Dictionary <EditorCurveBinding, AnimationCurve>(); foreach (var curve in toCompress) { if (AnimationUtility.GetAnimatedObject(gameObject, curve.Key) == targetObj) { filteredCurves[curve.Key] = curve.Value; } } doCompression(progress, targetObj, filteredCurves, bindingMap); } }); progress.Begin(bindingMap.Count, "Assigning Curves", "", () => { foreach (var binding in bindingMap) { progress.Step(binding.Key.propertyName); AnimationUtility.SetEditorCurve(clip, binding.Key, binding.Value); } }); }); return(clip); }
public void BuildPlaybackPrefab(ProgressBar progress) { var timeline = ScriptableObject.CreateInstance <TimelineAsset>(); var animationTrack = timeline.CreateTrack <AnimationTrack>(null, "Playback Animation"); var clip = generateCompressedClip(progress); var timelineClip = animationTrack.CreateClip(clip); timelineClip.duration = clip.length; timelineClip.asset = clip; timelineClip.underlyingAsset = clip; //Try to generate a leap recording if we have leap data RecordingTrack recordingTrack = null; LeapRecording leapRecording = null; if (leapData.Count > 0) { leapRecording = ScriptableObject.CreateInstance(_leapRecordingType) as LeapRecording; if (leapRecording != null) { leapRecording.LoadFrames(leapData); } else { Debug.LogError("Unable to create Leap recording: Invalid type specification for " + "LeapRecording implementation.", this); } } string assetPath = Path.Combine(assetFolder.Path, recordingName + ".asset"); AssetDatabase.CreateAsset(timeline, assetPath); AssetDatabase.AddObjectToAsset(animationTrack, timeline); AssetDatabase.AddObjectToAsset(clip, timeline); //If we do have a leap recording, create a recording track to house it if (leapRecording != null) { recordingTrack = timeline.CreateTrack <RecordingTrack>(null, "Leap Recording"); var recordingClip = recordingTrack.CreateDefaultClip(); recordingClip.duration = leapRecording.length; var recordingAsset = recordingClip.asset as RecordingClip; recordingAsset.recording = leapRecording; AssetDatabase.AddObjectToAsset(leapRecording, timeline); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); foreach (var recording in GetComponentsInChildren <RecordedData>(includeInactive: true)) { DestroyImmediate(recording); } //Create the playable director and link it to the new timeline var director = gameObject.AddComponent <PlayableDirector>(); director.playableAsset = timeline; //Create the animator and link it to the animation track var animator = gameObject.AddComponent <Animator>(); director.SetGenericBinding(animationTrack.outputs.Query().First().sourceObject, animator); //Destroy existing provider var provider = gameObject.GetComponentInChildren <LeapProvider>(); if (provider != null) { GameObject providerObj = provider.gameObject; DestroyImmediate(provider); //If a leap recording track exists, spawn a playable provider and link it to the track if (recordingTrack != null) { var playableProvider = providerObj.AddComponent <LeapPlayableProvider>(); director.SetGenericBinding(recordingTrack.outputs.Query().First().sourceObject, playableProvider); } } buildAudioTracks(progress, director, timeline); progress.Begin(1, "", "Finalizing Prefab", () => { GameObject myGameObject = gameObject; DestroyImmediate(this); string prefabPath = Path.Combine(assetFolder.Path, recordingName + ".prefab"); PrefabUtility.CreatePrefab(prefabPath.Replace('\\', '/'), myGameObject); }); }
protected void finishRecording(ProgressBar progress) { progress.Begin(5, "Saving Recording", "", () => { if (!_isRecording) { return; } _isRecording = false; //Turn on auto-pushing for all auto-proxy components foreach (var autoProxy in GetComponentsInChildren <AutoValueProxy>()) { autoProxy.autoPushingEnabled = true; } progress.Begin(1, "", "Reverting Scene State", () => { foreach (var pair in _initialTransformData) { pair.Key.localPosition = pair.Value.localPosition; pair.Key.localRotation = pair.Value.localRotation; pair.Key.localScale = pair.Value.localScale; pair.Key.gameObject.SetActive(pair.Value.enabled); } foreach (var pair in _initialActivityData) { EditorUtility.SetObjectEnabled(pair.Key, pair.Value); } }); progress.Begin(1, "", "Patching Materials: ", () => { GetComponentsInChildren(true, _recorders); foreach (var recorder in _recorders) { DestroyImmediate(recorder); } //Patch up renderer references to materials var allMaterials = Resources.FindObjectsOfTypeAll <Material>(). Query(). Where(AssetDatabase.IsMainAsset). ToList(); var renderers = GetComponentsInChildren <Renderer>(includeInactive: true); progress.Begin(renderers.Length, "", "", () => { foreach (var renderer in renderers) { progress.Step(renderer.name); var materials = renderer.sharedMaterials; for (int i = 0; i < materials.Length; i++) { var material = materials[i]; if (!AssetDatabase.IsMainAsset(material)) { var matchingMaterial = allMaterials.Query().FirstOrDefault(m => material.name.Contains(m.name) && material.shader == m.shader); if (matchingMaterial != null) { materials[i] = matchingMaterial; } } } renderer.sharedMaterials = materials; } }); }); progress.Begin(_behaviourActivity.Count, "", "Converting Activity Data: ", () => { foreach (var pair in _behaviourActivity) { var targetBehaviour = pair.Key; var activityData = pair.Value; progress.Step(targetBehaviour.name); string path = AnimationUtility.CalculateTransformPath(targetBehaviour.transform, transform); Type type = targetBehaviour.GetType(); string propertyName = "m_Enabled"; AnimationCurve curve = new AnimationCurve(); foreach (var dataPoint in activityData) { int index = curve.AddKey(dataPoint.time, dataPoint.enabled ? 1 : 0); AnimationUtility.SetKeyLeftTangentMode(curve, index, AnimationUtility.TangentMode.Constant); AnimationUtility.SetKeyRightTangentMode(curve, index, AnimationUtility.TangentMode.Constant); } if (curve.IsConstant()) { continue; } var binding = EditorCurveBinding.FloatCurve(path, type, propertyName); if (_curves.ContainsKey(binding)) { Debug.LogError("Binding already existed?"); Debug.LogError(binding.path + " : " + binding.propertyName); continue; } _curves.Add(binding, curve); } }); progress.Begin(_transformData.Count, "", "Converting Transform Data: ", () => { foreach (var pair in _transformData) { var targetTransform = pair.Key; var targetData = pair.Value; progress.Step(targetTransform.name); string path = AnimationUtility.CalculateTransformPath(targetTransform, transform); bool isActivityConstant = true; bool isPositionConstant = true; bool isRotationConstant = true; bool isScaleConstant = true; { bool startEnabled = targetData[0].enabled; Vector3 startPosition = targetData[0].localPosition; Quaternion startRotation = targetData[0].localRotation; Vector3 startScale = targetData[0].localScale; for (int i = 1; i < targetData.Count; i++) { isActivityConstant &= targetData[i].enabled == startEnabled; isPositionConstant &= targetData[i].localPosition == startPosition; isRotationConstant &= targetData[i].localRotation == startRotation; isScaleConstant &= targetData[i].localScale == startScale; } } for (int i = 0; i < TransformData.CURVE_COUNT; i++) { string propertyName = TransformData.GetName(i); Type type = typeof(Transform); AnimationCurve curve = new AnimationCurve(); var dataType = TransformData.GetDataType(i); switch (dataType) { case TransformDataType.Position: if (isPositionConstant) { continue; } break; case TransformDataType.Rotation: if (isRotationConstant) { continue; } break; case TransformDataType.Scale: if (isScaleConstant) { continue; } break; case TransformDataType.Activity: if (isActivityConstant) { continue; } type = typeof(GameObject); break; } for (int j = 0; j < targetData.Count; j++) { int index = curve.AddKey(targetData[j].time, targetData[j].GetFloat(i)); if (dataType == TransformDataType.Activity) { AnimationUtility.SetKeyLeftTangentMode(curve, index, AnimationUtility.TangentMode.Constant); AnimationUtility.SetKeyRightTangentMode(curve, index, AnimationUtility.TangentMode.Constant); } } var binding = EditorCurveBinding.FloatCurve(path, type, propertyName); if (_curves.ContainsKey(binding)) { Debug.LogError("Duplicate object was created??"); Debug.LogError("Named " + targetTransform.name + " : " + binding.path + " : " + binding.propertyName); } else { _curves.Add(binding, curve); } } } }); progress.Begin(_curves.Count, "", "Compressing Data: ", () => { foreach (var pair in _curves) { EditorCurveBinding binding = pair.Key; AnimationCurve curve = pair.Value; progress.Step(binding.propertyName); GameObject animationGameObject; { var animatedObj = AnimationUtility.GetAnimatedObject(gameObject, binding); if (animatedObj is GameObject) { animationGameObject = animatedObj as GameObject; } else { animationGameObject = (animatedObj as Component).gameObject; } } //But if the curve is constant, just get rid of it! if (curve.IsConstant()) { //Check to make sure there are no other matching curves that are //non constant. If X and Y are constant but Z is not, we need to //keep them all :( if (_curves.Query().Where(p => p.Key.path == binding.path && p.Key.type == binding.type && p.Key.propertyName.TrimEnd(2) == binding.propertyName.TrimEnd(2)). All(k => k.Value.IsConstant())) { continue; } } //First do a lossless compression curve = AnimationCurveUtil.Compress(curve, Mathf.Epsilon); Transform targetTransform = null; var targetObj = AnimationUtility.GetAnimatedObject(gameObject, binding); if (targetObj is GameObject) { targetTransform = (targetObj as GameObject).transform; } else if (targetObj is Component) { targetTransform = (targetObj as Component).transform; } else { Debug.LogError("Target obj was of type " + targetObj.GetType().Name); } var dataRecorder = targetTransform.GetComponent <RecordedData>(); if (dataRecorder == null) { dataRecorder = targetTransform.gameObject.AddComponent <RecordedData>(); } dataRecorder.data.Add(new RecordedData.EditorCurveBindingData() { path = binding.path, propertyName = binding.propertyName, typeName = binding.type.Name, curve = curve }); } }); progress.Step("Finalizing Prefab..."); var postProcessComponent = gameObject.AddComponent <HierarchyPostProcess>(); GameObject myGameObject = gameObject; DestroyImmediate(this); string targetFolderPath = targetFolder.Path; if (targetFolderPath == null) { if (myGameObject.scene.IsValid() && !string.IsNullOrEmpty(myGameObject.scene.path)) { string sceneFolder = Path.GetDirectoryName(myGameObject.scene.path); targetFolderPath = Path.Combine(sceneFolder, "Recordings"); } else { targetFolderPath = Path.Combine("Assets", "Recordings"); } } int folderSuffix = 1; string finalSubFolder; do { finalSubFolder = Path.Combine(targetFolderPath, recordingName + " " + folderSuffix.ToString().PadLeft(2, '0')); folderSuffix++; } while (Directory.Exists(finalSubFolder)); Directory.CreateDirectory(finalSubFolder); AssetDatabase.Refresh(); postProcessComponent.recordingName = recordingName; postProcessComponent.assetFolder.Path = finalSubFolder; postProcessComponent.leapData = _leapData; string prefabPath = Path.Combine(finalSubFolder, recordingName + " Raw.prefab"); PrefabUtility.CreatePrefab(prefabPath.Replace('\\', '/'), myGameObject); AssetDatabase.Refresh(); EditorApplication.isPlaying = false; }); }