I didn't really need to know how to make this kind of stuff but I enjoyed watching the video so much and it was so well put together I might add something like that to my game :D Thank you so much, amazing work.
What about using Animation Curves i.e. each snapshot where data changes, a new key frame is created. Once a recording has completed, you can then optimise the animation curve to only contain the key frames you need to maintain the same animation curve so playback still works.
For the playback, you also have to disable/ignore user input right (just so a player can't tinker with it)? I guess maybe that's what you do when you press watch replay, the gameplaycontroller turns into 'replay mode' and a user can no longer control it.
Daaaamn!! Super complicated and useful feature made accessible to us plebs. Appreciate it!! Just to review at a high level: 1) SnapshotInfo stores specific info of each snapshottable object 2) SnapshotData stores list of all SnapshotInfos 3) ReplayContainer stores all SnapshotDatas. But why did you make this an SO? Thank you again!!! Wishlisted CoC
I'm thinking he went with an SO as the container as a way for the serialized data to be persisted between (editor) play sessions and so it can be viewable in the inspector. It's a good way to do it too since it's all built-in to the engine.
Yep, exactly! As I mentioned in the voiceover it's basically just dealer's choice, I like it because I can see it independently in the project so it's useful for me for debugging (and for the video) How did you wishlist it? It doesn't have a steam page yet. Are you from the future?
@@GameDevGuide Thanks for the answer! Yeah I commented and then saw it has no Steam page. I will WL when it does. I've learned a lot from your videos over the years.
What I instantly thought of was to save less frequently and fill in some of the frames by looking at the previous and next saved frames. This may look weird in some cases, but if you save frequently enough I think it can cut the save data size and the required processing power in half.
Interresting choice, I would rather capture an initial state and record all inputs as event metrics as well as a used seed for randomization. Then recreate the initial state of captured scene and playback (simulate) all recorded input (events).
you could get extra performance by not using guids for object IDs, and instead only relying on ints,; his can be easily achieved by creating a singleton which issues these IDs - it's not the biggest optimization, but can rack up some frames when you record a large number of objects
It's probably correct in your impl, but in the video i think LoadNextSnapshot was wrong, at least from the code you show here, it basically gets stuck in a loop at snapshot zero, since you have "m_hasNextSnasphot = replayContainer.GetSnapshot(snapshotIndex)" (the same index we just loaded), i think that should be snapshotIndex+1 (peek ahead 1 snapshot)
We want to make sure that the physics objects continue moving correctly between frames we've captured. If we just record the position and rotation alone, it could end up with the object behaving incorrectly.
Giving that frame data for each object is a struct and mostly information are just prymitives, it realy asks for some parallelization using jobs and burst
It's a little bit more work, but I'm actually using this for handling traffic that I instantiate at runtime. As long as you give your objects a unique id, you can use a manager to handle instantiating, and assigning the IDs in your saved replay to a fresh instance upon load.
it has been a while but the tutorials/topics covered here are always so high quality!
Well said!
Cool stuff. Aiming for replays can be a good way to ensure the game-play-system is well handled and cleanly implemented.
Braid (Anniversary edition is gorgeous) is an incredibly sophisticated rewind system (the game is based around that) one of the best system I ever saw
I didn't really need to know how to make this kind of stuff but I enjoyed watching the video so much and it was so well put together I might add something like that to my game :D
Thank you so much, amazing work.
What about using Animation Curves i.e. each snapshot where data changes, a new key frame is created. Once a recording has completed, you can then optimise the animation curve to only contain the key frames you need to maintain the same animation curve so playback still works.
For the playback, you also have to disable/ignore user input right (just so a player can't tinker with it)? I guess maybe that's what you do when you press watch replay, the gameplaycontroller turns into 'replay mode' and a user can no longer control it.
Daaaamn!! Super complicated and useful feature made accessible to us plebs. Appreciate it!! Just to review at a high level:
1) SnapshotInfo stores specific info of each snapshottable object
2) SnapshotData stores list of all SnapshotInfos
3) ReplayContainer stores all SnapshotDatas. But why did you make this an SO?
Thank you again!!! Wishlisted CoC
I'm thinking he went with an SO as the container as a way for the serialized data to be persisted between (editor) play sessions and so it can be viewable in the inspector. It's a good way to do it too since it's all built-in to the engine.
Yep, exactly! As I mentioned in the voiceover it's basically just dealer's choice, I like it because I can see it independently in the project so it's useful for me for debugging (and for the video)
How did you wishlist it? It doesn't have a steam page yet. Are you from the future?
@@GameDevGuide Thanks for the answer! Yeah I commented and then saw it has no Steam page. I will WL when it does. I've learned a lot from your videos over the years.
What I instantly thought of was to save less frequently and fill in some of the frames by looking at the previous and next saved frames. This may look weird in some cases, but if you save frequently enough I think it can cut the save data size and the required processing power in half.
Very cool!
Interresting choice, I would rather capture an initial state and record all inputs as event metrics as well as a used seed for randomization. Then recreate the initial state of captured scene and playback (simulate) all recorded input (events).
to get the same behaviour every time the game engine physics would need to be deterministic though and I don't think Unities physics engine is
insane
you could get extra performance by not using guids for object IDs, and instead only relying on ints,; his can be easily achieved by creating a singleton which issues these IDs - it's not the biggest optimization, but can rack up some frames when you record a large number of objects
It's probably correct in your impl, but in the video i think LoadNextSnapshot was wrong, at least from the code you show here, it basically gets stuck in a loop at snapshot zero, since you have "m_hasNextSnasphot = replayContainer.GetSnapshot(snapshotIndex)" (the same index we just loaded), i think that should be snapshotIndex+1 (peek ahead 1 snapshot)
why would you need to record velocity and angular velocity? Shouldn't position and rotation suffice?
We want to make sure that the physics objects continue moving correctly between frames we've captured. If we just record the position and rotation alone, it could end up with the object behaving incorrectly.
@ So the physics kind of act as interpolation to the incomplete set of snapshots?
@@expired___milk yes, just like you would do for multiplayer games as well
I imagine this could be adjusted to add a "sands of time" function to the game for users to correct their mistakes?
Giving that frame data for each object is a struct and mostly information are just prymitives, it realy asks for some parallelization using jobs and burst
I've been looking for something like this for the longest time. I had a solution but the algorithm to find the snapshot was too slow.
gotchu man i think i was close tho
I wonder how this would work for instantiated objects
It's a little bit more work, but I'm actually using this for handling traffic that I instantiate at runtime.
As long as you give your objects a unique id, you can use a manager to handle instantiating, and assigning the IDs in your saved replay to a fresh instance upon load.
Someone mention riot games