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 1233 times

7 Comments

  • At 2008.11.21 20:27, Kevinper said:

    Does this all go into one file? Is this saved as ACT_Player.wdl?

    I guess I need to be more advanced to understand exactly what to do but getting to that point seems almost impossible with all the old tutorials that do not work.

    It might be helpful if you had links to the files that were already finished as you cannot copy and paste this code into the script editor.

    I do think this is awesome and I hope to figure it out so I can use it and learn a little finally.

    Thanks,

    Kevin

    • At 2008.11.21 20:38, Michael Schwarz said:

      Hi!
      Yes everything comes in one file with exception for the last snippet box, which comes into your main function (as the comment says)

      Well, I made it “un-copiable” exactly for the reason that you learn better if you copy something by typing it, instead of copy+paste.

      But if you really insist, I can upload you the file.

      Anyway, I would suggest you to copy it. It’s the better way to learn. As you type, you realize what has to do with what. You recognize the conjunctions faster and better as if you would just copy and paste and run it ;)

      • At 2008.11.21 20:55, Kevinper said:

        I do see your point about typing but when you have a 30 day trial and your on day 12 and you still can’t figure out how to make something work, then you want to copy and paste.

        I did copy it and then got rid of the numbers (1:, etc.)

        Now to give it a try. Thanks!

        • At 2008.11.21 21:27, Michael Schwarz said:

          Ah sorry, I didn’t know you only had a trial.
          Anyway, If you still need the complete script or any help with it, dont hesitate to ask!

        • At 2009.01.18 23:05, Matt said:

          Thank you so much it works!

          • At 2009.01.19 16:03, Michael Schwarz said:

            You’re welcome anytime!

          • At 2009.11.15 09:17, TheDarkKnight said:

            This script is great, but is there a place to download it from as a working .c file because i meet difficulty with the defining. There are some syntax errors I can’t fix.

            Author (Required)

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

            Website (Woo, optional!)