Time travel in Unity | Useless Game Dev

  Рет қаралды 24,131

Useless Game Dev

Useless Game Dev

4 ай бұрын

New video out, it's about time. Time rewind more specifically, like you find in Legend of Zelda: Tears of the Kingdom, or Braid, or many other cool games that utilize this concept as a game mechanic or as part of their story. Let's see if we can make it work.
This video took two years to make. It was supposed to be the very first video on the channel but I could never get the tone and angle right. Recently I reopened this Unity project and thought there still was something to make out of this.
More reading/watching on this subject:
- Check out Shadwen by Frozenbyte it's a great game store.steampowered.com/app/42...
- More on dichotomic search en.wikipedia.org/wiki/Dichoto...
Music:
"Wholesome" Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 4.0 License
creativecommons.org/licenses/b...
"Vibing Over Venus" Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 4.0 License
creativecommons.org/licenses/b...
"Beauty Flow" Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 4.0 License
creativecommons.org/licenses/b...
Other Credits:
- Character model: assetstore.unity.com/packages...
- Mine Environment: assetstore.unity.com/packages...
- Crates: assetstore.unity.com/packages...
- Trees: assetstore.unity.com/packages...
- More environment: assetstore.unity.com/packages...
Support the channel on Patreon to get extra content, and access to the source code of all projects!
/ uselessgamedev
Follow me on Twitter, not that I tweet that often: / uselessgamedev
Kappa Avatar art by Boskoop www.artstation.com/boskoop
And as always, have a good one!
#Unity #GameDev #IndieDev #devlog

Пікірлер: 100
@jorgellorca7232
@jorgellorca7232 4 ай бұрын
I worked in a game for a game jam last week and one of the mechanics was time travel. We implemented a much easier system, as we only had to modify time in one scene with a set length of one minute, and we just used timeline for doing it. But watching your video has motivated me to take this approach in future projects that have this kind of mechanics. Also, as an engineer, I really enjoyed the optimization you did on the recorder algorithm. Congrats!
@uselessgamedev
@uselessgamedev 4 ай бұрын
Interesting, how did it work with timelines exactly, I assume you wouldn't have any dynamic object then? Just a "film" on the timeline that you can rewind/fast-forward? Interesting nonetheless for a narrative game!
@jorgellorca7232
@jorgellorca7232 4 ай бұрын
@@uselessgamedev exactly! it was a pre-designed scene using the timeline, there were few elements that moved through time, such as npcs, and you could control the quantity of time that had passed via script (miles away of the system you've developed, but an easier approach for such a time limited event as a game jam is, nonetheless)
@MattyDoesGameDev
@MattyDoesGameDev 4 ай бұрын
I'm imagining how exponentially more complicated this can get when you include objects that change states, for example a barrel that explodes! You'd have to design each gameplay object to have time-travel in mind. Thanks for the fun video, as always!
@RGHdrizzle
@RGHdrizzle 4 ай бұрын
I was just planning to implement a time rewind system as a small project and here u go with an upload exactly about that which also gave me some insights on how to do it
@uselessgamedev
@uselessgamedev 4 ай бұрын
As other have suggested, if you don't need arbitrary access, consider using a linked list!
@notTryio
@notTryio 4 ай бұрын
Mayonnaise on an escalator
@sushismitcher225
@sushismitcher225 4 ай бұрын
That's a really interesting and valuable remark on this subject. It made me think of a whole new facet of time travel in video games. Thank you so much. You have changed my life
@Youtubershacksshifat
@Youtubershacksshifat 4 ай бұрын
yay
@wittymchitty
@wittymchitty 4 ай бұрын
Going upstairs, so see you later
@sushismitcher225
@sushismitcher225 4 ай бұрын
be careful not to slip on the mayo@@wittymchitty
@Rknife
@Rknife 4 ай бұрын
​@@wittymchittyrotalacsananoesiannoyam
@alex2416
@alex2416 4 ай бұрын
i always wondered how they made this work on a Switch for TOTK. Thanks for exploring this question, great video ! :D
@alejo460
@alejo460 4 ай бұрын
Useless game dev but every single video is the most useful and talented game dev video possible
@REVYMofficial
@REVYMofficial 4 ай бұрын
Your design also shows the potential for in-game replays. Great Work!😄
@williefr
@williefr 4 ай бұрын
What an awesome video! I love when we can optimize stuff. I also learned a time ago these algorithms and just forgot them as I'm not using them at work. Also very well explained and a lot of work put in. New subscriber 😊
@AdamMelvins
@AdamMelvins 4 ай бұрын
Awesome video! :) I'd like to note that your "simple scene" is utterly beautiful! :) I love that art style, the distance fade - it looks gorgeous and makes for a really nice video to watch. :) I've been working on a "replay system" for work, and thankfully it's not got anything with physics, just player positions, the task/quest, and what's on the UI and the positions/visibility of objects. Even that... took a LOT of work :) You're a wizardly wizard, and that's a really clean implementation! Thank you for making something near and dear. :)
@xeetsh
@xeetsh 4 ай бұрын
Great video! Especially great that you showed how to optimize this. Most tutorials are lacking this step. I made a game jam game once that used basically the same technique, not for rewinding, but for playing certain actions back. Your goal was to catapult a car to a certain point with conveyors, explosives and oil spills by placing those objects, seeing where the car would land, resetting the car and continuing to build your track from there. As Unity physics are far from deterministic, I needed a way to record what happened before the reset in order for the car to land where it landed last time. Not the best thing to have to deal with during a three-day game jam, but I managed to do it basically exactly how you managed to record and rewind movement of game objects in your video (just way less performant)! The game is Wildlife Camping Experiences (on itch) if anyone is interested :)
@uselessgamedev
@uselessgamedev 4 ай бұрын
That sounds cool! I guess this is more akin to a save system to be able to restore the game at checkpoints but that's interesting nonetheless!
@TobyJWalter
@TobyJWalter 4 ай бұрын
I love your videos, keep up the amazing work! ♥
@BobsMudHut
@BobsMudHut 4 ай бұрын
Great video! I've been working on a game with time manipulation for over a year and still use lists to store all my rapidly changing data (like transform positions) because I haven't gotten around to optimizing everything yet 😅. For things that don't change very often or on many objects like changing animations or interactions between timed objects I use a linked list that records forwards/backwards functions with any needed state parameters to save on space. Writing the functions can be a little complicated sometimes but it means I can record any arbitrary action I want and it doesn't have to be dependent on constantly storing/checking state every frame like when recording transform positions. When things get really fun (read: complicated) is when you have different objects rewinding/replaying at different speeds/directions in time.
@uselessgamedev
@uselessgamedev 4 ай бұрын
Linked lists are a great solution indeed!
@stickguy9109
@stickguy9109 4 ай бұрын
This video instantly reminded me of you
@draakevil
@draakevil 4 ай бұрын
Awesome video. Very easy to understand the concept.
@PingsGolf
@PingsGolf 4 ай бұрын
This was a nice video. Well made and interesting topic Also i like the music👍
@designator7402
@designator7402 4 ай бұрын
A little while ago I saw a video about cursed units of measurement, and I think I will add "Frames per Frame" to my own personal list of cursed measurements. Thanks, funny blue turtle friend.
@KansasToYou
@KansasToYou 4 ай бұрын
Awesome video man. Great work
@DrEnzyme
@DrEnzyme 4 ай бұрын
It's amazing how easily you can get cool mechanics out of a system like this when you have the base implementation. You can have objects that are immune to time travel just by deleting their recorders. The objects that lose momentum when they're unfrozen in time can be a game mechanic. You can have multiple copies of your player that appear every time loop, leading to fun interactions and paradoxes. Time travel is sick.
@ibrabdo
@ibrabdo 4 ай бұрын
Great video :D Interesting idea, i'll try to apply the same concept in unreal engine for my game 😅
@Queue_Bert
@Queue_Bert 4 ай бұрын
My Team is actually working on a 3D puzzle platformer about time travel right now! I'm the animator, not the lead programmer, but from what I can tell your implementation of rewinding and recording is similar to ours but a fair bit more complex and versatile. We're mainly recording the player's position rotation velocity, and then playing it back as "clones" the player can then interact with. For at least my animations I wish we could go back and rework the systems to take advantage of some of the ideas you brought up in your video here, but I'm not sure how possible it's gonna be this late in development 🫠. Either way, this was a great video as always, keep up the amazing work!!
@uselessgamedev
@uselessgamedev 4 ай бұрын
Ah, good luck! Looking forward to a new time travel game!
@slice6298
@slice6298 4 ай бұрын
Great idea, yet relatively simple to implement Really shows that you don't need to be incredible at programming to be able to create games with fun systems
@omnisel
@omnisel 4 ай бұрын
Binary searches are so cool !!! Always the best way to search an arbitrarily large ordered list.
@PantheraLeo04
@PantheraLeo04 4 ай бұрын
You may have mentioned this and I just missed it, but if you're already recording velocity each frame, wouldn't recording position be redundant. If you have a starting position (the moment you begin rewinding time) and the velocity at each point in time, I'm pretty sure you should be able to just derive the position from those right? And the same would be true of the rotation and angular velocity.
@uselessgamedev
@uselessgamedev 4 ай бұрын
Interesting, I guess that's true. It would probably be a good way to cut down on RAM usage, but if that's not an issue I think my One-Recorder-for-one-Component architecture is pretty clean and readable (I like that everything is separate). I don't know if calculating velocity ourselves yields results as good as reading them directly from the rb (although maybe deriving it from the difference in position is exactly what they do under the hood idk). But great suggestion!
@vonbaldric9767
@vonbaldric9767 4 ай бұрын
Unity physics probably aren't determistic enough to use dead-reckoning exclusively like that. If you scrubbed back far enough you would probably not arrive at the same positions that you originally passed through. You could do a hybrid system with 'key-frames' like how video compression works. You might be able to do the opposite and derive velocity from delta_position, though I can definitely imagine this resulting in velocity getting 'eaten' if you scrub back to just the wrong frame.
@morgan0
@morgan0 3 ай бұрын
i think you could get a further speed increase by storing all parameters for one object in one frame, since most of the time they’d all change together. it might also be worth looking back at the last second recorded, and replacing some frames with a smaller amount of interpolation frames, which could store less data and be simpler to lookup.
@amarok8bit
@amarok8bit 4 ай бұрын
What a great video! I also like games with time manipulation like Braid. Last year I wrote my game Time Wizard for Atari 8-bit computer. You can see it on my channel. I fully understand problems related to memory management for storing and restoring gameplay. In my case I had 48kB of RAM and CPU with 1.77 MHz. So this is completely different environment but the same challenges. Thanks for sharing. :)
@DarthBiomech
@DarthBiomech 4 ай бұрын
I had an idea about space strategy game that takes light speed into account and forces the player to play _predictively,_ so this will definitely be useful, thanks!
@jjrubes1880
@jjrubes1880 4 ай бұрын
How did you implement binary search? I can't think of how it would be possible if you've got gaps in the list without some indication of the nearest change
@uselessgamedev
@uselessgamedev 4 ай бұрын
You search the list of *keys*, that's what you can split in half, en check if the median key is higher or below your desired frame, then you split again and again and again. The list of keys has continuous indices of course. I hope this makes sense
@jjrubes1880
@jjrubes1880 4 ай бұрын
That makes sense, but there isn't much point it being a hashmap any more as you could get the same look up efficiency with an array of (time, position) pairs. But, seeing as most of the time you're only looking 1 or 2 frames forwards or backwards it would make more sense just to store the index instead of recalculating it each frame, getting that sweet O(1).
@beidero
@beidero 4 ай бұрын
I do wonder if a linked list would be a good method for storing the recorded frames, since you would always start at the last frame and jump backwards. Search would always be 0(log(n)) but you get to skip the conversion to array or list which would save quite some time on larger lists. You could even do like a secondary list that stores references to every 100 recorded frames or so, so you can skip backwards quickly if needed and then just start your search from the closest frame to the frame you are looking for.
@uselessgamedev
@uselessgamedev 4 ай бұрын
Yes that would very much work! Unless we want to do big jumps and skip to arbitrary frames IDs, but with my setup we could only rewind/fast-forward, which even when rewinding at a higher speed (>=2 Game frame per Unity frame) the linked list would work very well. That's probably even faster than my solution T_T good job!
@beidero
@beidero 4 ай бұрын
@@uselessgamedev I added something in an edit, storing a second list with refs to every 100/1000 frames would allow for fast big jumps. would be even faster than 0(log(n)). Could also keep a ref to last looked up frame since next lookup is likely next to it
@slice6298
@slice6298 4 ай бұрын
Binary search on linked list wouldn't be log(n), it would be n*log(n), naive would be just n, but it's definitely a lot better idea if you just want a simple time rewind
@beidero
@beidero 4 ай бұрын
​@@slice6298 You are indeed right, I was typing up my reply a bit quickly as I had to run earlier. But the more I think about this problem the more interesting it is. I think I would still go for the linked list approach as it has several benefits. But the optimizations you could make are just super fun to consider, if you like a programming challenge that is. You could do a lot of lookup optimzations, but also when you consider that not every frame is recorded it becomes more difficult to look up neighbours since an object might not have moved for a long time so how would you optimize for it. I guess you could optimize for worst case scenario but is there a way to do better? For a real game you would need a time cutoff point so maybe you say we record the last 30 seconds. In which case a linked list is fantastic, specially if you do a lookup table of 1 second interval (so I guess lookup table distance is your FPS). You can just cap the lookup table at 30 items and if it ever is above it you just cut off the linked list. So very easy memory management.
@slice6298
@slice6298 4 ай бұрын
@@beidero I've thought of something, you could have an array that wraps around and a value pointing to the beginning. That should be both memory and time performant, although I think time shouldn't be an issue, especially since the game probably wouldn't need to and maybe even shouldn't do part of the updates. Also the frames solutions is prone to instable framerate, so having set time different from the game frame is good idea
@MarekNijaki
@MarekNijaki 4 ай бұрын
Great video
@JamesTM
@JamesTM 4 ай бұрын
Really cool video, as always! Very interesting. One possible, significant optimization you could consider: For the rewind/ff, I dont think you need to access an arbitrary frame. You only need to acces the frame either immediately before or immediately after the "current" (last recorded/restored) frame. Or at least within a very small number of frames, if you're skipping some. But never thousands of records backwards in the list. So, perhaps a doubly-linked list would be the most efficient data structure. That way, you can scrub backwards and forewards through the list without worrying about arbitrary lookups. In theory, that would also make deleting "future" frames as easy as deleting the forward link from the current frame record. (I say "in theory" because I don't know anything about Unity's garbage collection.)
@uselessgamedev
@uselessgamedev 4 ай бұрын
That's correct, others have suggested linked lists as well, which is definitely something I should have thought about! I need to try to find an excuse as to why we would need to access any arbitrary frame at any point then
@JamesTM
@JamesTM 4 ай бұрын
@@uselessgamedev One possible excuse would be loading an arbitrary point in time. But you could probably resolve that easily enough with some kind of intermittent bookmark. Maybe a lookup table that provides a link to every 1000th frame? (Though, the garbage collection gets harder again if you do that.) Still a stretch, though, since taking a few seconds to find all the correct frames to restore isn't unreasonable for a quick-load, in the way it would be for rewind (which needs to complete with enough time left to render the frame.)
@steluste
@steluste 4 ай бұрын
What's the name of your visual studio theme?
@uselessgamedev
@uselessgamedev 4 ай бұрын
This is a Sublime Text theme called Mariana that I modified, I don't remember exactly what I did other than changing the background color. I do use Visual Studio when working but record the text snippets for the video in Sublime Text
@newe6000
@newe6000 4 ай бұрын
I'm surprised nobody has mentioned how this approach naively assumes that a frame is a fixed length of time, instead of being dynamic. To be fair it works much better than I expected in this prototype, but once you put this in a more complicated scene with framerate fluctuations they're going to start showing up in the recordings. A more ideal solution I think would record at a fixed framerate (maybe every second fixed update?) then interpolate between those frames on playback. Though I understand avoiding that complexity in a first draft prototype.
@DarthBiomech
@DarthBiomech 4 ай бұрын
One question though, why is your solution was generic instead of a simple inheritance? at least in the video, I don't think any of the script examples seem to utilize .
@uselessgamedev
@uselessgamedev 4 ай бұрын
Eh it appears you're right. I think it used to be that recorders would return their T components at some point but right now there isn't any use of T in the code. At least it's there for the future I guess
@bungercolumbus
@bungercolumbus 4 ай бұрын
Wouldn't time.timescale = 0 be a better approach for pausing the game?
@uselessgamedev
@uselessgamedev 4 ай бұрын
Timescale is generally not a good approach for pausing a game. It works in some cases and is super easy to use so it's fine but more complex projects will want another system for pausing. Regardless, in this case it wouldn't allow for rewinding/fast-forwarding so I use the playbackcontroller anyway
@mmertduman
@mmertduman 2 ай бұрын
You could also set a fixed time delta for when you record the state! Most of your values are interpolable, and given a small enough time delta, a linear interpolation wouldn't look bad. So instead of saving 60 states per second (or whatever your fps is), just save 5 or 10 and lerp between them.
@uselessgamedev
@uselessgamedev 2 ай бұрын
That's a good point!
@Conman9310
@Conman9310 3 ай бұрын
couldnt you store the frame number when adding to the record and just use that number to find the most recently added one instead of a search
@uselessgamedev
@uselessgamedev 3 ай бұрын
Do you mean storing the (frame, data) tuple as a struct in a list? because then iterating through the list would be O(n). Or you could still implement a binary search too, in a way both this and a dictionary store the frame id somewhere. Maybe the ultimate data structure is the KeyedCollection, it's the best of both worlds, I should use it more often.
@anonymous49125
@anonymous49125 4 ай бұрын
converting the keys to array or list also means you're allocating a new array each time you do that... which your GC is going to have a field day and will likely cause a huge lag hickup every 1 second.... with thousands of objects doing this every frame --- wew lad... I'm glad with the bst optimization you were able to cut the times down - and if it works, it works - but the approach I would use is having all the recorders be subscribers, recording not to a dictionary but a fixed size array (a list that keeps keyframe for each rendered frame forever is too wild for my blood), and then going back in time, just ask each subscriber if they have something to do for the current frame (a rolling index to keep track of what your current saved keyframe is); if they have nothing going on and are just waiting around then they can check if the last recorded key (that current frame index again) matches the current time or not, if it does, then go back one more keyframe in the array and figure out the distance of time between the current and the previous keyframe - and use that for interpolating between those two keyframes- then when asked if they are busy or not next time (which they are busy interpolating), then interpolate between those two keyframes instead. If you ask them to do something and they don't have anything for this frame and they are not interpolating between frames, then do nothing. this takes your O(log n) - where N are all your keyframes in the tree for that object and brings it down to O(1), just asking "do something for this frame" to each object. I think the benefits are not just performance (where there assuredly would be - doing a bst per frame per object is bananas - and in any case O(log n)>O(1)) but more important, readability. Mucking about with dictionary is nice for that key lookup of O(1) but any type of figuring out where the last keyframe is, makes it a less than optimal strategy.
@uselessgamedev
@uselessgamedev 4 ай бұрын
About GC, what I ended up doing was caching the array keys, and flagging it as dirty when recording a new frame. This way it is rebuilt exactly once per playback session. I agree it's still a lot but that's better than allocating it for every lookup
@jokesterthemighty227
@jokesterthemighty227 4 ай бұрын
That's not how it's typically done, see with rollback network solution you already store a lot of previous frames so it's trivial to make them play backwards (in an offline setting)
@zaj007
@zaj007 4 ай бұрын
How's your indie game coming along?
@uselessgamedev
@uselessgamedev 4 ай бұрын
It's doing all right except we will soon run into the first obstacle of "we can't keep going until we get funding" and so far all our funding efforts have been in vain. So we will probably have to put the project on the back burner and take care of pre-production ourselves, and go back to seeking (less) funding when our prototype is more convincing
@oglothenerd
@oglothenerd 2 ай бұрын
Have you considered switching to Godot?
@uselessgamedev
@uselessgamedev 2 ай бұрын
I have considered learning Godot to add it to the technologies I know. It will take a while for me to reach full proficiency though
@oglothenerd
@oglothenerd 2 ай бұрын
@@uselessgamedev Does the Unity drama bother you at all?
@omayoperations8423
@omayoperations8423 4 ай бұрын
There's a great VR game that does something like this. It's called "The Last Clockwinder"
@uselessgamedev
@uselessgamedev 4 ай бұрын
That was really fun to play there aren't many good VR games but I loved this one. And I love rube Goldberg machines which another topic we can explore in the future ;)
@ladiesman2048
@ladiesman2048 4 ай бұрын
Iron Danger is a Unity game that has time rewind mechanic
@uselessgamedev
@uselessgamedev 4 ай бұрын
Thanks I'll check it out!
@ladiesman2048
@ladiesman2048 4 ай бұрын
Looks like it's -90% at Steam this week so now is a perfect time to check it out :)
@edwardperkins1225
@edwardperkins1225 4 ай бұрын
I wonder if Nintendo will actual try suing people for time rewind mechanics in non-Nintendo games considering the US patient office was stupid enough to give them one for it in TotK. Maybe it only counts if you use a cursor to select what to rewind.
@uselessgamedev
@uselessgamedev 4 ай бұрын
I don't know, they've been known to not sue people for these weird all-encompassing patents. It seems they do it more as a way to protect themselves in case someone tries to patent this and then comes after them. The "link is on an vehicle that moves therefore link moves" one is especially crazy.
@AxeLea3
@AxeLea3 4 ай бұрын
An interesting game that is centered around time travel is Prince of Persia: the sands of time Everything is time travel in that game. Story, movement and combat all implement it
@uselessgamedev
@uselessgamedev 4 ай бұрын
Yeah I really liked that game when I was younger!
@0hellow797
@0hellow797 4 ай бұрын
How impossible would it be to implement a replay system that can fit on any game. I don’t want to think about it anyway lmao
@reguret2976
@reguret2976 4 ай бұрын
boids!
@dantelaviero7782
@dantelaviero7782 4 ай бұрын
he coded a vegan robot
@awesomemike3857
@awesomemike3857 4 ай бұрын
Why do all of this when you can just set Time.timescale = -1 lol
@s1lkysl1m83
@s1lkysl1m83 4 ай бұрын
unity, LOL!
@Draugo
@Draugo 4 ай бұрын
Personally instead of gaps I would have recorded changed values as a new index and then had a frame array where each frame has the value of the current latest recorded values. Sure you're adding an int to the recorded data but you don't need to deal with gaps and the correct data is easily findable with frame index. For purge you could remake the frame array until the desired spot and then discard all frame data that is higher than the stored index of the last frame.
Just Boids | Useless Game Dev
12:10
Useless Game Dev
Рет қаралды 55 М.
Crushing Minesweeper | Game Dev's Revenge
9:46
Useless Game Dev
Рет қаралды 28 М.
格斗裁判暴力执法!#fighting #shorts
00:15
武林之巅
Рет қаралды 85 МЛН
СҰЛТАН СҮЛЕЙМАНДАР | bayGUYS
24:46
bayGUYS
Рет қаралды 738 М.
What Jumping Spiders Teach Us About Color
32:37
Veritasium
Рет қаралды 1,2 МЛН
5D Diplomacy With Multiverse Time Travel
16:44
Oliver Lugg
Рет қаралды 276 М.
A better way to do time travel
3:51
Goddamn Superhero
Рет қаралды 1,3 М.
A Simple Puzzle Game With Multiverse Time Travel - Induction!
17:51
I tried to break a World Record and failed miserably | Useless Game Dev
11:21
What I Learned After 10 Years of Game Development!
6:01
Johnny Ostad
Рет қаралды 9 М.
A Weird VR Musical Instrument | Useless Game Dev
8:43
Useless Game Dev
Рет қаралды 16 М.
How Games Make VFX (Demonstrated in Godot 4)
5:46
PlayWithFurcifer
Рет қаралды 297 М.
I Made A Difficult Game About Climbing
15:04
Pontypants
Рет қаралды 1,9 МЛН
XL-Power Best For Audio Call 📞 Mobile 📱
0:42
Tech Official
Рет қаралды 773 М.
😱НОУТБУК СОСЕДКИ😱
0:30
OMG DEN
Рет қаралды 2,4 МЛН
iPhone 12 socket cleaning #fixit
0:30
Tamar DB (mt)
Рет қаралды 2 МЛН
Xiaomi Note 13 Pro по безумной цене в России
0:43
Простые Технологии
Рет қаралды 1,9 МЛН