Creating an attract mode demo
by Peter Dwyer · in Torque Game Builder · 07/19/2005 (6:23 am) · 13 replies
Melv or anyone else out there.
I want to create an attract mode demo that shows the user a few levels with some basic crappy (gotta get that old arcade feel) player getting killed long before they show all the level secrest and shortcuts etc.
My question is simply. How do I go about recording one of my gameplay sessions so that it can be played back when the program is idle for a while.
I want to create an attract mode demo that shows the user a few levels with some basic crappy (gotta get that old arcade feel) player getting killed long before they show all the level secrest and shortcuts etc.
My question is simply. How do I go about recording one of my gameplay sessions so that it can be played back when the program is idle for a while.
#2
Hey Melv any chance you can give this thread some lovin' and tell me if T2D supports the journal. I don't want to go that route if T2D turns out not to be supported.
I guess I should also ask how the journal works, though I'll also do a resource search on that.
07/19/2005 (2:35 pm)
Er thanks Ricky...i think...Hey Melv any chance you can give this thread some lovin' and tell me if T2D supports the journal. I don't want to go that route if T2D turns out not to be supported.
I guess I should also ask how the journal works, though I'll also do a resource search on that.
#3
I suspect that I will need to write a trigger class that invokes the journal only when I hit a key or something and stops it in the same manner. I should then be able to load up these journal files and play them back at will.
So why do I get the feeling it's not that easy.......
07/24/2005 (5:27 am)
Ok for anyone who is even remotely interested. The journal seems to be command line based and records all actions from the moment the player starts up your game (if it is switched on). So although it will record a demo, the demo will include everything from the player clicking menu buttons to him typing something into a console window. Not sure how you then get just the gameplay bit out of the journal and into your own game.I suspect that I will need to write a trigger class that invokes the journal only when I hit a key or something and stops it in the same manner. I should then be able to load up these journal files and play them back at will.
So why do I get the feeling it's not that easy.......
#4
07/24/2005 (7:02 am)
A quick a rough way would be to script a sequence of movement actions.. but yes.. that wouldn't relaly be a proper demo:)
#5
It wouldn't be that hard to create your own recording system, though, if you really need it. Edo Broekman did it for his game, Eggstatic.
07/24/2005 (8:01 am)
I used it for a while for debugging, but the problem is that you can't replay a journal from an arbitrary point. In other words, if you want to play a journal it takes over the whole game until exit. :( But also it doesn't record things like results of calls to GetRandom(), so any non-deterministic features will not play back the same way.It wouldn't be that hard to create your own recording system, though, if you really need it. Edo Broekman did it for his game, Eggstatic.
#6
The whole system must be deterministic. So you must set the random seed to be the same.. this makes it so that GetRandom() will return the same sequence of numbers every time. Then you have to record all the inputs at the times that they occur (which I guess is what this journal thing does?).
You also have to make it so the each frame is the same as before. On most games that you see an attract mode, the framerate is constant (they lock it at a certain rate) so this is easy to do. I'm not sure if it is possible to do in T2D.
I suppose if you can't figure out a way to make the game determinisic you could also do it by saving every state of every object every frame.. but that would only work for a small game because it would use so much resources.
edit: Oh ya, you could also write an AI player.. depending on the game this might be the easiest way.
07/24/2005 (12:58 pm)
I'm not sure about Torque2D specifics but basically this is how you do it:The whole system must be deterministic. So you must set the random seed to be the same.. this makes it so that GetRandom() will return the same sequence of numbers every time. Then you have to record all the inputs at the times that they occur (which I guess is what this journal thing does?).
You also have to make it so the each frame is the same as before. On most games that you see an attract mode, the framerate is constant (they lock it at a certain rate) so this is easy to do. I'm not sure if it is possible to do in T2D.
I suppose if you can't figure out a way to make the game determinisic you could also do it by saving every state of every object every frame.. but that would only work for a small game because it would use so much resources.
edit: Oh ya, you could also write an AI player.. depending on the game this might be the easiest way.
#7
07/24/2005 (5:38 pm)
Thanks guys. I'm going to look at exactly how the journal does things and then hopefully adapt it into a console object that can be triggered through script. Failing that I'll opt to have an ai player do the moves. The doem does not really need to be the same each time as it runs for a maimum of about 30 seconds. I don't mind cutting off mid move as it's a demo and shouldn't really be showing too much.
#8
Replay is super simple; Set random seed, sync internal game clock, and then just start the game normally, hammering out user-controlled events as recorded in the demo file.
I believe the journaling system in Torque works by recording every event that comes through the main loop. I've never inspected the code, but I would have assumed that things such as GetRandom() seeds would be a part of this... Interesting that they are not.
This is different than just normal Torque (TGE) demo recording, which captures the client network stream and plays it back. There isn't a need to record random seeds. In the case of demos, you don't care about GetRandom() because you're just slamming objects around based purely upon ingame events, the sequence of which is set by the server and then sent to the clients. Client-side stuff isn't all sync'd anyway. Who cares if your mini-cannon shell casings bounce the same way, every time? If something needed to be sync'd on the client, the server would say something about it and that'd be your event logged int the recording.
But, well, as pointed out, journals are recorded from start to finish, unlike demos.
07/25/2005 (2:07 am)
I recently implemented a demo record system in Adagio, which had to be flawlessly robust as it is going to be used for proof of high score records and so on. As James stated, your game world needs to be entirely deterministic, so that a starting seed can be used to recreate the entire gameplay from there. After that, all you need to do is just record player-controlled triggered events with sufficient resolution. You can do deltas to keep the spam to a minimum. With compression, my demo files are usually less than 2k in size, and that's for a few minutes worth of play.Replay is super simple; Set random seed, sync internal game clock, and then just start the game normally, hammering out user-controlled events as recorded in the demo file.
I believe the journaling system in Torque works by recording every event that comes through the main loop. I've never inspected the code, but I would have assumed that things such as GetRandom() seeds would be a part of this... Interesting that they are not.
This is different than just normal Torque (TGE) demo recording, which captures the client network stream and plays it back. There isn't a need to record random seeds. In the case of demos, you don't care about GetRandom() because you're just slamming objects around based purely upon ingame events, the sequence of which is set by the server and then sent to the clients. Client-side stuff isn't all sync'd anyway. Who cares if your mini-cannon shell casings bounce the same way, every time? If something needed to be sync'd on the client, the server would say something about it and that'd be your event logged int the recording.
But, well, as pointed out, journals are recorded from start to finish, unlike demos.
#9
Would you mind explaining your demo system in a little more detail. I'm not sure how a seed can be used to determine where enemies are and where the player starts in the demo etc. I have set-up random dungeons from seeds before, and this allows you to create exactly the same dungeon time and again but, the dungeon had no dynamic content and the player was added later.
Surely you can't re-create what the player is doing from the seed alone?
If I understand you correctly. Upon starting the recording, I would need to first create a seed by logging the exact time and create a seed by reading the positions of all objects on the screen. From then on I would record the players movements and keystrokes. All random numbers should be the same as they are based on the curent time.
When I want to re-play the demo/attract mode, I sync the game time with the time in the demo and then fire off the player movement events and let the game take care of re-creating the other gameplay events. Each object should appear as it did when the demo is recorded because the times are the same and so the random numbers generated will be the same.
Is this right?
07/25/2005 (3:56 am)
@DavidWould you mind explaining your demo system in a little more detail. I'm not sure how a seed can be used to determine where enemies are and where the player starts in the demo etc. I have set-up random dungeons from seeds before, and this allows you to create exactly the same dungeon time and again but, the dungeon had no dynamic content and the player was added later.
Surely you can't re-create what the player is doing from the seed alone?
If I understand you correctly. Upon starting the recording, I would need to first create a seed by logging the exact time and create a seed by reading the positions of all objects on the screen. From then on I would record the players movements and keystrokes. All random numbers should be the same as they are based on the curent time.
When I want to re-play the demo/attract mode, I sync the game time with the time in the demo and then fire off the player movement events and let the game take care of re-creating the other gameplay events. Each object should appear as it did when the demo is recorded because the times are the same and so the random numbers generated will be the same.
Is this right?
#10
However, I see no reason why you couldn't do this with randomly-placed items, too. You said yourself that you've done random dungeons from seeds; It's the same concept. Maybe I am not getting what you mean by dynamic content?
You can ignore what I said about ingame time -- If your ingame events aren't controlled by the scene graph clock, for example. In my game, some events are controlled by an internal tick counter that increments every time the gameloop runs, and so it was necessary for me to record this as part of the initial state information. (Along with the random number generator seed. Sorry, I was referring to these as two seperate entities.)
The rest of what you said is correct, though. But, as James said, everything must be deterministic, which is pretty simple with the pseudo-RNGs that are used in computers. (So you can still have "random" behavior but given the same PRNG seed it's always the same kind of randomness.)
07/25/2005 (11:38 am)
Well, in my game, all positions (player, enemies, starting location, etc.) are all fixed per level. There's no variance in enemy behavior, they will always execute the same moves when started from the same position, and so on. A very few minute things are controlled via random numbers on the enemies. (Most of the use of rand() comes from the player's side, where their power-up chooses its behavior randomly.)However, I see no reason why you couldn't do this with randomly-placed items, too. You said yourself that you've done random dungeons from seeds; It's the same concept. Maybe I am not getting what you mean by dynamic content?
You can ignore what I said about ingame time -- If your ingame events aren't controlled by the scene graph clock, for example. In my game, some events are controlled by an internal tick counter that increments every time the gameloop runs, and so it was necessary for me to record this as part of the initial state information. (Along with the random number generator seed. Sorry, I was referring to these as two seperate entities.)
The rest of what you said is correct, though. But, as James said, everything must be deterministic, which is pretty simple with the pseudo-RNGs that are used in computers. (So you can still have "random" behavior but given the same PRNG seed it's always the same kind of randomness.)
#11
I did a bit of experimenting and the random seed works for generating the same enemies for the attract mode. The problem is that you need the seperate demo tick cycle to ensure that things happen at the same time they did before. Without this, anything that has a linear velocity can end up in the wrong place and a shot that hit on my test system, may miss altogether on a different slower system. This leads to an attract mode that can quickly get out of sync and fall victim to entropy!
Syncing everything to an attarct mode tick solves this as the frame rate no longer matters.
Thanks for all the help
07/26/2005 (2:42 pm)
@DavidI did a bit of experimenting and the random seed works for generating the same enemies for the attract mode. The problem is that you need the seperate demo tick cycle to ensure that things happen at the same time they did before. Without this, anything that has a linear velocity can end up in the wrong place and a shot that hit on my test system, may miss altogether on a different slower system. This leads to an attract mode that can quickly get out of sync and fall victim to entropy!
Syncing everything to an attarct mode tick solves this as the frame rate no longer matters.
Thanks for all the help
#12
In addition you have to cover for situations where there can be a variance in time and also cover any near hit/miss situations. To explain this with an example: A falling player might just miss a hawk in my game, but during replay he hits him. The solution to this is to record every crucial event and trigger it while replaying and not rely (ie: switch off) on the game to reproduce it during replay. With clean gamecode this should be easy to add (took me about a day for EggStatic although playback is not perfect yet).
07/29/2005 (1:33 am)
@Peter I basicly followed the same tactic as David and most behaviour in EggStatic was -by design- already entirely predictable and absolute. If this isn't the case you need to record the random bit so you can feed that during replay. In addition you have to cover for situations where there can be a variance in time and also cover any near hit/miss situations. To explain this with an example: A falling player might just miss a hawk in my game, but during replay he hits him. The solution to this is to record every crucial event and trigger it while replaying and not rely (ie: switch off) on the game to reproduce it during replay. With clean gamecode this should be easy to add (took me about a day for EggStatic although playback is not perfect yet).
#13
My game allows for random enemy appearance (in fact this is a necessary part of the action) but, the seed seems to take care of this. I've rigged my demo to stop when the player gets killed or the demo timer runs out so, it is possible to have a different demo each time. I kind of like this so think I'll keep the random element :o)
Thanks for the help guys it is much appreciated. It's good to bounce ideas off of people who have already been there and know the problems that may be encountered.
07/29/2005 (3:57 am)
Thanks Edo and David.My game allows for random enemy appearance (in fact this is a necessary part of the action) but, the seed seems to take care of this. I've rigged my demo to stop when the player gets killed or the demo timer runs out so, it is possible to have a different demo each time. I kind of like this so think I'll keep the random element :o)
Thanks for the help guys it is much appreciated. It's good to bounce ideas off of people who have already been there and know the problems that may be encountered.
Torque Owner Ricky Taylor
Basically I beleive it stores every value required, input, net, etc... And then can play it back, im not sure... in the slightest.