Home | Gaming | Programming | Play Online | Contact | Keyword Query
Games++ Games & Game Programming

GAMES++
Games++ Home
Games++ Gaming
Games++ Programming
Beta Testing Games
Free Online Games
Hints & Cheats

BROWSER UTILITIES
E-mail This Page
Add to Favorites

SITE SEARCH

Web Games++

AFFILIATES
Cheat Codes
Trickster Wiki
Game Ratings
Gameboy Cheats
PlayStation Cheats
BlackBerry Games
Photoshop Tutorials
Illustrator Tutorials
ImageReady Tutorials

ADVERTISEMENT

Demo Recording/Playing

Techtutor: Part 5

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.
Copyright © 1998-2007, Games++ All rights reserved. | Privacy Policy