07 August 2009

Code of the Ninja: At the Movies

If you haven't already, read the Code of the Ninja: Introduction

Welcome back, Code Ninjas!

Last time, we learned how to implement a joypad system using binary values. Now we're going to be expanding on that a little by adding some scripts for recording joypad "movies".

Joypad movies can be useful in many ways. For one, you can easily record yourself playing a level of your game to make a demo, which might play if the player lingers on the title screen for too long. Or, you might allow players to record themselves, and share these movies as a way to show off their skills.

But they are also useful to you, as the game developer. If there is a bug or unexpected behaviour in your game, just start recording the joypad, and then trigger the bug. Now you can replay that sequence over and over until you determine the source of the bug. Then, once you think you've fixed the bug, you can play it once more to make sure that it's absolutely gone.

Also, if you want to record a true video movie of your game, for instance to make a YouTube trailer, many programmes that record video of the game window will slow down your game. If you record a joypad movie first, and then make a video of the joypad movie being played back, you won't have to actually be playing the game while it's slow.

Well, let's get to it!

Joypad movies will be saved to binary files. Our first script is save_joypad_file, which will set things up to start writing input to a file. It takes one argument, which is the name of the file to write to.

save_joypad_file()

close_joypad_file();
joy_mode = 2;
jfile = file_bin_open(argument0,1);
file_bin_rewrite(jfile);

You'll notice that the first thing it does is perform the script close_joypad_file. That's our next script. It takes no arguments.

close_joypad_file()

if joy_mode > 0
  {
  joy_mode = 0;
  file_bin_close(jfile);
  }

This script has to be called first so that if you call save_joypad_file while it is already recording or playing back a joypad movie, it will close the first one automatically.

It is also important to call close_joypad_file in the game end event of the Joypad object.

Next, let's write our next script, open_joypad_file. Like save_joypad_file, it takes one argument - the name of the file to read back.

open_joypad_file()

close_joypad_file();
joy_mode = 1;
jfile = file_bin_open(argument0,0);
jsize = file_bin_size(jfile);

You'll have noticed by now that the preceding three scripts all set the variable joy_mode to a value - 0 for normal, 1 for playback, and 2 for recording. Where joy_mode comes into play is in the next script - joy_step.

This script, joy_step, is not new. We wrote it in the last lesson, but it needs to be rewritten to accomodate the joypad movie system. Replace the whole script with this code:

joy_step()

JoyPast = JoyPrevious;
JoyPrevious = JoyCurrent;

if joy_mode = 1
  {
  JoyCurrent = file_bin_read_byte(jfile)+
  file_bin_read_byte(jfile)*256;
  jpos = file_bin_position(jfile);
  jprog = jpos/jsize;
  if jpos >= jsize
    {
    close_joypad_file();
    }
  }
else
  {
  JoyCurrent = 0;

  for (t=0;t<6;t+=1)
    {
    if joystick_check_button(1,Joy[t])
    or keyboard_check(Key[t])
    JoyCurrent |= 1<<t;
    }

  if AnalogCount
    {
    if joystick_xpos(1) < -AnalogDeadzone JoyCurrent |= LEFT;
    if joystick_xpos(1) > AnalogDeadzone JoyCurrent |= RIGHT;
    if joystick_ypos(1) < -AnalogDeadzone JoyCurrent |= UP;
    if joystick_ypos(1) > AnalogDeadzone JoyCurrent |= DOWN;
    }
  }

if joy_mode = 2
  {
  file_bin_write_byte(jfile,JoyCurrent mod 256);
  file_bin_write_byte(jfile,JoyCurrent div 256);
  }

First it checks to see if joy_mode is 1, for playback. If so, it reads from the file, updating a variable called jprog (you can take that bit out if you want, but it's useful for drawing a progess bar), and calling close_joypad_file automatically when it reaches the end. If joy_mode is something other than 1, input is received from the joypad and keyboard as normal, instead of from the file.

Next, it checks if joy_mode is 2, for recording. If so, it simply writes the value of JoyCurrent to the file as two bytes. If you choose to use more than 8 buttons (this tutorial only uses 6), the second byte will be necessary.

Okay! Now, all you have to do is include some interface for calling open-, save-, and close_joypad_file, and the rest will be taken care of. The simplest way is to call them in events for the press of function keys in the Joypad object.

Before we close this lesson, there is one more cool thing that recording joypad movies can do for you. Ghosts!

In Mario Kart, one of the coolest features is the ability to race against "ghosts". A ghost is just another driver, but with one important distinction. Instead of being controlled by computer AI, it is being controlled by a joypad movie. In this way, you can race against yourself, or go head to head with legendary runs by expert players, made years ago!

So, how do you include ghosts? If you simply play back the joypad file, JoyCurrent will receive its value from the file, and not the joypad, and your player object will follow the movie. We don't want that, though. We want the player object to be left alone, and have a new ghost object that follows the movie, co-existing with the player object. How do you get a ghost to play from the file, but leave the player object controlled by the joypad?

Well, you have to use a second set of variables besides JoyCurrent, JoyPrevious, and JoyPast. Let's call them gJoyCurrent, gJoyPrevious, and gJoyPast. These won't be global, but local to the ghost object, which should be a copy of the player object. Next, we'll need another set of scripts for checking them. Duplicate the joy, joy_pressed, and joy_released scripts and call them gjoy, gjoy_pressed, and gjoy_released. Then edit them so that they query gJoyCurrent, etc, instead of JoyCurrent.

Now, in the ghost object's code, replace anywhere the joy scripts were called with the gjoy scripts. Finally, you have to add code for opening, reading and closing the joypad movie in the ghost object, so that it does it all independently of the rest of your game.

I'd go into more detail about this last step, but it's easier to just look at it to see how it's done. You can use the link below.

Download the GML scripts and a GMK example of this lesson here.

Next time - Smart Triggers! Until then, happy coding, fellow Code Ninjas!

If you use my code or scripts in your game or engine, no credit is necessary. But I'd love to hear about your project if you do! Just drop me a comment below, or e-mail me at us.mercurysilver@gmail.com

No comments:

Post a Comment