jarcane / minicosm

A FP-oriented game engine inspired by universe.rkt, written in Clojurescript
24 stars 2 forks source link

Change time value passed to :on-tick #9

Open jarcane opened 4 years ago

jarcane commented 4 years ago

At present, the time argument to the :on-tick handler is a DOMHighResTimeStamp, indicating the number of ms since time origin (https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#The_time_origin).

It's been suggested, and indeed so far in experience, that this is a less than useful value.

One proposal instead has been to return the delta between the current time stamp, and that of the previous frame. This would enable the user to time the distance between frames and this value could be more useful for some purposes.

Another option that occurred to me during development of the demo is to provide either in its place or as an additional argument, the count of the current frame since initialization. The demo as it is counts frames manually in a few places for different mechanics, and it might've been less fiddly to implement if the game provided the frame value already. That said, this might not be that useful and I dunno if overflow would be something to worry about here.

Feedback appreciated.

CaptainLexington commented 4 years ago

I read in Game Engine Architecture that it's useful for a game engine to provide both HighRes time and a less granular "game time" that can be easily manipulated. Additionally, I think the number of frames is extremely useful because it's a timestamp that's guaranteed to get you every value - if you try to schedule an event at 2500 ms, you will probably miss it, but if you schedule an event for the 150th frame, you will definitely hit it. I'm not sure that's the best way to schedule events, but it seems to work for smaller projects.

And delta seems pretty useful too. Probably I would pass a map to on-tick like this:

{
  :real-time <high res timestamp>
  :game-time <???>
  :delta <high res (or maybe game-time?) delta since last frame>
  :ticks <total number of calls to on-tick>
}

I think the idea is that real-time is useful for making performance tweaks and game-time is useful for driving the simulation. In the book I mentioned above, he suggests 1/300 second to be the base unit of game time because it fits easily into 30 and 60 fps.

One thing I like about this approach is it makes it easy to add other fields if they become valuable, without breaking the API. I've heard it's useful to keep an average of deltas, but the local average of, say, the last five or ten frames, might be very different than the lifetime average - but both might be useful! With a map of time stats, we can just throw in whatever we want. It may also be useful to give the target and true fps, although the latter can be calculated from the delta (or some average).

jarcane commented 4 years ago

Additionally, I think the number of frames is extremely useful because it's a timestamp that's guaranteed to get you every value - if you try to schedule an event at 2500 ms, you will probably miss it, but if you schedule an event for the 150th frame, you will definitely hit it.

This is exactly why the demo does it the way it does. I was previously trying to schedule against time stamps or time intervals but it was just too unpredictable, so I wound up kludging together some frame counting tricks instead.

I do like this idea of a map of useful time values. I'm not really sure what game-time would be either other than frames, at least for now, and being able to manipulate it would require a more complex on-tick handler so I think that would have to be thought out as to how that API would work. Otherwise the other three values make good sense to keep around for sure.

This is gonna be breaking change, the demos will have to be updated, so I'll add this to a milestone for 0.2.0.