A blog about 3DGS, Nerds and a lot of other stuff

Code Snippet #1

April 15th, 2008 Michael Schwarz and categorized as 3DGS

Code Snippets

Hereby I introduce the new code snippets section. Today serving you a Half-Life like movement script.

This script works different than most movement scripts, as it uses vec_accelerate instead directly passing the speed to the c_move parameters, thus creating a move natural movement.

More after the click:

First off we want to set a few constants, aswell as a few dynamic variables which we will later use in the script. Using defined constants makes it easier to read the script as it does make it easier to later modify a specific value without having to crawl through the whole script to find a specific value.

   1: define true, 1;
   2: define false, 0; 
   3:  
   4: define relSpeed_x,skill7;
   5: define relSpeed_y,skill8;
   6: define relSpeed_z,skill9;
   7: define absSpeed_x,skill10;
   8: define absSpeed_y,skill11;
   9: define absSpeed_z,skill12;
  10: define P_Speed,skill13;
  11: define P_AngSpeed,skill14;
  12: define P_Weight,skill15;
  13: define P_Friction,skill16; 
  14:  
  15: var GLOB_BkgColor[3];
  16: var PHY_Gravity[3];
  17: var MOV_Playerspeed = 25;
  18: var MOV_Turnspeed = 10;
  19: var MOV_MaxUpAngle = 90;
  20: var MOV_MaxDownAngle = -90;
  21: var MOV_ShiftFactor = 1.35;

So, I use the opportunity to set up my standard defines for true and false, which I prefer to use over 1/0 or on/off respectively.

We store the player input in an imaginary vector called “relSpeed” in the skills of the entity, for the sake of being able to have a better accessibility for external events.

Global acceleration, like gravity, is passed by the absSpeed vector.

Then we have the values P_Speed, P_AngSpeed, P_Weight and P_Friction. P_Speed indicating the current speed the player is moving and P_AngSpeed indicating the speed the player is turning around.

Now we have some dynamic variables that are part of the movement system:

  • GLOB_BkgColor[3] : Actually just for defining the sky color.
  • PHY_Gravity[3] : We will define the values for this at game start. It’s as the name says the gravity.
  • MOV_Playerspeed : You will have to play with this value, as it defines how fast your player will move.
  • MOV_Turnspeed : You have to play with this value too. Defines the speed you will turn/look around.
  • MOV_MaxUpAngle : The maximal angle you can look upwards.
  • MOV_MaxDownAngle : The maximal angle you can look downwards.
  • MOV_ShiftFactor : The factor the MOV_Playerspeed gets multiplied by when holding the shift key. Use a value over 1.00 to accelerate the movement when pressing this key, or a value below 1.00 if you want the player to slow down when you press the shift key.

Now comes the main function for the whole thing, it is actually this function that does most of the difference to other movement codes:

   1: function FNC_ApplyPhysics(ent, enablePoly)
   2: {
   3:     me = ent;
   4:     vec_set(me.absSpeed_x, PHY_Gravity.x);
   5:  
   6:     while(me)
   7:     {
   8:         c_move(me, nullvector, me.absSpeed_x, IGNORE_SPRITES);
   9:         c_move(me, me.relSpeed_x, nullvector, IGNORE_SPRITES|GLIDE);
  10:         wait(1);
  11:     }
  12: }

As the commentary says, we use c_move twice here. One time for the absolute speed and one time for the relative speed. This is necessary as for the normal “movement” we need the GLIDE flag, or else we would become stuck in edges; and honestly you would actually not even move without it. Then we need the second c_move without the GLIDE flag because else you wouldn’t be able to walk up stairs or ledges.

And finally, the main movement code:

   1: function FNC_MoveWASD(ent)
   2: {
   3:     // :: Assign ME pointer to entity.
   4:     me = ent;
   5:  
   6:     // :: Main loop
   7:     while(me)
   8:     {
   9:         // :: Rotate camera
  10:         camera.pan += -((mouse_force.x * MOV_Turnspeed) * time_step);
  11:  
  12:         // :: Assign camera pan to entity pan
  13:         me.pan = camera.pan;
  14:         
  15:         // :: Camera Tilting
  16:         // : If Camera tilt is over or under maximal tilt angles
  17:         if(camera.tilt > MOV_MaxUpAngle || camera.tilt < MOV_MaxDownAngle)
  18:         {
  19:             if(camera.tilt > MOV_MaxUpAngle)
  20:             {
  21:                 // :: If it is over the maximal upper limit
  22:                 camera.tilt = int(camera.tilt - 2*time_step);
  23:                 // : Limit mouse movement
  24:                 if(mouse_force.y < 0){camera.tilt += ((mouse_force.y * MOV_Turnspeed) * time_step);}
  25:             }
  26:             else
  27:             {
  28:                 // :: If it is over the maximal lower limit
  29:                 camera.tilt = int(camera.tilt + 2*time_step);
  30:                 // : Limit mouse movement
  31:                 if(mouse_force.y > 0){camera.tilt += ((mouse_force.y * MOV_Turnspeed) * time_step);}
  32:             }
  33:         }
  34:         else
  35:         {
  36:             // :: Else
  37:             // : Free tilting
  38:             camera.tilt += ((mouse_force.y * MOV_Turnspeed) * time_step);
  39:         }
  40:         
  41:         // :: If camera roll is not equal to 0°
  42:         if(camera.roll != 0)
  43:         {
  44:             // :: And if camera roll is higher or equal to 180°
  45:             if(camera.roll >= 180)
  46:             {
  47:                 // : Roll clockwise
  48:                 camera.roll = int(camera.roll + 2*time_step);
  49:             }
  50:             else
  51:             {
  52:                 // :: else
  53:                 // : roll counter-clockwise
  54:                 camera.roll = int(camera.roll - 2*time_step);
  55:             }
  56:         }
  57:         
  58:         vec_accelerate(my.relSpeed_x, my.P_Speed, vector(((key_w-key_s)*MOV_Playerspeed*(1+key_shift*MOV_ShiftFactor))*time_step, ((key_a-key_d)*MOV_Playerspeed*(1+key_shift*MOV_ShiftFactor))*time_step, me.relSpeed_z), 0.5);
  59:         wait(1);
  60:     }
  61: }
  62:  
  63: function FNC_AttachCam(ent, z_offset)
  64: {
  65:     me = ent;
  66:     vec_set(camera.x, vector(me.x, me.y, me.z));
  67:     if(!key_ctrl){ camera.z += z_offset; }    
  68: }
  69:  
  70: action ACT_Player
  71: {
  72:     player = me;
  73:     FNC_ApplyPhysics(player, false);
  74:     FNC_MoveWASD(player);
  75:     
  76:     camera.genius = player;
  77:     
  78:     while(1)
  79:     {
  80:         FNC_AttachCam(player, 35);
  81:         wait(1);
  82:     }
  83: }

In the action ACT_Player you might want to change the second parameter of the FNC_AttachCam inside the while-loop, it defines the height of the camera from the origin of the player, so depending of the size of your player model this value can vary.

Inside your main function, you will also need to include the following BEFORE loading any level:

   1: PHY_Gravity[0] = 0;
   2: PHY_Gravity[1] = 0;
   3: PHY_Gravity[2] = (-9.8); 
   4:  
   5: camera.arc = 75;
   6: fps_max = 60;
   7: fps_min = 35;
   8: fps_lock = on;

This defines the Gravity. The value of (-9.8) should be adequate to the real earth gravity when using the Warlock/Cbabe/Guard model as reference. The FOV is set to 75, as it gives the whole thing a more natural feeling overall and we set an FPS lock so the movement doesn’t get jumpy or jerky whenever your FPS should rise or fall unexpectedly.

Now just attach the ACT_Player action to your player model in WED and have some Half-Life-Movement-Fun !

Stay tuned for the upcoming issue #2 of Code Snippets!

This article has been read 173 times

No Comments

Author (Required)

Mail (Will not be published) (But required anyway)

Website (Woo, optional!)