Search Unity

[Depreciated] Ultimate Replay - Complete state based replay system

Discussion in 'Assets and Asset Store' started by scottyboy805, Jun 28, 2017.

  1. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    Ya, I checked the previous version, the replayObjects count is still 1 in PrepareForPlayback function so the gameObject is able to be turned on
     
  2. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    So you mean we could keep track of the active state of children but not itself in the latest version, is that right?
     
  3. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Yes that is correct. If the parent is enabled and has the script attached it will be able to record the state of its child objects.
     
  4. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    Could we do something about this? It would make the workflow pretty complicated if you need to create an empty parent or find somewhere else to attach the ReplayRecordActive script.
     
    Last edited: Jan 17, 2019
  5. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Do you have any suggestions as to how this could be improved considering that we changed to OnEnable/OnDisable to avoid other issues we were having, most notably in support for pooling objects. I think it is more intuitive and inline with Unity's approach that disabled objects don't get recorded. All I can think at the moment would be to have a variable on the ReplayObject component where you can toggle between recording when disabled and not recording when disabled but this would not be ideal. Let me know your thoughts.
     
  6. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    You are right, actually I am concerning about pooling right now. In my current setup, I can simply attach the ReplayRecordActive anywhere and just assign any gameObject I want to record the active state, as long as they are under the hierarchy. And so I attach the script in the parent of certain pooling gameObject prefab I want to record.

    With the new update, I have to either 1. create an empty gameObject as parent for every pooling prefab which is super complicated, 2. attach ReplayRecordActive to the PoolManager, but instead of recording all the children under the hierarchy, maybe I could look for ReplayTransform with GetComponentInChildren in runtime, no matter if I really want to record the active state or not.

    In either way it's not intuitive as the situation in the previous version though. I understand it could save some memory for not recording the inactive gameObjects. The toggle you mentioned may be a quick solution. Or you could suggest any way I can implement recording the active state of pooling objects in your mind?
     
  7. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    If you are intending to pool your replay objects then surely you will not want them to be recorded while they are disable in the scene pool as that would cause issues. This was one of the main issues another customer had when trying to implement pooling. I would have thought that not recording disabled objects would help in this scenario.

    I cannot think of any better alternative at the moment but I will continue to think about it for a little while. We may end up just adding an option to toggle disabled recording in the end but it doesn't seem like a nice solution.
     
  8. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    If you are actually handling pooling, you would need to record the active state as well to make the replay works right? What kind of script structure you are using for pooling?
     
  9. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    In all the pooling patterns I have used a disabled game object represents a dead object that has been recycled and is waiting to be reused as another instance, Ie instead of calling 'Destroy' you would call 'Pool.Despawn' or something similar which would internally disable and cache the object until it is needed. In this situation there are no alive disabled objects sot he problem doesn't exist.
     
  10. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    Sorry, Pool.Despawn is not Unity internal stuff right?

    For doing pooling, I simply instantiate needed instances and SetActive(false) them all on start, when you need an instance, I activate it, when you don't need it, I deactivate it. That's how I do pooling. So this involves SetActive of gameObjects and that's why I need to record the active state of gameObjects.

    Isn't it your case? How would you hide unused instances without recording active state? Or your client does not need to record instances in replay?
     
  11. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    'Pool.Despawn' is not built in or anything, I only used it as an example of some pooling API you might be using. Here is a very basic example of how the pooling systems that I have experienced work:

    Code (CSharp):
    1. class ExamplePool : MonoBehaviour
    2.     {
    3.         HashSet<GameObject> trackedObjects = new HashSet<GameObject>();
    4.         Stack<GameObject> pooledObjects = new Stack<GameObject>();
    5.         public GameObject poolPrefab;
    6.         public int initialPoolSize = 10;
    7.  
    8.         public void Start()
    9.         {
    10.             // Prewarm the pool
    11.             for(int i = 0; i < initialPoolSize; i++)
    12.             {
    13.                 GameObject instance = Instantiate(poolPrefab, transform, false);
    14.  
    15.                 trackedObjects.Add(instance);
    16.                 pooledObjects.Push(instance);
    17.             }
    18.         }
    19.        
    20.         public GameObject SpawnFromPool()
    21.         {
    22.             GameObject result = null;
    23.  
    24.             if (pooledObjects.Count > 0)
    25.                 result = pooledObjects.Pop();
    26.             else
    27.             {
    28.                 result = Instantiate(poolPrefab, transform, false);
    29.                 trackedObjects.Add(result);
    30.             }
    31.  
    32.             // Make the object alive
    33.             result.SetActive(true);
    34.  
    35.             return result;
    36.         }
    37.  
    38.         public void ReturnToPool(GameObject instance)
    39.         {
    40.             if(trackedObjects.Contains(instance) == true)
    41.             {
    42.                 // Make the object dead
    43.                 instance.SetActive(false);
    44.                 pooledObjects.Push(instance);
    45.             }
    46.         }
    47.     }
    As you can see an objects state does not need to be tracked because generally the pooled object is cached in some internal container and while it is in that container it can only be in a certain state. In this example the container is the 'pooledObjects' stack and when objects exist in this stack then are pooled and as a result must be disabled. Most of the major pooling assets I have used have a similar sort of setup. I hope this helps you.
     
  12. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    I could actually see you using SetActive(true) to make instance alive and SetActive(false) to make instance dead. So without recording the active state of the instance, it would be impossible to make the replay working if the instance is alive and dead during the recording, right? I wonder where did you put the script, something similar to ReplayRecordActive I posted before?
     
  13. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    You will still be able to change the state of the object during recording which will cause the object to be removed from the recording list or is this not what you want? The whole point of this approach is to allow for pooling objects where alive objects are enabled and dead objects are disabled and there is not in between. Ie an object cannot be alive and disabled. I may have the wrong end of the stick wig what you want to achieve.
     
  14. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    I tried to copy the ExamplePool you have posted, but once the objects are "ReturnToPool" during recording, they are no longer activated during the playback.
     
  15. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Hi,
    I have attached a demo scene using the pooling script I posted so you can see how it works. One step I left out but is important was to register your pool with Ultimate Replay so that it uses the pool for spawning objects rather than just using Instantiate. You can see that this is done in the 'ExamplePool.cs' script inside the 'Awake' method. You just need to import the package into a project where Ultimate Replay is installed and then open and run the scene at 'Assets/Exclude/DemoPooling.unity'.
     

    Attached Files:

  16. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    Thanks for the demo. The demo works to me. However we have a bit complicated local network system to sync pooling objects so I couldn't make "OnReplayInstantiate" being called. I guess I would stick to the old version for now.
     
  17. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Ok no problem. Let me know if you have any more questions.
     
  18. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    I saw the following errors when I tried to build with Development Build and Script Debugging

    Code (CSharp):
    1. Type '[Assembly-CSharp]UltimateReplay.ReplayManager' has an extra field 'playbackEndBehaviour' of type 'UltimateReplay.PlaybackEndBehaviour' in the player and thus can't be serialized  (expected 'dontDestroyOnLoad' of type 'System.Boolean')
    2. UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)
    Code (CSharp):
    1. Type '[Assembly-CSharp]UltimateReplay.ReplayManager' has an extra field 'recordFPS' of type 'System.Int32' in the player and thus can't be serialized  (expected 'playbackEndBehaviour' of type 'UltimateReplay.PlaybackEndBehaviour')
    2. UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)
    Code (CSharp):
    1. Type '[Assembly-CSharp]UltimateReplay.ReplayManager' has an extra field 'prefabs' of type 'UnityEngine.GameObject[]' in the player and thus can't be serialized  (expected 'playbackEndBehaviour' of type 'UltimateReplay.PlaybackEndBehaviour')
    2. UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)
    Code (CSharp):
    1. Fields serialized in Editor, class 'UltimateReplay.ReplayManager'
    2.     'recordOnStart' of type 'System.Boolean'
    3.     'dontDestroyOnLoad' of type 'System.Boolean'
    4.     'playbackEndBehaviour' of type 'UltimateReplay.PlaybackEndBehaviour'
    5.     'updateMethod' of type 'UltimateReplay.UpdateMethod'
    6.     'recordFPS' of type 'System.Int32'
    7.     'prefabs' of type 'UnityEngine.GameObject[]'
    8.  
    9. UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)
    10.  
    Code (CSharp):
    1. Fields serialized in target platform, class 'UltimateReplay.ReplayManager'
    2.     'recordOnStart' of type 'System.Boolean'
    3.     'playbackEndBehaviour' of type 'UltimateReplay.PlaybackEndBehaviour'
    4.     'dontDestroyOnLoad' of type 'System.Boolean'
    5.     'recordFPS' of type 'System.Int32'
    6.     'prefabs' of type 'UnityEngine.GameObject[]'
    7.  
    8. UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)
     
  19. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Hi again,
    Thanks for reporting.
    Can you let me know what Unity version you are using because we have not been able to reproduce these errors yet. Also does the build complete or does it fail? If it completes does the built game work as expected or are there issues in build?
     
  20. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    Strange, I tried again and there are no errors. Forget it please.
     
  21. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Very strange indeed. Did you restart the editor or something because we have found that it can usually go a long way to fixing these sort of bizarre unexpected errors.
     
  22. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    I didn't restart the editor as I can remember. I pulled some unrelated code from my colleagues. No idea.
     
  23. Arsinx

    Arsinx

    Joined:
    Apr 14, 2014
    Posts:
    55
    Hello,
    when is the Save to File Option is going to be coming out?
     
  24. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Hi,
    Streaming to file is actually released although the documentation has yet to be updated. To use it you will need to remove the default 'ReplayMemoryTarget' component from the replay manager game object and replace it with a 'ReplayFIleTarget' component and you should be good to go. There are a couple of examples that make use of file streaming so it would be worth taking a look at those as well. Let me know if you need any help setting it up or have any more questions.
     
    Arsinx likes this.
  25. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    Hi @scottyboy805 ,

    In fact the "System.InvalidOperationException: There is no data in the object state" error occurs occasionally when I develop. I notice that state is null when this occurs (InvokePrepareForGameplay in ComponentPreparer.cs) since it just create a new ReplayState with nothing inside.

    I further checked, componentData.ContainsKey(hash) is false, any idea why it's trying to get the state which does not contain in componentData?
     
    Last edited: Mar 11, 2019
  26. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Hi,
    The only reason I can think of that would cause the system to try to find a component hash that doesn't exist is that the component did not exist while recording and was added to the game object during playback. Could this be the case? If so then it should be a simple fix.
     
  27. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    I added log in "InvokePrepareForPlayback". Imagine I have 12 objects with the same name (actually from the same pool). Right now, 2 are active and 10 are inactive. In "InvokePrepareForPlayback" only 11 gameObjects show log (try to add state to componentData there), one of the active object didn't show log, and that's the exact same object caused error when trying to run "InvokePrepareForGameplay"
     
  28. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    I checked replayObjects Count is the same in "PrepareForPlayback" and "PrepareForGameplay". I added log in "PrepareForGameplay" in DefaultReplayPreparer.cs, right after
    Code (CSharp):
    1. if (component == null)
    I added debug log, the missing object in my previous reply didn't show up. So the component is gone when running "PrepareForGameplay". However, the whole gameObject exists in the scene.
     
  29. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    Still remember I have this script?

    Code (CSharp):
    1. public class ReplayRecordActive : ReplayBehaviour
    2. {
    3.     public GameObject[] go;
    4.  
    5.     public override void OnReplaySerialize(ReplayState state)
    6.     {
    7.         if (IsRecording)
    8.         {
    9.             for (int i = 0; i < go.Length; i++)
    10.             {
    11.                 state.Write(go[i].activeInHierarchy);
    12.             }
    13.         }
    14.     }
    15.  
    16.     public override void OnReplayDeserialize(ReplayState state)
    17.     {
    18.         if (IsReplaying)
    19.         {
    20.             for (int i = 0; i < go.Length; i++)
    21.             {
    22.                 go[i].SetActive(state.ReadBool());
    23.             }
    24.         }
    25.     }
    26. }
    The only difference between that mentioned object and the other 11 gameObjects is: that is the only object which toggled active state once during recording. All the remaining 11 gameObjects have no state change during recording.
     
  30. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Are you able to find out what component type is causing the problems? Is it a script or a built in Unity component? You mentioned that you are also using a pool for the objects. Is it possible your pool is adding and removing the same component possibly to reset it?
     
  31. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    When toggling the objects active state are you sure that the object is active before recording ends otherwise it will not be picked up in the scene. The state preparation is done when switching from live or recording mode to replaying mode and this is when all active replay objects are collected for state preparation. The objects that will be prepared are stored in the 'ModScene.replayObjects' field. It may be worth checking this field in the debugger to make sure that all expected replay objects are present.
     
  32. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    Problem solved.

    replayObject.GetComponentsInChildren<Component>() gets only active gameObjects. By the time playback starts, the above mentioned missing gameObject is inactive, so it's not pushed to componentData.

    I changed it to replayObject.GetComponentsInChildren<Component>(true), done.

    I am using 1.1.3 anyway.
     
  33. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Glad to hear that you sorted the problem. Let me know if you have any more issues.
     
  34. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    Any news on supporting animations?

    We can use ReplayEvent to play animations but we are running into problems when the recording starts, the animation has already started playing, then the animation will not be played during playback
     
  35. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Have you had any luck with the ReplayAnimator component? If it is not able to replay your animations correctly then we would be interested in looking into it perhaps with a repro project. The only alternative at the moment is to use a ReplayTransform at each joint but this assumes that your animated object is rigged.
     
  36. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    ReplayAnimator didn't record the enabled state and I don't want to skip all animators in DefaultReplayPreparer so I added
    Code (CSharp):
    1. state.Write(observedAnimator.enabled);
    in OnReplaySerialize, and
    Code (CSharp):
    1. observedAnimator.enabled = state.ReadBool();
    in OnReplayDeserialize
     
  37. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    The animator should be re-enabled during playback by the ReplayAnimator component. In the ReplayAnimator script there is the following code which will make sure that the target animator is enabled so that it can be replayed properly:

    Code (CSharp):
    1. public override void OnReplayUpdate()
    2.         {
    3.             // Check for animator
    4.             if (observedAnimator == null)
    5.                 return;
    6.  
    7.             // Make sure the animator is enabled for playback
    8.             observedAnimator.enabled = true;
    .
     
  38. stevenwanhk

    stevenwanhk

    Joined:
    Jul 20, 2016
    Posts:
    113
    Ah, I don't have
    Code (CSharp):
    1. observedAnimator.enabled = true;
    in my version. That makes sense. Thanks.
     
  39. avaiz

    avaiz

    Joined:
    Aug 13, 2018
    Posts:
    2
    Hello,

    I am hoping to use UltimateReplay to record gameplay, save the gameplay to a file, then play the saved file back. It's clear to me how to save the gameplay from the demos (using the Replay File Target script), but I am unsure how to then re-load the saved file back in, and to then play it. Can you please point me in the direction of which component or script I should check out?

    Thank you!
     
    Last edited: Mar 19, 2019
  40. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Hi,
    The ReplayFileTarget is also responsible for loading any replay file you need to access. You just need to provide the location of the replay file that you want to load to the ReplayFileTarget component and the rest should be taken care of. To specify the file location you can do something like this:

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UltimateReplay;
    4. using UltimateReplay.Storage;
    5.  
    6. class Example : MonoBehaviour
    7. {
    8.     void Start()
    9.     {
    10.         // Get the replay file target component
    11.         ReplayFileTarget fileTarget = ReplayManager.Target as ReplayFileTarget;
    12.        
    13.         // Specify the directory and file names
    14.         fileTarget.FileOutputDirectory = "C:/ReplayFiles";
    15.         fileTarget.FileOutputName = "ReplayIWantToLoad.replay";
    16.        
    17.         // You may need to enable 'overwriteexistingfiles' if it is not working for you otherwise a unique file name may be used with an additional number on the end
    18.         fileTarget.overwriteExistingFiles = true;
    19.        
    20.         // Start playback and the file should be loaded
    21.         ReplayManager.BeginPlayback(true);
    22.     }
    23. }
    I hope this helps you.
     
  41. avaiz

    avaiz

    Joined:
    Aug 13, 2018
    Posts:
    2
    Thank you! This is incredibly helpful.

     
  42. umair_hassan1991

    umair_hassan1991

    Joined:
    Jun 27, 2018
    Posts:
    18
    If we have a model let say a cricket batsmen, that has different meshes e.g. helmet, bat, gloves.
    EZReplayManager scale it to 1 in replay, also it turns on the disabled gameObject.
    so my question is, will ultimate workflow is same as EZ Replay Manager or its different...???
     
  43. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Hi,
    The scaling depends upon whether the game object is a prefab or not and you are also able to record scale so that if its value changes over time it will be replayed correctly. Here are some scenarios:
    1. A scene game object with 'record scale' disable will not have its scale modified at all
    2. A scene game object with 'record scale' enabled will have its scale value recorded at every snapshot and assigned during playback.
    3. A prefab instance created before you start recording with 'record scale' disabled with not have its scale modified at all
    4. A prefab instance created during recording with 'record scale' disabled will have its initial scale value recorded along with some other initial data when the prefab is instantiated.
    5. A prefab instance created during recording with 'record scale' enabled will have its initial scale recorded as in point 4 and will also have its scale value recorded every snapshot as in point 3.
    As for disabling game objects: A game objects enabled state is stored when the replay starts and then after the replay has finished the object will have its enabled state reset to the recorded value. Note that this only affects game object with replay components attached.

    I hope this helps you.
     
  44. Nate2033

    Nate2033

    Joined:
    Mar 15, 2019
    Posts:
    1
    What happened to the ReplayAnimator script? Will this be updated soon?
     
  45. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    We did not hear back about this issue so we assumed they had got it working. It should work as is with any Animator setup even though it is not fully released but if you are facing issues then please do report them so we can look into it.
     
  46. FMWFiremage

    FMWFiremage

    Joined:
    Mar 19, 2015
    Posts:
    6
    Hi there,

    I am noticing a slight stutter when playing back a replay in Ultimate Replay,
    Unity version 2018.3.12f1 - I have one character just walking around on a flat terrain, replay is recording to a file, with all other settings as default but at the point of processing a chunk I get a stutter on the character as though it's missing data, this happens all the way through the replay at the same interval, which can be changed when chunkSize is altered.
    I have tried increasing the chunkCacheSize but that makes no difference, the only way I've got a totally smooth replay was by increasing chunkSize so it covers the recording with one chunk, which isn't ideal.
    I'm not sure if it's losing a bit of data when saving or it's at the point of loading and playing the next chunk.

    Any ideas on this one?

    Thanks in Advance
    Martin
     
  47. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Hi,
    We will have to look into this further but it sounds like the chunks are not getting cached properly. If you enable debug messages in the inspector of the ReplayFileTarget do you get and messages or warnings related to missing chunks?
     
  48. umair_hassan1991

    umair_hassan1991

    Joined:
    Jun 27, 2018
    Posts:
    18
    how can i share these replays on social media?
     
  49. scottyboy805

    scottyboy805

    Joined:
    Apr 10, 2013
    Posts:
    1,193
    Hi,
    By sharing on social media do you mean sharing as an embedded video replay because this is not possible due to the fact that Ultimate Replay does not capture the screen or generate video files. You can share replay files by uploading them to a host of your choice and then having other users download these replay files to replay in game. If this is what you would like to do then I can give more detail if needed.
     
  50. FMWFiremage

    FMWFiremage

    Joined:
    Mar 19, 2015
    Posts:
    6
    Hi, thanks for the response.
    I can't see any warnings related to missing chunks, but I have just recorded a snapshot in Unity Editor of everything that is happening if this is of any help, you should notice the slight stutter at regular intervals in the play back.
    https://www.dropbox.com/s/hn0sc8nro...andalone_ _DX11_ 2019-04-25 14-16-57.mp4?dl=0