A huge thanks to "tesanders". Without him most of this code wouldn't exist, so if you use this, give him a nice, huge credit.

This is what I consider to be the finest Resident Evil style camera system around. Of course, you can use it for other types of games....point & click adventures for example....but most of us associate them with survival horror type acton adventures.

The system sets up a "voronoi field". If you don't know what that means, you can find an explanation, complete with browser demo, here: http://www.cs.cornell.edu/Info/People/chew/Delaunay.html This type of environment sets up hot zones for each trigger and allows you to disconnect the camera from the trigger. This means you can place the camera 150000 miles away from its trigger or 150 inches for that matter. This opens up the creative possibilities and gives you, the level designer, much more breathing room. It does have a quirk or two. For example, it doesn't work very well with multifloored levels. You CAN use them with them, but it can be tricky at first. Once you get the hang of it, you'll find the right way but it can still be a royal hair pulling exercise to get the triggers in just the right spot.

It requires 3 objects to work(or 2 if you use the commented out alternate version of camera_move), and you can have cameras that always look at the player, always look at an offset between the player and another object, or completely still views like prerendered backgrounds.

Each camera has a camera entity and its corresponding trigger entity. You assign the "set_cam" and "set_cam_trigger" actions and set each entities skill1 to a value. Both entities must have the same skill1 value and you can use up to 200 cameras, by default, but you can use any total you want by changing the array value. Do not assign "0" as a skill1 value or it'll return without switching cameras. You can also use more than one trigger entity for each camera. This is useful when you need to extend the influence of a specific camera. You can insert an "island" of influence inside the zone of another camera's hotzone, too.

The 3rd object is a look at entity. It is used for an offset(if desired) based on the "cam_factor" variable. If you set it to 0, the camera will always look directly at the player. If you set it to 1, it will always look at the look at entity and will never move...a perfectly still camera. If you set it to any value between 0 and 1, it will always watch the "lerped" spot between the player and the look at entity and will move when the player moves. The downside to this setup is, as is, you can only have one look at object per level. It can easily be adapted so that each camera has its own look at object, but if you don't require an offset, you can comment out the camera_move function and uncomment the other. Using the alternate function, you can still achieve a perfectly still camera by setting the camera entities' flag1 to on. If you want it to follow the player, set it to off.

Code:
/////////////CAM

entity* cam_look_at; //the look at object pointer
entity* cam_ptr; //the camera object pointer
var cam_array[200]; // a maximum of 200 cameras...increase/decrease for your needs
var cam_closest_distance;
var cam_active; //0 = not active; 1 = active
var cam_target[3];  //temp camera storage
var cam_factor = 1; //0 = always follows player
  		    //1 = always look at cam_look_at
  		    //between 0-1 looks at offset between player and cam_look_at

function init_cameras()  //gets first camera
{ 
   get_cam(1); 
}

starter cam_poller() //controls the camera trigger array
{
    proc_kill(4);
    while(1) 
    {
       cam_closest_distance = 100000;
       wait(1);
    }
}

function get_cam(cam_number)  //retrieve fixed view
{ 
    proc_kill(4);
    if(cam_array[cam_number] == 0) { return; } 
    cam_ptr = ptr_for_handle(cam_array[cam_number]);
}

action set_cam //store fixed view
{
    my.invisible = on;
    my.passable = on;
    cam_array[my.skill1] = handle(me);
}

action set_cam_trigger //trigger for fixed view
{ 
    while(player == null) { wait(1); }
    my.passable = on;
    my.invisible = on;
    while(1) 
    {
      if(vec_dist(my.x, player.x) < cam_closest_distance) 
      {
         cam_closest_distance = vec_dist(my.x, player.x);
         get_cam(my.skill1);
      }
     wait(1);
    }
}

action set_cam_look_at //assign to look at object
{
  my.passable = on;
  my.invisible = on;
  cam_look_at = me;
}

function camera_move() //camera looks at cam_look_at or player or inbetween them depending on cam_factor
{
  while(cam_ptr == null) { wait(1); }
  while(cam_active == 1)
  {
    if(cam_look_at != null)
    {
      vec_set(camera.x, cam_ptr.x);
      vec_lerp(cam_target.x, player.x, cam_look_at.x, cam_factor); //get offset based on cam_factor
      vec_set(temp, cam_target.x); //assign to temp
      vec_sub(temp, camera.x);  //get cam_target's distance from camera
      vec_to_angle(camera.pan, temp); //camera looks at cam_target
    }
    wait(1);
  }
}

/*function camera_move() //alternate method...no offset funtionality
{
  while(cam_ptr == null) { wait(1); }	
  while(cam_active == 1)
  {
  	if(cam_ptr.flag1 == 1) //still camera
  	{
   	   vec_set(camera.x, cam_ptr.x);
   	   vec_set(camera.pan, cam_ptr.pan);
  	}
  	else //pivot camera
  	{
           vec_set(camera.x, cam_ptr.x);
     	   vec_set(temp, player.x); //assign to temp
      	   vec_sub(temp, camera.x);  //get player's distance from camera
      	   vec_to_angle(camera.pan, temp); //camera looks at player
        }
   wait(1);
  }
}*/

action player_action //assign to player
{   
   wait(1);
   if (player == null) { player = me; } 
	
   my.narrow = on;	
   //my.fat = on;
   //my.shadow = on;
   //my.cast = on;
	
   //put any other player flags here
	
   cam_active = 1;
   camera_move();
   init_cameras();
   
   //player while loop will go here
}



My User Contributions master list - my initial post links are down but scroll down page to find list to active links