Code snippets

Top  Previous  Next

Cutscene camera code

 

Here's how it works: you press R (record) and move the camera around the player (or wherever you want) ; all this time camera's coordinates (x y z pan tilt roll) are stored in a huge array. Press R again to stop recording - a small file (cutdata.txt) is saved on your hard drive. Now you can load it and play it whenever you want. Let's take a look at the code:

view cutscene_camera{}
var start_pos[6];

action cutscene_on
{
   start_pos[0] = my.x;
   start_pos[1] = my.y;
   start_pos[2] = my.z;
   start_pos[3] = my.pan;
   start_pos[4] = my.tilt;
   start_pos[5] = my.roll;

   waitt (64);

   camera.visible = off;
   cutscene_camera.size_x = screen_size.x;
   cutscene_camera.size_y = screen_size.y;
   cutscene_camera.pos_x = 0;
   cutscene_camera.pos_y = 0;
   cutscene_camera.x = my.x;
   cutscene_camera.y = my.y;
   cutscene_camera.z = my.z;
   cutscene_camera.visible = on;
   on_mouse_right = null;
   while (1)
   {
      cutscene_camera.x += 30 * cos(cutscene_camera.pan) * (mouse_right - mouse_left) * time;
      cutscene_camera.y += 30 * sin(cutscene_camera.pan) * (mouse_right - mouse_left) * time;
      cutscene_camera.z += 100 * sin(cutscene_camera.tilt) * (mouse_right - mouse_left) * time;
      cutscene_camera.pan -= 20 * (mouse_force.x) * time;
      cutscene_camera.tilt += 25 * (mouse_force.y) * time;
      if (key_r == 1 && index == 0) // if we press r and we aren't recording already
      {
          record_data();
      }
      if (key_p == 1) // if we press p
      {
           play_data();
      }
      wait (1);
  }
}

First of all you have to place an entity in the level and attach it the cutscene_on action; its position should be close to the final cut scene position. We store the initial position (x y z pan tilt roll) in start_pos[6] and then we wait 4 seconds to make sure that the splash screen has disappeared. I have created a new view named cutscene_camera that will replace the default view (camera). The new cutscene_camera view moves using the mouse and its buttons (including the right mouse button - RMB) so I had to disable other right click actions. The code that moves the cutscene_camera is simple (take a look at the cameras in Aum4 - I have similar code there). Maybe you are wondering what's with (mouse_right - mouse_left). When we press the RMB, mouse_right = 1 and the camera will move forward; when we press the LMB, mouse_right = 0 and mouse_left = 1 so the camera will move backwards. If both buttons are pressed the camera won't move because (mouse_right - mouse_left) = 0 (this happens if none of them is pressed, too).

if (key_r == 1 && index == 0)
{
    record_data();
}
if (key_p == 1)
{
    play_data();
}

If we press R and we aren't recording already record_data is called; we should press P to load and play the previously stored data.

Function record_data stores camera's coordinates in the array and saves the file that contains it on the hard disk.

var cutscene_position[60000];

function record_data()
{
   index = 0;
   while (index < 60000)
   {
       cutscene_position[index] = 0;
       index += 1;
   }
   index = 0;
   while (key_r == 0 && index < 59994)
  {
       cutscene_position[index+0] = cutscene_camera.x;
       cutscene_position[index+1] = cutscene_camera.y;
       cutscene_position[index+2] = cutscene_camera.z;
       cutscene_position[index+3] = cutscene_camera.pan;
       cutscene_position[index+4] = cutscene_camera.tilt;
       cutscene_position[index+5] = cutscene_camera.roll;
       index += 6;
       wait (1);
  }
  filehandle = file_open_write("cutdata.txt");
  temp = 0;
  while (temp <= index)
  {
      file_var_write(filehandle, cutscene_position[temp]);
      temp += 1;
  }
  file_close(filehandle);
  beep;
}

First of all we reset the array to make sure that its end won't contain data from previously recorded cut scenes. I have used an array with 60,000 elements; we use 6 of them every frame (x y z pan tilt roll) so this is enough for 10,000 frames; even if your game runs with 100 fps you can record for 100 seconds (increase the number of elements if this isn't enough for you).

The recording stops when we press R again or when the array has been filled. The first instruction after the while loop opens (or creates and opens, if it doesn't exist) the file named cutdata.txt. The data in cutscene_position is written in this file; in the end a simple beep tells us that the recording is really over.

The last function opens the cutdata.txt file, reads its content and restores the cutscene_camera positions every frame:

function play_data()
{
   filehandle = file_open_read("cutdata.txt");
   index = 0;
   temp = 0;
   while (key_l == 0 || temp < 59994)
   {
        cutscene_camera.x = file_var_read(filehandle);
        cutscene_camera.y = file_var_read(filehandle);
        cutscene_camera.z = file_var_read(filehandle);
        cutscene_camera.pan = file_var_read(filehandle);
        cutscene_camera.tilt = file_var_read(filehandle);
        cutscene_camera.roll = file_var_read(filehandle);
        if (cutscene_camera.x + cutscene_camera.y + cutscene_camera.z + cutscene_camera.pan + cutscene_camera.tilt + cutscene_camera.roll == 0)
        {
             index = 0;
             cutscene_camera.x = start_pos[0];
             cutscene_camera.y = start_pos[1];
             cutscene_camera.z = start_pos[2];
             cutscene_camera.pan = start_pos[3];
             cutscene_camera.tilt = start_pos[4];
             cutscene_camera.roll = start_pos[5];
             return;
        }
        temp += 1;
        wait (1);
    }
}

The camera stops when we have reached the end of the file (temp > 59994) or when we find the first zero elements in the array (look at the if structure - there's the test); the camera will be moved to its initial position. There are two small panels for recording (red) and playing (green) but they're way too simple.

Redefining the keys

The ability to redefine the keys can be implemented even if you use A4 but it is much easier to do it with A5. I have created a standalone example that moves a wmb box in a small level; you can redefine the movement keys anytime you want to by clicking a small panel.

First of all we define some strings, vars, a panel (click on it to redefine the keys) and a text:

string empty_str, "          ";
string up_str, "Move up";
string down_str, "Move down";
string left_str, "Move left";
string right_str, "Move right";
var left_key;
var right_key;
var up_key;
var down_key;
entity* player;

panel redefine_pan
{
  bmap redefine_map;
  pos_x = 510;
  pos_y = 10;
  flags = refresh, overlay, visible;
  on_click = change_keys();
}

text redefine_txt
{
  pos_x = 10;
  pos_y = 10;
  font = comic_font;
  string = empty_str;
}

Function main is simple:

function main()
{
   fps_max = 50; 
   d3d_panels = on; 
   load_level (<redeftst.wmb>);
   wait (2);
   camera.x = 0;
   camera.y = 0;
   camera.z = 490;
   camera.tilt = -90; 
   mouse_map = pointer_map;
   mouse_mode = 2;
   while(1) 
   {
       mouse_pos.x = pointer.x;
       mouse_pos.y = pointer.y;
       wait(1);
   }

}

We set the camera position and angle, we select the mouse pointer and we make it visible (mouse_mode = 2); function main ends with some code that moves the mouse. The action associated to the player contains a single line of code:

action set_player
{
player = me; 
}

Every time we click the redefine_pan panel, change_keys is executed:

function change_keys()
{
    redefine_txt.visible = on;

    redefine_txt.string = up_str;
    while (key_any == 0) {wait(1);}
    while (key_any == 1) {wait(1);}
    up_key = key_lastpressed;
    key_set(up_key, move_up);

    redefine_txt.string = down_str;
    while (key_any == 0) {wait(1);}
    while (key_any == 1) {wait(1);}
    down_key = key_lastpressed;
    key_set(down_key, move_down);

    redefine_txt.string = left_str;
    while (key_any == 0) {wait(1);}
    while (key_any == 1) {wait(1);}
    left_key = key_lastpressed;
    key_set(left_key, move_left);

    redefine_txt.string = right_str;
    while (key_any == 0) {wait(1);}
    while (key_any == 1) {wait(1);}
    right_key = key_lastpressed;
    key_set(right_key, move_right);

    redefine_txt.visible = off;
}

Let's take a good look at the code for the "Move up" key - the code for all the other keys is similar:

redefine_txt.visible = on;
Makes the text visible

redefine_txt.string = up_str;
Displays the "Move up" string

while (key_any == 0) {wait(1);}
Waits until we press a key

while (key_any == 1) {wait(1);}
Waits for release

up_key = key_lastpressed;
Stores the last pressed key in the up_key variable

key_set(up_key, move_up);
Assigns function move_up to the key

I have used a simple move_up function, without collision detection. You don't want to use a function like that in your game.

function move_up()
{
    while (key_pressed(up_key) == 1)
    {
         player.x += 10 * time;
         wait (1);
    }
}

As long as the key stored in up_key is pressed, the player (the box) is moved to the right. The functions that are used to move the box down, left and right are similar. Make sure that the functions associated to the player are using a single key (you need an action that moves the player to the right, another one that moves the player to the left and so on).