• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[system]Orbital Camera

Level 3
Joined
Nov 12, 2010
Messages
43
Orbital Camera
This simple script allows to implement a camera that orbits arround the desired unit using the arrow keys.
Main Script
JASS:
//! zinc
///////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
//                 Orbital Camera 
//                    (By IO)
// Requires 
//    -Last version of JassHelper
/////////////////////////////////////////////////////////////////

library OrbitalCamSystem  {
//Configurable System Variables
  constant real DELAY = 0.45; //smoothing factor
  constant real ANGLE_OF_ATTACK = 150.0; //
  constant real MIN_AOA = -20.0; //min angle of attack
  constant real MAX_AOA = -80.0; //max angle of attack 
  constant real AOA_SENS = 4.0; //AOE change velocity
  constant real ROTATION_SENS = 16.0; //Rotation Change Velocity
  constant real DEFAULT_SENSITIVITY = 0.07; //Arrow Keys Sensitivity
  constant real DEFAULT_DISTANCE = 1000.0; //default distance of camera
  constant real DEFAULT_Z_DISTANCE = 128.0; //Height of camera
  constant real FPS = 12.0; //Frame Rate
  constant real CAM_ORIGIN = 0.0; //camera focus
  
  public unit HERO[];

 //   Default
 real DEF_AOA = -20;
 //  
 public struct Camera extends array{
  real rotation,angleOfAttack;
  real sensitivity;
  boolean Upressed,Dpressed,Lpressed,Rpressed;
  boolean IsCameraActive;
  location l;
  
  static method onPressUp() -> boolean{
    thistype this = GetPlayerId(GetTriggerPlayer());
      this.Upressed=true;
    return false;
  }
  
 static method onPressLeft() -> boolean{
    thistype this = GetPlayerId(GetTriggerPlayer());
      this.Lpressed=true;
    return false;
  }

  static method onPressRight() -> boolean{
    thistype this = GetPlayerId(GetTriggerPlayer());
      this.Rpressed=true;
    return false;
  }
  
    static method onPressDown() -> boolean{
    thistype this = GetPlayerId(GetTriggerPlayer());
      this.Dpressed=true;
    return false;
  }
  
  static method onRelease() -> boolean{
    thistype this = GetPlayerId(GetTriggerPlayer());

     if (this.Upressed){
      this.Upressed=false;
     }
     
     if (this.Dpressed){
      this.Dpressed=false;
     }
     
     if (this.Lpressed){
      this.Lpressed=false;
     }
     if (this.Rpressed){
      this.Rpressed=false;
     }
     
    return false;
  }
  
  static method updatingControls(){
    integer i=0;
    thistype this = 0;
    
   while (i<12){ 
     this = i;
    if ((this.Upressed)&&(this.angleOfAttack<MIN_AOA)){ this.angleOfAttack += AOA_SENS;}
    if ((this.Dpressed)&&(this.angleOfAttack>MAX_AOA)){ this.angleOfAttack -= AOA_SENS; }
    if (this.Lpressed){ this.rotation += ROTATION_SENS;}
    if (this.Rpressed){ this.rotation -= ROTATION_SENS;}
    if ((this.rotation > 360.0)||(this.rotation < -360)){this.rotation=0.0; }
    i +=1;
   }
   
  }
  
  static method forcingCamera(){
    integer i=0;
    thistype this = 0;
    real z =0.0;
    real x =0.0 , y=0.0;
    real a =0.0,a2=0.0;

   while (i<12){
     this = i;
   if (this.IsCameraActive){  
    
    if (Player(i)==GetLocalPlayer()){
    
    a = GetUnitFacing(HERO[i])*bj_DEGTORAD;
    x = GetUnitX(HERO[i]);
    y = GetUnitY(HERO[i]);
    
     MoveLocation(this.l,x,y);
     z = GetLocationZ(this.l);
    
     SetCameraField(CAMERA_FIELD_TARGET_DISTANCE,DEFAULT_DISTANCE, DELAY);
     SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, this.angleOfAttack, DELAY);
     SetCameraField(CAMERA_FIELD_ROTATION, this.rotation, DELAY);
     
     SetCameraField(CAMERA_FIELD_ZOFFSET, GetCameraField(CAMERA_FIELD_ZOFFSET) + z - GetCameraTargetPositionZ() + DEFAULT_Z_DISTANCE, DELAY);
      SetCameraField(CAMERA_FIELD_ZOFFSET, GetCameraField(CAMERA_FIELD_ZOFFSET) + z - GetCameraTargetPositionZ() + DEFAULT_Z_DISTANCE, DELAY);
    
    SetCameraTargetController(HERO[i],CAM_ORIGIN*Cos(a),CAM_ORIGIN*Sin(a),false);
    }
    
   }

    i +=1;
    
   }  
  }
  
 } 

 //GUI support
 
 public function SupportGUI(){
   integer i =0;
   if(udg_UsingGUI){
   
   while(i <= bj_MAX_PLAYERS){
    HERO[i]=udg_Heroe[i+1];
    i +=1;
   }
   }
 }
 
 function onInit (){
  trigger t_up_press = CreateTrigger(), onOneSec= CreateTrigger();
  trigger t_up_release = CreateTrigger(),t_down_press=CreateTrigger(),t_down_release=CreateTrigger(),t_left_press=CreateTrigger(),t_left_release=CreateTrigger()
  ,t_right_press = CreateTrigger(),t_right_release=CreateTrigger();
  integer i = 0;
  real fps = 1.0/FPS ;
  Camera cam = 0;
  
  while (i < bj_MAX_PLAYER_SLOTS){
  if((GetPlayerSlotState(Player(i))== PLAYER_SLOT_STATE_PLAYING)&&( GetPlayerController(Player(i)) == MAP_CONTROL_USER )){
   TriggerRegisterPlayerKeyEventBJ( t_up_press , Player(i), bj_KEYEVENTTYPE_DEPRESS, bj_KEYEVENTKEY_UP );
   TriggerRegisterPlayerKeyEventBJ( t_up_release, Player(i), bj_KEYEVENTTYPE_RELEASE, bj_KEYEVENTKEY_UP );
   TriggerRegisterPlayerKeyEventBJ( t_down_release, Player(i), bj_KEYEVENTTYPE_RELEASE, bj_KEYEVENTKEY_DOWN );
   TriggerRegisterPlayerKeyEventBJ( t_down_press, Player(i), bj_KEYEVENTTYPE_DEPRESS, bj_KEYEVENTKEY_DOWN );
   TriggerRegisterPlayerKeyEventBJ( t_left_release, Player(i), bj_KEYEVENTTYPE_RELEASE, bj_KEYEVENTKEY_LEFT );
   TriggerRegisterPlayerKeyEventBJ( t_left_press, Player(i), bj_KEYEVENTTYPE_DEPRESS, bj_KEYEVENTKEY_LEFT);
   TriggerRegisterPlayerKeyEventBJ( t_right_release, Player(i), bj_KEYEVENTTYPE_RELEASE, bj_KEYEVENTKEY_RIGHT );
   TriggerRegisterPlayerKeyEventBJ( t_right_press, Player(i), bj_KEYEVENTTYPE_DEPRESS, bj_KEYEVENTKEY_RIGHT );
    cam = i;
    cam.angleOfAttack = DEF_AOA;
    cam.l = Location(0.0,0.0);
   } 
    i +=1;
  }
  TriggerRegisterTimerEvent(onOneSec, 1.0, false);
  TriggerAddAction(onOneSec, function SupportGUI );
  
  TriggerAddCondition(t_up_press, Condition(function Camera.onPressUp ));
  TriggerAddCondition(t_up_release, Condition(function Camera.onRelease ));
 
  TriggerAddCondition(t_down_press, Condition(function Camera.onPressDown ));
  TriggerAddCondition(t_down_release, Condition(function Camera.onRelease ));
 
  TriggerAddCondition(t_left_press, Condition(function Camera.onPressLeft ));
  TriggerAddCondition(t_left_release, Condition(function Camera.onRelease )); 
  
  TriggerAddCondition(t_right_press, Condition(function Camera.onPressRight ));
  TriggerAddCondition(t_right_release, Condition(function Camera.onRelease ));

  TimerStart(CreateTimer(),fps ,true , function Camera.forcingCamera);
  
  TimerStart(CreateTimer(),DEFAULT_SENSITIVITY ,true , function Camera.updatingControls);
 }
 

}
//! endzinc

Snippet
JASS:
//! zinc
library CamControl requires OrbitalCamSystem {
/*
  (snippet)
        this script activates and deactivates the camera system using the ESC key

*/

 function cond () -> boolean{
 Camera cam =GetPlayerId(GetTriggerPlayer());
 if (cam.IsCameraActive){
  cam.IsCameraActive=false;
  if(Player(GetPlayerId(GetTriggerPlayer()))==GetLocalPlayer()){
   ResetToGameCamera(1.0);
  }
 } 
  else if (!cam.IsCameraActive) {
  cam.IsCameraActive=true;
 }
 return false;
}


//===========================================================================
function onInit (){
    integer i = 0;
    trigger t = CreateTrigger(  );

     while (i <= bj_MAX_PLAYERS){
      TriggerRegisterPlayerEvent(t, Player(i), EVENT_PLAYER_END_CINEMATIC);
      i +=1;
     }
     TriggerAddCondition(t, Condition( function cond));
 }

}
//! endzinc

PD: Press the Esc Key to Activate the camera in the test map...
 

Attachments

  • OrbitalCamera.w3x
    34.4 KB · Views: 81
Pro/Bro-tips:

  • Your indenting is off. You should be using tabs, not single spaces.
  • JASS:
      static method onPressUp() -> boolean{
        thistype this = GetPlayerId(GetTriggerPlayer());
          this.Upressed=true;
        return false;
      }
      
     static method onPressLeft() -> boolean{
        thistype this = GetPlayerId(GetTriggerPlayer());
          this.Lpressed=true;
        return false;
      }
    
      static method onPressRight() -> boolean{
        thistype this = GetPlayerId(GetTriggerPlayer());
          this.Rpressed=true;
        return false;
      }
      
        static method onPressDown() -> boolean{
        thistype this = GetPlayerId(GetTriggerPlayer());
          this.Dpressed=true;
        return false;
      }
    These can be shortened:
    JASS:
    static method onPressUp() -> boolean{
        thistype(GetPlayerId(GetTriggerPlayer())).Upressed=true;
        return false;
    }
    
    static method onPressLeft() -> boolean{
        thistype(GetPlayerId(GetTriggerPlayer())).Lpressed=true;
        return false;
    }
    
    static method onPressRight() -> boolean{
        thistype(GetPlayerId(GetTriggerPlayer())).Rpressed=true;
        return false;
    }
    
    static method onPressDown() -> boolean{
        thistype(GetPlayerId(GetTriggerPlayer())).Dpressed=true;
        return false;
    }
  • Instead of registering all those arrow key events, you could make this require ArrowKeyEvent by Bribe. That way, you'd only need one function and you wouldn't have to spam functions :D
  • Upressed/Dpressed/Lpressed/Rpressed -> uPressed/dPressed/lPressed/rPressed
    I know lPressed is a bit ugly, but following convention is pretty important :/
    Also, IsCameraActive -> isCameraActive
  • Instead of using HERO unit variables, you could declare a public function (In Zinc, a public function is just like a normal vJass function with no prefix) so that a user could register units to the system. (function RegisterOrbitalCameraUnit takes player p, unit u returns nothing)
    That function would store the unit into a unit array you should declare in the globals of your system, and it would use that. Simple, ey? :>
  • Your onRelease function is bugged. Two buttons could be pressed and held simultaneously you know :p
    To fix this, you should instead check to see which event was fired using GetTriggerEventId()
    If the UP release event was fired, you would set Upressed = false.
    But, if you're going to use Bribe's awesome ArrowKeyEvent, you would just register an arrow key event, and check if not IsEventArrowKeyPressed() returns true. Then you would check which arrow key got pressed using GetEventArrowKey().

Other than that, looking good =)
 
I think this is way to simple for a camera system. I've seen much better systems out there. Well, I even wrote a much better system on my own for my map and never even bothered to share it just because I felt it was too simple.

In my oppinion, a camera system should at least have some kind of clipping protection and camera angle adjustment depending on wether the ground goes up or down.

For example, when the unit you view stands in front of stairs or a high cliff, you can not look up the cliff and simply lose all freedom to move around.
You can easily use your z detection method, do the vectoring and adjust the angle of attack in this case so that the camera looks up the cliff.

Also, you should check the line of sight of your camera to avoid nasty clipping with high doodads or destructables.
You can do that by using destructables for high objects and scan for them in the viewing vector.
Of course, it doesn't have that much to do with the camera system itself, but the peripherals to do so should be there.
 
Top