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).