Part 1
|
Part 2
|
Part 3
|
Part 4
|
Part 5
Introduction
I finally got a nice new idea for a tech-topic, and while I was doing this I
also released all tech-tutors to the net so that they can now be viewed
offline...
This topic is Demos, and I dont mean playable-demos, I mean a demo that will
be viewed when the player isn't touching the keyboard for a while (like in
Doom, JazzJackrabbit,etc...) or like the demos showed when you start Quake.
----------------------------------------------------------------
For contacting Just4Fun or me (PeeBee) use the following ways:
Internet : http://people.zeelandnet.nl/rpb
Email : just4fun@zeelandnet.nl
----------------------------------------------------------------
Difficult AI?
Ok most people don't have a clue as to how to program a rolling-demo, some
people might think that the programmer has programmed a very difficult AI
routine for the computer to play like a human...guess again, it much simpler
then that, because you can just find some human beta-tester (or do it
yourself) and let it play the computer while you "record"!
If you ever played with Doom, you might have created your own demo showing
how cool you are, and how good you where...well we'll do it that way as well!
Is This Camera On?
What we are going to do is record the actions of our beta tester...
Now we can do this in a smart way, or a very stupid way..I'll choose the
smart way for this tutor (it makes me look smart aswell ;)
To show the difference, here's how the stupid way would be:
Everytime the players keys are read for taking actions, we also write those
values to the demo file...wich means that every frame the keys are saved.
Now most games have 4 directions keys, 2 or more fire keys, and since doom
most games also have 0-9 for selecting weapons...
You want to save that all to a file? each frame? what if the player plays
for 30 minutes in one level? you better make sure your harddisk is large
enough for those demo files!
I Did it the Smart Way
First we will examine what went wrong in the "stupid" way. The reading of
the keys is ok. No problem there, but saving them at that point?
I dont think so, what if the player is not pressing any keys? then we are
saving things that need no saving! On the other hand, what if the player is
still pressing that one direction key he was pressing for the last 2
minutes? we would save alot of extra values to the file which we don't need...
What we do is the following: We keep a small counter (preferably a long int)
that is set to 0 at the beginning of the game/level/demo.
Now as long as the player doesn't release/press a key, we simply increase
this counter (per frame) once the player DOES make a change, we need to
write those changes to the demo file!
So first we write the counter value, because this can be used when reading
the demo, to specify the time of the previous keys pressed values and the
amount of frames that they are being pressed/released...
After the counter we write the new values for all keys involved, and reset
the counter to 0...so we can start again with counting the frames until
something happens again.
Timeout, TIMEOUT!
One thing we really need to take care of is a small thing: the maximum value
of a long int! Because if the player doesn't touch anything for too long, and
the game is not paused, our counter is being increased, but once it reaches
its maximum value (which is possible!) it will restart at 0, and mix up our
timing of the complete demo...
So when increasing the counter, check on the maximum value, and if matched
just treat it like a new key-change was reported and save the counter+key
values...
Lights! Camera! Action!
Now we want to play the demo! Simple, just reset the demo (you might like to check if it's a valid demo, in
which case you would have to save the ID of your demo file at the beginning
of the demo-recording procedure). Reset the demo_frame-counter, and make a few changes to your player
procedure. The few changes are simple, you just have to keep a boolean that
specifies if there's a demo being played, and if so read the keys from the
file instead of from the keyboard!
Now the procedure that reads the keys from the demo file must only read when
the counter hits 0, because if counter > 0 then dec(counter).
After the counter hits 0, you simply read in the new demo-key values (store
them in a separate set of booleans then your normal key booleans because the
viewer might press some keys, and screw up the demo-playing).
Then the player procedure simply uses the demo-key values and walk thru the
normal player procedures.
Important things
Some important things are:
- Don't use random enemies!
- Make sure you initialize the player BEFORE playing the demo!
- You might like to save the map-file information into the beginning of the demo.
- Take a look at the example procedures supplied with this text
Final word
This tutorial was a bit difficult to write, but I hope its less painfull to
read. The best way to read this is to also look at the example procedures
which show how you could do it. Again, the examples are taken from the
SuperFX engine.
{*******************************Source Code*********************************}
{
TechTutor5
Demo Recording/playing (ripped from SuperFX Engine)
Coding by P.Bestebroer
FreeWare
Source does NOT work on its own, it was merely used for showing
howto, instead of doing it foryou.
The code DOES work when put inside the SuperFX engine (wich is
allready done).
Contacting:
HTTP://people.zeelandnet.nl/rpb/
EMAIL:just4fun@zeelandnet.nl
}
PROGRAM Tech05;
USES CRT;
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
{
First create a few variables
}
VAR DEMrecord : boolean; { recording demo? }
DEMplay : boolean; { playing a demo? }
DEMfile : file; { filename of the .DEM file }
CONST DEMID : string = 'SFX_DEMOv1'; { ID of the SuperFX Demo files }
VAR DEMleft : boolean; { left key is pressed? }
DEMright : boolean; { right key is pressed? }
DEMup : boolean; { up key is pressed? }
DEMdown : boolean; { down key is pressed? }
DEMfire : boolean; { fire key is pressed? }
DEMfire2 : boolean; { support for 4 joystick buttons }
DEMfire3 : boolean;
DEMfire4 : boolean;
DEMpress : longint; { time the keys are pressed }
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
{
This procedure will create a new DEMO file
Expects: Filename of the Demo
Returns: Nothing
}
FUNCTION _NewDemo(filename:string):boolean;
BEGIN
Halt_Proc:='_NewDemo';
assign(DEMfile,filename);
{$I-}
rewrite(DEMfile,1);
{$I+}
if IoResult<>0 then begin
_NewDemo:=false;
halt_Error:='Unable to assign demoname ('+filename+')';
exit;
end;
blockwrite(DEMfile,DEMID[1],sizeof(DEMID)); { write DEMO ID }
blockwrite(DEMfile,MapFile[1],32); { write map-filename }
blockwrite(DEMfile,MapLib,1); { write map-using lib? }
DEMleft:=false; { not pressed,}
DEMright:=false; { not pressed neither, }
DEMup:=false; { definetly not pressed, }
DEMdown:=false; { maybe...NAH not pressed! }
DEMfire:=false; { ...don't shoot!, don't shoot! }
DEMfire2:=false; { support for 4 joystick buttons }
DEMFire3:=false;
DEMfire4:=false;
DEMpress:=0; { the pressing-time of the keys }
DEMrecord:=true; { silence please, we're recording! }
{$IFDEF PLUGIN_SHOWEVENTS}
_Ucase(Filename);
_AddEvent('RECORDING DEMO ('+filename+')');
{$ENDIF}
_NewDemo:=true;
END;
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
{
This procedure will OPEN a DEMO for playing
Expects: Filename of the Demo
Returns: Nothing
}
FUNCTION _OpenDemo(filename:string):Boolean;
VAR testID : string[24];
tmpFile : string;
BEGIN
Halt_Proc:='_OpenDemo';
tmpFile:=filename;
assign(DEMfile,filename);
{$I-}
reset(DEMfile,1);
{$I+}
if IoResult<>0 then begin
_OpenDemo:=false;
Halt_error:='Couldn''t spawn demo ('+tmpFile+')';
exit;
end;
blockread(DEMfile,testID[1],sizeof(DEMID));
testID[0]:=DEMid[0];
if testID<>DEMid then begin
halt_Error:='Not a valid DEMO file ('+filename+')';
halt_proc:='_PlayDemo';
halt;
end;
blockread(DEMfile,MapFile[1],32); { read map-filename }
blockread(DEMfile,MapLib,1); { read map-using lib? }
LoadMap(mapLib,MapFile);
DEMpress:=0;
DEMplay:=true;
{$IFDEF PLUGIN_SHOWEVENTS}
_Ucase(tmpFile);
_AddEvent('PLAYING DEMO ('+tmpFile+')');
{$ENDIF}
_OpenDemo:=true;
END;
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
{
This will END the DEMO file, and close it...
Expects: Nothing
Returns: Nothing
}
PROCEDURE _EndDemo;
BEGIN
{$I-}
Close(DEMfile);
{$I+}
END;
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
{
This will handle the recording of key-presses, and save
it to the DEMO file.
Expects: all directions, and the fire presses
Returns: Nothing
}
PROCEDURE _RecordDemo;
PROCEDURE _ADDtoDEMObyte(code:byte);
BEGIN
blockwrite(DEMfile,code,1);
END;
PROCEDURE _ADDtoDEMO(code:longint);
BEGIN
blockwrite(DEMfile,code,4);
END;
BEGIN
if (joystickleft<>demleft) or (joystickright<>demright) or
(joystickup<>demup) or (joystickdown<>demdown) or (joystickA<>demfire) or
(joystickB<>demfire2) or (joystickC<>demfire3) or (joystickD<>demfire4) then begin
if demleft then _ADDtoDEMObyte(1) else _ADDtoDEMObyte(0);
if demright then _ADDtoDEMObyte(1) else _ADDtoDEMObyte(0);
if demup then _ADDtoDEMObyte(1) else _ADDtoDEMObyte(0);
if demdown then _ADDtoDEMObyte(1) else _ADDtoDEMObyte(0);
if demfire then _ADDtoDEMObyte(1) else _ADDtoDEMObyte(0);
if demfire2 then _ADDtoDEMObyte(1) else _ADDtoDEMObyte(0);
if demfire3 then _ADDtoDEMObyte(1) else _ADDtoDEMObyte(0);
if demfire4 then _ADDtoDEMObyte(1) else _ADDtoDEMObyte(0);
_ADDtoDEMO(dempress);
DEMleft:=joystickleft;
DEMright:=joystickright;
DEMup:=joystickup;
DEMdown:=joystickdown;
DEMfire:=joystickA;
DEMfire2:=joystickB;
DEMfire3:=joystickC;
DEMfire4:=joystickD;
DEMpress:=0;
end else inc(DemPress);
END;
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
{
This will handle the PLAYING the demo
Expects: all directions + fire keys (VARS to be returned)
Returns: the boolean filled with TRUE or FALSE
}
PROCEDURE _PlayDemo;
VAR i : byte;
PROCEDURE _GETfromDEMObyte(var code:byte);
BEGIN
blockread(DEMfile,code,1);
END;
PROCEDURE _GETfromDEMO(var code:longint);
BEGIN
blockread(DEMfile,code,4);
END;
BEGIN
Joystickleft:=DEMleft;
JoystickRight:=DEMright;
JoystickUp:=DEMup;
JoystickDown:=DEMdown;
JoystickA:=DEMfire;
JoystickB:=DEMfire2;
JoystickC:=DEMfire3;
JoystickD:=DEMfire4;
if DemPress>0 then dec(DemPress) else begin
if eof(DEMfile) then DEMplay:=false else begin
_GetFromDemoByte(i); if i=1 then DemLeft:=true else DemLeft:=false;
_GetFromDemoByte(i); if i=1 then DemRight:=true else DemRight:=false;
_GetFromDemoByte(i); if i=1 then DemUp:=true else DemUp:=false;
_GetFromDemoByte(i); if i=1 then DemDown:=true else DemDown:=false;
_GetFromDemoByte(i); if i=1 then DemFire:=true else DemFire:=false;
_GetFromDemoByte(i); if i=1 then DemFire2:=true else DemFire2:=false;
_GetFromDemoByte(i); if i=1 then DemFire3:=true else DemFire3:=false;
_GetFromDemoByte(i); if i=1 then DemFire4:=true else DemFire4:=false;
_GetFromDemo(DEMpress);
end;
end;
END;
{$ENDIF}
{ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
BEGIN
while port[$60]<>156 do ;
textcolor(7); textbackground(0); clrscr;
writeln('TechTutor #5');
writeln('written by P.Bestebroer, Just4Fun Productions');
writeln('');
writeln('A great source code showing how you COULD implement demo-playing');
writeln('and demo recording. The source does not work stand-alone, but it');
writeln('can be used when some tweaking is done, and you have a game to put');
writeln('it in.');
writeln('The source code was taken from the SuperFX engine.');
writeln;
writeln('Watch out for the other techtutors...');
writeln;
writeln('Press a key');
writeln;
writeln;
writeln;
writeln('----------------------------------');
writeln('Contacting: just4fun@zeelandnet.nl');
writeln('http://people.zeelandnet.nl/rpb ');
writeln('----------------------------------');
repeat until port[$60]<>156;
END.
|