This
month I want to show you a "pro" feature that is extremely useful in action
games, sport games, adventure games, strategy games... in fact, I think
that any game could use a remote camera.
Acknex
has improved a lot lately and this is one of the hot new additions to the
engine: the ability to render a view on a sprite.
Practical use: display your football game on a big screen, keep an eye on your fortress' entrance, create rear view mirrors for your car and so much more!
First of all, we define a view:
view
remote
{
pos_x = 0;
pos_y = 0;
size_x = 128; // size of the view on x and y
size_y = 64;
layer = 5;
flags = visible;
}
We must set its size on x and y to the size of the sprite (128 x 64 pixels in my example). I chose layer = 5 but you can choose any small value. This will be our virtual "screen" but we also need a camera to watch what's happening:
action
camera1
{
my.invisible = on;
my.passable = on;
remote.x = my.x;
remote.y = my.y;
remote.x = my.x;
remote.pan = my.pan;
remote.tilt = my.tilt;
remote.roll = my.roll;
}
You can use any model for the camera: place it in Wed, set its position and angles and they will be transmitted to the remote view. This should be all... oh, wait, we need to show the image captured by the "screen" using a sprite:
action
monitor
{
my.oriented = on; // oriented sprite
remote.bmap = bmap_for_entity (my, 0);
}
I am
using an oriented sprite; the view will be rendered on "my" bitmap = the
bitmap used for the sprite with this action attached to it.
Blood pool
This snippet will teach you make your enemies bleed as soon as they die. I have kept the code as simple as possible:
define number_of_hits skill1;
action
enemy_blood
{
var blood_coords; // local var
my.enable_entity = on;
my.enable_impact = on;
my.event = enemy_hit;
my.number_of_hits = 0;
while(my.number_of_hits == 0) // standing
{
ent_cycle("stand", my.skill20); // play "stand" animation
my.skill20 += 3 * time;
my.skill20 %= 100; // loop
wait (1);
}
I have defined a local var named blood_coords. I know that you are too shy to ask but I will explain right here, right now what's with these local and global variables. A global variable is defined outside any action or function - it isn't enclosed by curly brackets. A global variable can be accessed from any other function or action. It is good to know that you can type ammo1 += 100 at the console and get 100 extra bullets for the weapon that uses ammo1, isn't it? A local variable is defined exactly like a global var, but its definition is placed inside the action or function, like the one in action enemy_blood. A local var is known only inside the action / function that has its definition inside it. If you would want to type blood_coords.x = 100; at the console it wouldn't work, get it? Local vars are just like entity skills: you can use them only within the action or function that has defined them. Let's get back to work now... The local var named blood_coords will be set to the origin of the blood spot. I could have used a global var but if you would have attached the same action to several models, every model could have changed blood_coords because this var would have been known to all the actions and functions. A local blood_coords var is recognized only inside its own action or function so the blood_coords value for one of the enemies can't interfere with other blood_coords value, used by another enemy.
The enemy is sensitive to impact; shoot it with a rocket to trigger its enemy_hit event. I have used number_of_hits as a name for skill1; as long as number_of_hits is zero, the enemy will play its "stand" animation in a loop
// the enemy was hit by a rocket - it must die
my.skill20 = 0;
while(my.skill20 < 60) // one shot animation, 60% of "death" because
the last "death" frame is damaged - open the model in Med to see for yourself
{
ent_cycle("death", my.skill20); // play "death" animation
my.skill20 += 2 * time;
wait (1);
}
my.event = null; // don't react to other events from now on
// the enemy is dead here - time to create the blood pool
vec_set(blood_coords, my.x);
blood_coords.z -= 500;
trace_mode = ignore_me + ignore_models + ignore_sprites;
blood_coords.z = my.z - trace (my.x, blood_coords) + 2; // place the blood
sprite 2 quants above the ground
ent_create(blood_pcx, blood_coords, create_blood);
}
When
number_of_hits is greater than zero, the enemy has to die; we play its
death animation (only 60% of its death frames because the last death frame
is damaged - open the model in Med to see it). We set the event to null,
we trace 500 quants below the enemy to get the position of the floor, we
set blood_coords.z two quants above the ground and we create the blood
sprite using function create_blood:
function
create_blood()
{
my.passable = on;
my.oriented = on;
my.tilt = 90;
my.flare = on;
my.ambient = 100;
my.bright = on;
while (my.scale_x < 5)
{
my.scale_x += 0.05 * time;
my.scale_y = my.scale_x;
wait (1);
}
}
The blood sprite is passable, oriented and has its flare and bright flags set. The sprite will increase its size on x and y five times.
The function that triggers all this scary stuff is:
function
enemy_hit()
{
if (event_type == event_entity || event_type == event_impact)
{
if (you == player) // collided with the player?
{
return; // do nothing
}
else // hit by a rocket
{
my.number_of_hits += 1;
}
}
// add other events here
}
If
the enemy has collided with an entity, it checks to see if that entity
is the player. If the enemy was hit by a rocket, number_of_hits will be
increased and the enemy will die.