To those of you who want to make this code more performant here are a few things to keep in mind: - Because we are only storing simple values, consider using a struct instead of a class. - Because we are "overwriting" values consider using an array with a fixed length and then simply swapping in/out values when we reach the end of the array. Using generic lists is handy but will always be slower than an array with a given size. Also to those of you who suggest using a Stack. This makes perfect sense and was actually what I started by using. However we do need the ability to overwrite values and therefore we need to access items at the bottom of the list which a stack by definition won't do. Please do let me know if there is any way to make this code run faster using a stack vs. an array :) Hope you guys like the video! :)
I posted this as a comment already, but I will paste it here since you seem to be interested in it. Performance tweek: as some already mentioned lists are not optimized for this usecase (intel focuses on lists so it's still lightning fast, but there are better solutions) The thing that fits this purpose perfectly would be a LinkedList. It provides you with removeLast() and addFirst() out of the box and is sickengly optimized for manipulating the start and the end of the list. Love your videos. Thanks.
are C# lists actually ArrayLists? because when i see "list" i immediately think about LinkedList, which should be quite good for this case (because prepending is O(1), iirc). I guess using an Array would work too, but it feels more awkward to me(if we insert stuff at the start)
Also adding to the end of the lists instead of inserting into the front is way more efficient: stackoverflow.com/questions/18587267/does-list-insert-have-any-performance-penalty Thanks so much for this technique. Love your videos!
The best performance would be if you use a fixed length circular array (also called circular buffer) so you do not have to rearrange values just change the pointers (start and end) whenever you add or remove values
Then I run this at the end of StopRewind: void ReapplyForces() { rb.position = pointsInTime[0].position; rb.rotation = pointsInTime[0].rotation; rb.velocity = pointsInTime[0].velocity; rb.angularVelocity = pointsInTime[0].angularVelocity; }
In PointInTime class you can add information about velocity and angular momentum to set the same velocity as it was at each particular moment (to get rid of the problem when cube after rewinding just falling down)
One thing I'd like to point out is that when Brackey's mentions that with 50 cubes, you're already storing 2,500 values, its not actually that much. Each point in time is a Vector3 and a Quaternion. The Vector3 is 3 floats, the Quaternion is 4. Each float is 4 bytes. That means each point in time is (3+4)*4=28 bytes. Unity can easily handle tens of thousands of 28 byte classes. If we take the scenario here, we have 50 cubes and we are recording 50 times per second. In one second: 50*50*28 = 70,000 bytes 70,000 bytes = ~68 kilobytes. In comparison, a 512x512 png image is 1027 kilobytes. In this scenario, you're loading in a small png sprite every ~15 seconds. That's nothing, even on a mobile device. You probably aren't ever going to rewind longer than 10 seconds, so you're not even loading a full png image into RAM ever.
Ritoban Roy Chowdhury yeh the storing of the values isn't much space but having 50 objects that have a script on them with both update and fixed update is unnecessary. It's a good video for beginners to understand the concept but I certainly wouldn't implement it this way.
I would store all the data inside a manager script. Unity can easily manage tens of thousands of simple game objects with renderers (see: quill18creates Project Porcupine Episodes 1-3). However MonoBehaviours have quite a large overhead (moreso when compared to just a GameObject or Transform Reference), making is significantly less memory.
I was looking for a tutorial like this for years. The presentation from the Braid videogame developer was the only thing I could find, but this is way better and more detailed. Thank you so much.
Seeing this reminded me of a project we did initially in our degrees where we did the same thing however we saved X amount of vector 3 positions to demonstrate our understanding of lists & stacks
Very nice and simple tutorial, I can see a lot of new developers making great use of this. One tip for anyone who wants the replay to continue as normal, be sure to save the current velocity of the rigidbodies as well as their positions and rotations. This way when you finish your rewind the explosions will still happen as expected. You may also need to keep track of more advanced things like timed events that you trigger at certain times such as sphere casting for a grenade explosion, or variables that could be effected. For advanced objects you will probably want to make a subclass of the basic script that records this extra data.
I did this yesterday. Now I found this tutorial. Turns out our ideas were exactly the same, I implemented it the exact same way (except for not setting the Rigidbodies kinematic, and just disabling their gravity instead). I also added interpolation for high-refresh-rate monitors, but other than that, it's the same!
Suggestion for future tutorials: before the tutorial do a quick showcase of how you are going to achieve the effect. For example here before you start programming just say we are going to rewind time by storing the positions and rotations of each object at some time rate and then setting them back... You get the idea :D. Some people are just interested in HOW to do something and they can do the programming part themselves :)
Very nice tutorial. Just make sure not to use List in this case, since Insert needs to shift all elements internally [O(N)], while Add does not [O(1)]. For example, when I ran insertion of 100000 items into List, it took 2.1sec, while addition took 0.003sec! Easiest would be to use LinkedList, where both insertion and removal are O(1) operations. Cheers!
I don't doubt your research, and I'm new to C# myself, but some quick googling says that Linked Lists in C# are usually slower than Lists, except, sometimes, in the case of random insertions. stackoverflow.com/questions/5983059/why-is-a-linkedlist-generally-slower-than-a-list
In our specific case we just need 2 major operations in our list to make rewind works. 1. To make sure we record only 5 seconds we constantly need to remove the last item of the list. Both LinkedLists and Lists will have the same performance in this operation (maybe Lists are a little faster since LinkedLists also need to deal with memory management, but to be honest it's not too relevant). 2. We have to constantly record the movement and INSERT it at the BEGINNING of the list. Here's when Lists become expensive. Every time you insert at the beginning of the List it will shift every other item to adjust indexes. You basically saying "Hey List, put this guy at the beginning (first position number 0)" then the List will answer "Okay, but since we already have someone at this position we first need to move him to the next room and do this to every guy after him". The same applies when we turn on our rewind. We will be removing items from beginning and the List will need to adjust indexes again. This is when LinkedLists become useful. It does not have indexes, so if we need to insert at the beginning of the list it will simple create it in memory and will link it to the next item that was previously our first element. The point is: Inserting at the end both are similar but since we need to CONSTANTLY insert items at the beginning of our array, List will CONSTANTLY shift ALL items to adjust indexes. Imagine every frame we having to move every item in the list. Meanwhile with LinkedLists we will worry only with adding and removing. See this answer in the same topic that you shared: stackoverflow.com/a/5983207. My English isn't that good, sorry in advance. Hope this explains your question. If you want to lean more you can search "doubly linked lists data structure" and "big-o notation".
Stack won't work properly since we need to remove from the bottom of our list constantly to hold only 5 seconds of data. Stack is LIFO (last in, first out) which means that it can only remove data from the top of the list.
So weird you have a video on this. I was just thinking about how Braid might have done what they did with time rewinding and this understanding certainly helps!
A tip if you want to edit your private fields in the inspector without making them public, you should use the tag [SerializeField]. Making a field public *only to see it in Unity* is a shame that breaks the encapsulation offered by POO, and could lead to chaos later if your project becomes bigger. Have a nice day.
I am studying video game programming and your videos are my inspiration to not stop chasing my dream. A M A Z I N G BTW: I have completed succesfully your tutorials of tower defense and rpg!! Thanks you very much
First of all, I love your videos, thanks for all the stuff I've learned. You should consider using a Stack instead of a List. That structure is exactly for this kind of approach. Also, if you stop rewinding in the middle, and record additional positions, you will still be able to rewind to the beginning afterwards.
To keep the velocity of the cube: (This is from another one of my posts) *In the PointInTime class:* public Vector3 position; public Quaternion rotation; public Vector3 velocity; public Vector3 angularVelocity; public PointInTime(Vector3 _position, Quaternion _rotation, Vector3 _velocity, Vector3 _angularVelocity) { position = _position; rotation = _rotation; velocity = _velocity; angularVelocity = _angularVelocity; } *Then in StopRewind() just add the code* GetComponent().velocity = pointsInTime[0].velocity; GetComponent().angularVelocity = pointsInTime[0].angularVelocity; *Just make sure the last pointInTime isn't deleted* *I just did this* if(pointsInTime.Count > 1) { PointInTime pointInTime = pointsInTime[0]; transform.position = pointInTime.position; transform.rotation = pointInTime.rotation; pointsInTime.RemoveAt(0); } else { PointInTime pointInTime = pointsInTime[0]; transform.position = pointInTime.position; transform.rotation = pointInTime.rotation; } *Also gives the nice effect of when you've completed rewinding, it just freezes time until you let go of the return key.*
Hello, i do what u wrote but its not working ;// . pointsInTime.Insert(0, new PointInTime(transform.position, transform.rotation) ); i Have problem here, please help!
To add on to this, I'd add a curve value that allows it to slow down to a halt, and then move slowly back, but at the value of the curve, ie, moving back faster the longer it's being done.
Really well made video, thanks for the hard work; It makes it all the more worth being a patreon. I would love to see a tutorial like this on how animations work, it could possibly fit together with your blender series!
Performance tweek: as some already mentioned lists are not optimized for this usecase (intel focuses on lists so it's still lightning fast, but there are better solutions) The thing that fits this purpose perfectly would be a LinkedList. It provides you with removeLast() and addFirst() out of the box and is sickengly optimized for manipulating the start and the end of the list. Love your videos. Thanks.
@Brackeys - Also have to record physics' velocities (both positional and rotational) and add them back to the objects after disabling kinematics flag, so that objects obey inertia after going out of the rewind (unless that sudden "stop in midair" at 13:10 is intended behavior =P). Other than that, perfect tutorial! =)
Maybe you could store position and rotation every 0.1 sec, then use translate to move object to that position. You save lot of intermediate positions and rotations. Also, as etaxi341 said, you could store the rigidbody velocity so it keeps moving in the same direction when you release the rewind button.
Very cool video, i personally don't use unity (i prefer ue4) but the logic you went through was well explained and can be applied to any game engine. For those that are using physics objects you may also want to track the objects velocity. This way objects will continue along there path instead of just dropping when you stop rewinding time.
I believe that PointInTime should be a struct, not a class. That way it can be copied around and you won't have to worry about load from GCing them later.
I haven't tried this, but in C#7, you can use tuples to create a list that holds both positions and rotations. So it may not be necessary to create a class to hold those two data types.
You should've store the latest position as the last element of the list. Add(...) has amortized O(1) time complexity, while Insert(0, ...) has O(n). It might have an impact on performance if you store a lot of positions for a lot of objects.
Haha I know why you’re here. Don’t worry, it’s first thing I searched after finding the theme. (Brackeys Game Jam 2020.2 for those who are a little slow)
A circular buffer is a lot faster and as you setup a maximum amount of time that can be recorded anyway you can initialize a circular buffer right at the start. From that moment you just overwrite values in this buffer. You need two indexes to know at which location in the buffer you are. Often called 'head' and 'tail'. In this particular case you can use only a 'tail' which points to the last index in the buffer that you wrote a value to. Next index is just increasing 'tail' and MOD it with the length of the buffer. This will make it wrap around. For playback you save the value of the 'tail' and decrease the 'tail' and wrap around to the end when 0 is reached and stop when the saved value is equal to the tail. This has only the overhead of a 'tail' index. Also there is no memory management needed as the circular buffer will be allocated only one time and is not changing in size. Fastest way to do it.
Yòu mentioned you want to put the last location of the object at the beginning because it's a stack. In computer science the stack data structure works by adding to the end of the list and removing the last element instead of the first
Thanks for your tutorial because since 2 years , I search a video of this type. And You should make a series of tutorial about controll time , like a " bubble of time where there are another timeline in the area
I'd recommend using rb.position if we alredy have a Rigidbody Component attached to the object the rewind script is attached to, as this will smooth out the movement even more, especially if the player himself is rewinding. It can cause some shaking back and forth with the camera following the player if Transform.position is used. I hope this will help some of you as this was a problem that took me some time to fix
@@PDCMYTC yes, I was able to get that to work. However, you may need to block the Input made by the player because it can override the position, better said the velocity of your character. If that happens, the player starts behaving very weirdly. You could post a part of your script in here if that doesn't help
To all the jammers coming here for the Brackeys jam: 4:41 - Please don't do it like Brackeys! Inserting/Removing to/from index 0 forces forces all other items in the list to be relocated Every Single Time! Better insert/remove to/from the end, although it will also cause occasional relocations (but much rarer). Even better to create an array of fixed size and two integers to keep track of the stack begin and end (no relocations ever). Between, My team is doing a 3D RPG game and we are still looking for extra: - Unity dev - 2D artist PM me in Discord (@Igor Konyakhin)
i rewrote this into king crimsons ability. i made it so it removes the players colliders and makes all enemies go and attack your last positions when activated. it then records all of their movements and attacks, after that it resets them back to the position they were at when the recording started and plays all of their movements. i also rewrote that, and turned into his skip time ability by just removing the record and play functions.
Another optimization you could do is limit the resolution of the savings. Then as you play back LERP between the positions to get smooth transitions between what essentially becomes keyframes.
the data structure that stores both position and rotation is called a transform. If that doesnt exist in unity, it's at least a good name for the script imo.
Thanks for the tutorial! However, using Insert repeatedly is not very optimal. It has to move all of the elements, so just a simple Add() at the end of the list is the best way to do this.
Is it possible to give 2 thumbs up??? One question: in the Update method, we're checking if the Return key is pressed as well as not pressed every frame. Wouldn't it be better only to check if it's not pressed if isRewind is true? Or would it not save any processing?
Thank you :) Saw a long and briefly confusing talk by Jonathan Blow how he did his rewind with Braid and he's doing pretty much the same but collecting less frames and then averaging positions from point A B in order to get a higher rewind time. At the least from what I could gather from that talk. As I said, it was a bit confusing :)
Almost done with the game for the Game jam rewind 2020 everyone looking at this video for help 👌😂 good choice can't wait to see other Gamer games good luck everyone
Inserting at position 0 will cause memory copying every time you insert at position 0. I would suggest simply adding via Add() function and then enumerating in reverse order. PointInTime should be a struct as well for additional performance gains. A LinkedList would be even better as you can remove the head and add to the tail basically for free. Finally, if you did something like storing only the changes in position and add a timestamp, you could rewind but not remove the entry in pointsInTime until you go beyond it's timestamp, similar to delta encoding.
I would only save data when the object is moving, checking it's velocity, and I would save the instant, so that you only save the moments it's moved, and then I'd jump from instant to instant using coroutines in order to avoid active wait. I think Brackeys' implementation needs a los of optimization. It can work as an starting point.
Everyone participating in Brackeys Game Jam 2020: OH YES, REWIND TIME
Hahaha, true!
i was going to type this comment
Yeah lol
true lol
It's a MUST to start brainstorming
To those of you who want to make this code more performant here are a few things to keep in mind:
- Because we are only storing simple values, consider using a struct instead of a class.
- Because we are "overwriting" values consider using an array with a fixed length and then simply swapping in/out values when we reach the end of the array.
Using generic lists is handy but will always be slower than an array with a given size.
Also to those of you who suggest using a Stack. This makes perfect sense and was actually what I started by using. However we do need the ability to overwrite values and therefore we need to access items at the bottom of the list which a stack by definition won't do. Please do let me know if there is any way to make this code run faster using a stack vs. an array :)
Hope you guys like the video! :)
I posted this as a comment already, but I will paste it here since you seem to be interested in it.
Performance tweek: as some already mentioned lists are not optimized for this usecase (intel focuses on lists so it's still lightning fast, but there are better solutions)
The thing that fits this purpose perfectly would be a LinkedList. It provides you with removeLast() and addFirst() out of the box and is sickengly optimized for manipulating the start and the end of the list.
Love your videos. Thanks.
are C# lists actually ArrayLists? because when i see "list" i immediately think about LinkedList, which should be quite good for this case (because prepending is O(1), iirc). I guess using an Array would work too, but it feels more awkward to me(if we insert stuff at the start)
Also adding to the end of the lists instead of inserting into the front is way more efficient: stackoverflow.com/questions/18587267/does-list-insert-have-any-performance-penalty
Thanks so much for this technique. Love your videos!
Could also only record every 2 or more fixedtimestep steps and lerp between each point if you really needed optimization.
The best performance would be if you use a fixed length circular array (also called circular buffer) so you do not have to rearrange values just change the pointers (start and end) whenever you add or remove values
you shuld store the rigidbody velocity too so it flys in the same direction again after the rewind
Great idea! I was just getting ready to ask about how to make the objects continue flying after the rewind.
You would only need to store this once, and change it after each new point.
don't forget about angular velocity too ;)
I've gone with:
above the TimeBody class
public struct PointInTime
{
public readonly Vector3 position;
public readonly Quaternion rotation;
public readonly Vector3 velocity;
public readonly Vector3 angularVelocity;
public PointInTime(Transform t, Vector3 v, Vector3 aV)
{
position = t.position;
rotation = t.rotation;
velocity = v;
angularVelocity = aV;
}
}
Then I run this at the end of StopRewind:
void ReapplyForces() {
rb.position = pointsInTime[0].position;
rb.rotation = pointsInTime[0].rotation;
rb.velocity = pointsInTime[0].velocity;
rb.angularVelocity = pointsInTime[0].angularVelocity;
}
Brackeys: Makes a game jam with the topic rewind
Me: (uses their own tutorial on it)
*STONKS*
...
In PointInTime class you can add information about velocity and angular momentum to set the same velocity as it was at each particular moment (to get rid of the problem when cube after rewinding just falling down)
This would be a fantastic addition to this.
How does one add the velocity and angular momentum? I mean what are the parameters? Just rigidbody.velocity? And rigidbody.angle?
Sometimes my boxes still falling down after rewind ( what could cause this problem?
@@silent1um706 is your gravity still on? On the rigidity. That would cause it fall when you are done rewinding
Привет) ты бы мог сделать игру с регрессией времени) было бы любопытно.
Who is here for the Brackeys Jam Theme : Rewind and getting help like i am!!
Gotta use what resources you have access to
I thought I am first from the game jam 😂😂
@@GwydionV trueeee
Brackeys: Makes a game jam with the topic rewind
Me: (uses their own tutorial on it)
*STONKS*
i'm 14 and this is deep, they literally made the theme about a brackeys video that already exists
One thing I'd like to point out is that when Brackey's mentions that with 50 cubes, you're already storing 2,500 values, its not actually that much.
Each point in time is a Vector3 and a Quaternion. The Vector3 is 3 floats, the Quaternion is 4. Each float is 4 bytes.
That means each point in time is (3+4)*4=28 bytes. Unity can easily handle tens of thousands of 28 byte classes.
If we take the scenario here, we have 50 cubes and we are recording 50 times per second. In one second:
50*50*28 = 70,000 bytes
70,000 bytes = ~68 kilobytes.
In comparison, a 512x512 png image is 1027 kilobytes.
In this scenario, you're loading in a small png sprite every ~15 seconds. That's nothing, even on a mobile device. You probably aren't ever going to rewind longer than 10 seconds, so you're not even loading a full png image into RAM ever.
Ritoban Roy Chowdhury SMART
Ritoban Roy Chowdhury yeh the storing of the values isn't much space but having 50 objects that have a script on them with both update and fixed update is unnecessary. It's a good video for beginners to understand the concept but I certainly wouldn't implement it this way.
What would you do instead? Store all the data inside one manager script?
La Flama Blanca there isn't another way to do it. that would be better necessarily
I would store all the data inside a manager script. Unity can easily manage tens of thousands of simple game objects with renderers (see: quill18creates Project Porcupine Episodes 1-3). However MonoBehaviours have quite a large overhead (moreso when compared to just a GameObject or Transform Reference), making is significantly less memory.
I'm dead fucking serious I searched for this yesterday and nothing came up. Thank you sooo much for reading my mind
there is a wonderful talk by jonathan blow on his game braid and his implementation of a rewind mechanic. He recorded up to a half hour of game
Brackeys game jam 4 goes brrrrrrr
Thats why im here 🤣
@@TheOneTaboo same
He brother....big fan shoutout??
I was looking for a tutorial like this for years. The presentation from the Braid videogame developer was the only thing I could find, but this is way better and more detailed. Thank you so much.
Brackeys theme : rewind
Me : ok, let's watch this again
Exactly bro
This is a GREAT mechanic for a video game. In fact, I know a few video games that do this.
Seeing this reminded me of a project we did initially in our degrees where we did the same thing however we saved X amount of vector 3 positions to demonstrate our understanding of lists & stacks
Couldn't wait for this video to come out after it was leaked a while ago
Here during Brackeys Jam 2020.2
well... The program doesnt work for me, did it work for you?
Any got ideas to spare for the game jam
Very nice and simple tutorial, I can see a lot of new developers making great use of this. One tip for anyone who wants the replay to continue as normal, be sure to save the current velocity of the rigidbodies as well as their positions and rotations. This way when you finish your rewind the explosions will still happen as expected. You may also need to keep track of more advanced things like timed events that you trigger at certain times such as sphere casting for a grenade explosion, or variables that could be effected. For advanced objects you will probably want to make a subclass of the basic script that records this extra data.
I did this yesterday. Now I found this tutorial. Turns out our ideas were exactly the same, I implemented it the exact same way (except for not setting the Rigidbodies kinematic, and just disabling their gravity instead). I also added interpolation for high-refresh-rate monitors, but other than that, it's the same!
Suggestion for future tutorials: before the tutorial do a quick showcase of how you are going to achieve the effect. For example here before you start programming just say we are going to rewind time by storing the positions and rotations of each object at some time rate and then setting them back... You get the idea :D. Some people are just interested in HOW to do something and they can do the programming part themselves :)
EXACTLY!!! haha
@@dmd356 Nice fake reply.
@@super8bitable what?
i wonder how many view this video will get after the 2020 summer jam ends x')
Very nice tutorial. Just make sure not to use List in this case, since Insert needs to shift all elements internally [O(N)], while Add does not [O(1)]. For example, when I ran insertion of 100000 items into List, it took 2.1sec, while addition took 0.003sec! Easiest would be to use LinkedList, where both insertion and removal are O(1) operations. Cheers!
Thanks for pointing this out. In the end performance matters and using Queue or LinkedList could make this code a lot more efficient.
I don't doubt your research, and I'm new to C# myself, but some quick googling says that Linked Lists in C# are usually slower than Lists, except, sometimes, in the case of random insertions.
stackoverflow.com/questions/5983059/why-is-a-linkedlist-generally-slower-than-a-list
In our specific case we just need 2 major operations in our list to make rewind works.
1. To make sure we record only 5 seconds we constantly need to remove the last item of the list. Both LinkedLists and Lists will have the same performance in this operation (maybe Lists are a little faster since LinkedLists also need to deal with memory management, but to be honest it's not too relevant).
2. We have to constantly record the movement and INSERT it at the BEGINNING of the list. Here's when Lists become expensive. Every time you insert at the beginning of the List it will shift every other item to adjust indexes.
You basically saying "Hey List, put this guy at the beginning (first position number 0)" then the List will answer "Okay, but since we already have someone at this position we first need to move him to the next room and do this to every guy after him".
The same applies when we turn on our rewind. We will be removing items from beginning and the List will need to adjust indexes again.
This is when LinkedLists become useful. It does not have indexes, so if we need to insert at the beginning of the list it will simple create it in memory and will link it to the next item that was previously our first element.
The point is: Inserting at the end both are similar but since we need to CONSTANTLY insert items at the beginning of our array, List will CONSTANTLY shift ALL items to adjust indexes. Imagine every frame we having to move every item in the list. Meanwhile with LinkedLists we will worry only with adding and removing.
See this answer in the same topic that you shared: stackoverflow.com/a/5983207.
My English isn't that good, sorry in advance. Hope this explains your question. If you want to lean more you can search "doubly linked lists data structure" and "big-o notation".
...Or if you wanted to have a more abstracted representation, I'd have used a Stack.
Stack won't work properly since we need to remove from the bottom of our list constantly to hold only 5 seconds of data. Stack is LIFO (last in, first out) which means that it can only remove data from the top of the list.
Imagine if Brackeys Jam had the rewind theme just so he could increase the view count in this video e.e
have you got a game idea? for the jam
Youssef Kassem I know a friend on yt called Part-time toaster, he really smart and makes devlog, he Will probably make a devlog out of the jam
So weird you have a video on this. I was just thinking about how Braid might have done what they did with time rewinding and this understanding certainly helps!
A tip if you want to edit your private fields in the inspector without making them public, you should use the tag [SerializeField]. Making a field public *only to see it in Unity* is a shame that breaks the encapsulation offered by POO, and could lead to chaos later if your project becomes bigger. Have a nice day.
I am studying video game programming and your videos are my inspiration to not stop chasing my dream.
A M A Z I N G
BTW: I have completed succesfully your tutorials of tower defense and rpg!! Thanks you very much
First of all, I love your videos, thanks for all the stuff I've learned. You should consider using a Stack instead of a List. That structure is exactly for this kind of approach. Also, if you stop rewinding in the middle, and record additional positions, you will still be able to rewind to the beginning afterwards.
Anyone watching this cuz of Brackeys Jam 2020.2 :) ?
Do we need to use unity3d 2020.2 version for this?
BTW im planning to join this game jam thats why im asking :)
Hey I am from Future,
I came here to tell you that your tutorial will help in your forth game jam themed Reverse
Thank you
hi, future, when is the deadline for this?
@@CreatePlayGames 8 August 3:30 PM according to Indian Standred Time
@@sajrapraveen4963 tnx, hows ur progress?
To keep the velocity of the cube:
(This is from another one of my posts)
*In the PointInTime class:*
public Vector3 position;
public Quaternion rotation;
public Vector3 velocity;
public Vector3 angularVelocity;
public PointInTime(Vector3 _position, Quaternion _rotation, Vector3 _velocity, Vector3 _angularVelocity) {
position = _position;
rotation = _rotation;
velocity = _velocity;
angularVelocity = _angularVelocity;
}
*Then in StopRewind() just add the code*
GetComponent().velocity = pointsInTime[0].velocity;
GetComponent().angularVelocity = pointsInTime[0].angularVelocity;
*Just make sure the last pointInTime isn't deleted*
*I just did this*
if(pointsInTime.Count > 1) {
PointInTime pointInTime = pointsInTime[0];
transform.position = pointInTime.position;
transform.rotation = pointInTime.rotation;
pointsInTime.RemoveAt(0);
} else {
PointInTime pointInTime = pointsInTime[0];
transform.position = pointInTime.position;
transform.rotation = pointInTime.rotation;
}
*Also gives the nice effect of when you've completed rewinding, it just freezes time until you let go of the return key.*
What about animations? Can I implement this on characters too?
I'm not sure... There might be a way.
Hello, i do what u wrote but its not working ;// . pointsInTime.Insert(0, new PointInTime(transform.position, transform.rotation) ); i Have problem here, please help!
When you said C sharp I just though of instruments and my instrument and I almost snapped into position....
SET
To add on to this, I'd add a curve value that allows it to slow down to a halt, and then move slowly back, but at the value of the curve, ie, moving back faster the longer it's being done.
Really well made video, thanks for the hard work; It makes it all the more worth being a patreon.
I would love to see a tutorial like this on how animations work, it could possibly fit together with your blender series!
Rasmus Tollund I would love to see this video. Nice to see other patreons as well ^.^
We all know why you're here
Game jam lol
ItsMeDash2 agreed
Can someone please tell me if the empty Game object named Cubes has some components to it?
I'm actually here cause I find it interesting. The coding seems easy but i'm too lazy to do anything in Unity.. Or even install Unity.
More like, UA-cam is now recommending this cause of all you lot going here to watch it for the gamejam...
Me when GameJam
Lmao, now everybody will be here cause of the jam :D
In addition to the low performance due to the use of List.Insert(0,*), your solution is dependent on FPS to work correctly.
Performance tweek: as some already mentioned lists are not optimized for this usecase (intel focuses on lists so it's still lightning fast, but there are better solutions)
The thing that fits this purpose perfectly would be a LinkedList. It provides you with removeLast() and addFirst() out of the box and is sickengly optimized for manipulating the start and the end of the list.
Love your videos. Thanks.
Wow, when I saw that rewind uoısoןdxǝ, what's in my mind it's like a, sort of, Prince of Persia game!
@Brackeys - Also have to record physics' velocities (both positional and rotational) and add them back to the objects after disabling kinematics flag, so that objects obey inertia after going out of the rewind (unless that sudden "stop in midair" at 13:10 is intended behavior =P).
Other than that, perfect tutorial! =)
Maybe you could store position and rotation every 0.1 sec, then use translate to move object to that position. You save lot of intermediate positions and rotations. Also, as etaxi341 said, you could store the rigidbody velocity so it keeps moving in the same direction when you release the rewind button.
Long time viewer Brackeys, love your vids. You explain things so perfectly. Definitely the best out there.
came here for the jam
Perfect! Now I’ll know how to rewind in the game Jam!
another excellent video from Brackeys. I don't understand why some people downvoted again! (oh well, internet)
I'm learning unity engine and your channel is helping a lot. Also. Blackthornprod's channel is very helpful as well
Hi, fellow Game Jammer :)
Very cool video, i personally don't use unity (i prefer ue4) but the logic you went through was well explained and can be applied to any game engine.
For those that are using physics objects you may also want to track the objects velocity. This way objects will continue along there path instead of just dropping when you stop rewinding time.
I believe that PointInTime should be a struct, not a class. That way it can be copied around and you won't have to worry about load from GCing them later.
anyone here from the brackeys 2020 game jam ?
Hello
That was quick
yes xD
dude...
ahaha yes
The way he explains why he does everything is so helpful and especially since it doesn't slow the video down so it'd fast but still followable
The *PointInTime* class should be a struct. I totally would have used it as a class too, but I just realized that a struct is a perfect fit for that.
Nice, now I can finally get a headstart in my Clock Blockers based game
I haven't tried this, but in C#7, you can use tuples to create a list that holds both positions and rotations. So it may not be necessary to create a class to hold those two data types.
You should've store the latest position as the last element of the list. Add(...) has amortized O(1) time complexity, while Insert(0, ...) has O(n). It might have an impact on performance if you store a lot of positions for a lot of objects.
Haha I know why you’re here. Don’t worry, it’s first thing I searched after finding the theme.
(Brackeys Game Jam 2020.2 for those who are a little slow)
So that show to rewind time, THAT'S SO COOL
A circular buffer is a lot faster and as you setup a maximum amount of time that can be recorded anyway you can initialize a circular buffer right at the start. From that moment you just overwrite values in this buffer. You need two indexes to know at which location in the buffer you are. Often called 'head' and 'tail'. In this particular case you can use only a 'tail' which points to the last index in the buffer that you wrote a value to. Next index is just increasing 'tail' and MOD it with the length of the buffer. This will make it wrap around. For playback you save the value of the 'tail' and decrease the 'tail' and wrap around to the end when 0 is reached and stop when the saved value is equal to the tail. This has only the overhead of a 'tail' index. Also there is no memory management needed as the circular buffer will be allocated only one time and is not changing in size. Fastest way to do it.
I didn't understood the full code, but I saw it worked!
This is killer queen's third ability: bites the dust!!!!
Finally the actual tutorial about making a replay system.
Yòu mentioned you want to put the last location of the object at the beginning because it's a stack. In computer science the stack data structure works by adding to the end of the list and removing the last element instead of the first
Thanks for your tutorial because since 2 years , I search a video of this type. And You should make a series of tutorial about controll time , like a " bubble of time where there are another timeline in the area
I was wondering how to do this, thank you very much!
You are the best person on the internet.
I'd recommend using rb.position if we alredy have a Rigidbody Component attached to the object the rewind script is attached to, as this will smooth out the movement even more, especially if the player himself is rewinding. It can cause some shaking back and forth with the camera following the player if Transform.position is used.
I hope this will help some of you as this was a problem that took me some time to fix
Were you able to rewind the player itself too? All of my other objects are able to rewind, except the player, please help me ..
@@PDCMYTC yes, I was able to get that to work. However, you may need to block the Input made by the player because it can override the position, better said the velocity of your character. If that happens, the player starts behaving very weirdly. You could post a part of your script in here if that doesn't help
Yaaaaa it’s rewind time!
Umair Khalid that’s hot, that’s hot.
Y'know, if I could control Rewind, I would want: Fortnite and *_MARK ASS BROWNIE._*
@@swiwiws125 !emit dniwer s'ti aaaaaY
To all the jammers coming here for the Brackeys jam:
4:41 - Please don't do it like Brackeys!
Inserting/Removing to/from index 0 forces forces all other items in the list to be relocated Every Single Time!
Better insert/remove to/from the end, although it will also cause occasional relocations (but much rarer).
Even better to create an array of fixed size and two integers to keep track of the stack begin and end (no relocations ever).
Between,
My team is doing a 3D RPG game and we are still looking for extra:
- Unity dev
- 2D artist
PM me in Discord (@Igor Konyakhin)
i rewrote this into king crimsons ability. i made it so it removes the players colliders and makes all enemies go and attack your last positions when activated. it then records all of their movements and attacks, after that it resets them back to the position they were at when the recording started and plays all of their movements. i also rewrote that, and turned into his skip time ability by just removing the record and play functions.
Another optimization you could do is limit the resolution of the savings. Then as you play back LERP between the positions to get smooth transitions between what essentially becomes keyframes.
I have a better method.
CALL WILL SMITH
"Yaaaaaaahh."
That's hot!
EMIT DNIWER STI
@@jackat4 ETINTROF
WillSmith();
Wow, you made that look very easy. Love it.
-Sees this tutorial
-opens Unity
*IT'S REWIND TIME!*
the data structure that stores both position and rotation is called a transform. If that doesnt exist in unity, it's at least a good name for the script imo.
Already looking forward to add this to my project
That intro had me hooked
Thanks for the tutorial! However, using Insert repeatedly is not very optimal. It has to move all of the elements, so just a simple Add() at the end of the list is the best way to do this.
Whos here for the jam??
me
me!
i am
almost everyone in it. i am in it too.
I love Brackeys' tutorials
@Brackeys
you said a swear 13:47
haha made my day
Brackeys...you are the best
Can't wait to try it out with the new ECS system.
Cheers love, the cavalry is here
Is it possible to give 2 thumbs up??? One question: in the Update method, we're checking if the Return key is pressed as well as not pressed every frame. Wouldn't it be better only to check if it's not pressed if isRewind is true? Or would it not save any processing?
Thank you :)
Saw a long and briefly confusing talk by Jonathan Blow how he did his rewind with Braid and he's doing pretty much the same but collecting less frames and then averaging positions from point A B in order to get a higher rewind time. At the least from what I could gather from that talk. As I said, it was a bit confusing :)
Almost done with the game for the Game jam rewind 2020 everyone looking at this video for help 👌😂 good choice can't wait to see other Gamer games good luck everyone
A game that tries to push forward and make a rewind of 1minute will be god tier
This will be so useful in racing games! Thanks for the tutorial man!
Damn, this is amazing, could convert this to 2D and had some cool results
Well this video is about to blow up
Yeah Lol
You and your patreons are awsome :)
Yaaa
Its Rewind time
I feel like you should also store the velocity so that when the rewinding ends you can give it the velocity at that point in time.
Brackeys you are such a pro. I love your videos. I'm learning so much!!!!
+1 for the rename shortcut
Cool stuff! Great Tutorial! ❤️
Inserting at position 0 will cause memory copying every time you insert at position 0. I would suggest simply adding via Add() function and then enumerating in reverse order. PointInTime should be a struct as well for additional performance gains. A LinkedList would be even better as you can remove the head and add to the tail basically for free. Finally, if you did something like storing only the changes in position and add a timestamp, you could rewind but not remove the entry in pointsInTime until you go beyond it's timestamp, similar to delta encoding.
6:27 - learn usage of guard-clauses. Check if the amount is zero, then set the status that we are no longer in rewind and return.
GAME JAM
I would only save data when the object is moving, checking it's velocity, and I would save the instant, so that you only save the moments it's moved, and then I'd jump from instant to instant using coroutines in order to avoid active wait.
I think Brackeys' implementation needs a los of optimization. It can work as an starting point.
Wow! Very well explained. Thanks.
Really inspiring video. Plenty of stuff to do with this, thanks!