So.. I’ve been thinking *alot* about how to best design Braid if I wanted to have a proper time manipulation feature in the game that would ressemble Braid, but still avoid using up MBs of RAM per second… I think I’ve got something, and I’ll try to explain it!
First: do not store the position of every object in the world at every timestamp, instead, only store the events, and let the objects interpolate on the event’s timestamp, their current timestamp/state to know what their new status should be.
I also realized today that I do not need to keep every object of the world in memory, even after they disappear.. all I need to do is make the object’s creation/destruction events get registered in the ‘timeline’ and let the rewind/fast forward feature recreate objects as needed. I should probably have a unique name generated per object in order to know which event goes to what object (instead of linking the event to the object’s address).
I also realized that I’d need multiple timelines, dependent on each other.. so for example, we could have the “world” timeline for the world (for time independent objects), then the “current” timeline that could get rewinded/forwarded (for normal objects), we could also have a “player timeline” that would have its timestamp generated depending on the player’s position in the world (for Braid’s world 4), and a “shadow” timeline that would be used by ‘shadow’ objects for world 5, also probably a ‘timewarp’ timeline to emulate the time-warp feature of the ring, so it could translate the timestamp of other timelines depending on the object’s position relative to the ring..
These timelines would be linked to each other.. For example, imagine a time dependent key in world 4. The world would request the key to update its state (position/broken or not/being held or not, etc..) to the new timestamp X on timeline “world”, this will cause it to ask the “current” timeline for the ‘current’ timestamp at time X, which would translate to timestamp Y (depending on whether or not you rewinded or not), this would cause the timeline to ask the “player” timeline for the new timestamp associated with timestamp Y (in other words, where was the player located at timestamp Y), which would give us a timestamp of Z, now that’s where the key should be.. so the key tries to go into its new state at timestamp Z, it realizes that it was ‘hooked’ (being held) to another object “abc” (player or monster) at that timestamp, so it asks that object (the world actually, since the object could have been destroyed) to get the status of object “abc” at timestamp Z, which could again chain up multiple timestamps (depending on whether it was the player, a time independent monster, or a normal monster) until it gets the state of the object at that timestamp, at that point, it will be able to know the position of that object, and set its own position to the same position as that object (with an offset depending also on the direction of the object)!
Ouff, now that sounded complicated! And we didn’t even start adding a ‘maybe there was a time warping ring in the path of that object between now and timestamp Z..’ Either way it seems to fit so far, holding only events, destroying unneeded objects, and chaining timelines together with each timeline holding its own objects/events, seems to make sense so far.. we do get the correct behavior apparently, and with minimal memory usage…
Now I still have to get my brain into a stable enough state to figure out questions like “what happens if the user gets inside a time-independent platform?” or “what happens when you need to figure out the position of an object when a ring might be in the way?” or “how to interpolate correctly our new state when the requested timestamp is not the old timestamp +/- 1 time unit?”
I’ll start designing some uml diagrams soon to try and make it fit, then I’ll start coding the basic structure… Assuming I don’t see any flaws in the design…
If you have comments about this design, feel free to share! 🙂
See you soon!