Forum

Sprinting

Discuss programming in the QuakeC language.

Moderator: InsideQC Admins

Sprinting

Postby ScatterBox » Wed Aug 13, 2014 10:57 pm

Hello all,

I've been working on adding sprinting to my Quake mod for over the course of about a week. (Meaning that I've been working on it off and on, or whenever I stumble back over the code)

It hasn't gone quite how I've planned, but it's going as good as expected I guess.. I added sprinting and it worked, yay. But then I wanted something more so I decided to add a "cool down" kind of thing for the sprinting. (You can only sprint for 4 seconds) it seems like it all should work, but it just doesn't. I've combed through the code for about an hour (which is a long time since there isn't much) and added things, changed things, shorted the function... a lot...

Anyways, I've been avoiding asking for help on here because I want to actually learn something, and you wouldn't believe how much more I've learned on my own then when I was asking everyone else for help, but, I've given in for now. I really need to finish this before this Friday (I have a deadline to meet) and I have some more things that I need to implement/fix as well.. but enough rambling, here's what I have.

Code: Select all
/*
   =================
   Sprinting
   =================
   */
   
   if (self.speed)
   {
      sprint_time = sprint_time + 1;
      
      cvar_set ("cl_forwardspeed", "95");

      if(sprint_time == 4) //four seconds
      {
         sound (other, CHAN_VOICE, "AMB/breath.wav", 1, ATTN_STATIC);   

         self.speed = !self.speed;
      }
   }
   else
   {
      cvar_set ("cl_forwardspeed", "50");
      
      if(sprint_time <= 4 && sprint_time >= 1)
      {
         sprint_time = sprint_time - 1;
      }
      
      if (sprint_time <= -1)
      {
         sprint_time = 0;
      }
      
      if(sprint_time >=5)
      {
         sprint_time = 4;
      }
   }


There you have it folks, sprinting. :D

I've done a minor debugging process for it and the problem is that sprint_time is always 0. Yes, zero, No matter what.

PS. I'm pretty sure a switch, case, break thing might work better, but I haven't grasped how to do that yet so yeah, multiple "if" functions for now :P

PSS. speed (aka, self.speed) is a button. (I have it assigned to left trigger) so when the player holds down the trigger the player sprints, and when he/she lets go, they walk
User avatar
ScatterBox
 
Posts: 50
Joined: Sun Oct 13, 2013 7:53 pm

Re: Sprinting

Postby Spike » Thu Aug 14, 2014 12:03 am

better to use .maxspeed instead of hacking client speeds.
bind a "+moveleft;cl_sidespeed 5000"
superfast!
the maxspeed field blocks it serverside.
wait, or was that qw only? thought it got added in 1.09? am I misremembering? too lazy to look it up. bah
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: Sprinting

Postby gnounc » Thu Aug 14, 2014 12:35 pm

ok, the first thing im noticing, is
Code: Select all
       if (self.speed)
       {
          sprint_time = sprint_time + 1;


Assuming that gets run every frame, (which is MUCH faster than being run every second)
in 4 frames, sprint_time is going to be 4.
at which time
Code: Select all
          if(sprint_time == 4) //four blinks of an eye..not four seconds
          {
             sound (other, CHAN_VOICE, "AMB/breath.wav", 1, ATTN_STATIC);   

             self.speed = !self.speed;
          }


will evaluate, see that sprint_time is four, and switch self.speed back off.

THEN the whole thing is going to be evaluated again, but self.speed will be off (false)

so the else block will execute

Code: Select all
   else
       {
          cvar_set ("cl_forwardspeed", "50");


shutting down your forward speed.

now in that same block, its going to check sprint time.
HOWEVER, nowhere that i can see, does sprint_time get reset. so its still at 4.
which means...

Code: Select all
          if(sprint_time <= 4 && sprint_time >= 1)
          {
             sprint_time = sprint_time - 1;
          }


its equal to 4. so that check passes, and its greater than 1, so that check passes.
so now sprint time is 3.

your two next checks fail, so its back to the top (with sprint_time being 3, and i assume you're still holding the button, so self.speed is 1.

at some point i got lost in your codes logic. but i wanted you to see step by step (though i may have missed some of the finer points) how it reads. next post will show how i would have done it.
User avatar
gnounc
 
Posts: 424
Joined: Mon Apr 06, 2009 6:26 am

Re: Sprinting

Postby gnounc » Thu Aug 14, 2014 1:01 pm

blegh. i didnt realize how rusty i was until i tried to do a simple timer. fml.
anyways, heres what i got.

hopefully it doesnt end up embarassing me in short order.

Code: Select all

float runLength;               //how long you can run.
float runDelay;                  //how long until you can run again.

runLength = 40;      //play with this number until you get the desired run time
runDelay = 60;      //play with this number until you get the desired run delay
runRechargeRate = 0.1;   //play with this number until you get the desired recharge rate
/*and for gods sake, PRINT THESE NUMBERS SOMEWHERE so you can see whats going on.*/

void run()
{
      if(runTimer >= runLength)
         runTimer = runTimer + runDelay;   //add stamina penalty
         return;                                 //cant run again yet
};

void unrun()
{
   if(runTimer < 0)
      runTimer = 0;      //cap it.

   if(runTimer)
      runTimer - runRechargeRate;   //recharge your stamina.
};

//add run() and unrun() to the impulse list
//then
//
//alias +sprint impulse 55
//alias -sprint impulse 56
//bind shift +sprint
User avatar
gnounc
 
Posts: 424
Joined: Mon Apr 06, 2009 6:26 am

Re: Sprinting

Postby ScatterBox » Thu Aug 14, 2014 5:10 pm

Well, under further testing of my code I found something odd.

I set sprint_time directly to 8 whenever the player presses the button

Code: Select all
if (self.speed)
   {
      sprint_time = 8;


then I put a simple bprint below the spint code in PlayerPreThink

Code: Select all
bprint (ftos(sprint_time));


and the number was always 0, when holding sprint button, and when not. So why is it not updating sprint_time whenever I hold the sprint button? It sets cl_forward speed correctly, but is not updating sprint_time.. hmm. :roll:
User avatar
ScatterBox
 
Posts: 50
Joined: Sun Oct 13, 2013 7:53 pm

Re: Sprinting

Postby Cobalt » Sun Aug 17, 2014 5:15 am

Maybe you need to strzone the string.
User avatar
Cobalt
 
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA

Re: Sprinting

Postby Spike » Sun Aug 17, 2014 5:28 am

when printing, add \n afterwards (second print?), or just use centerprint instead.
failure to do so will just weirdly bug out the prints.

secondly, your sprint_time value is presumably a global. this is stupid. if its a local then its even more broken.
this variable should be a field instead. self.sprint_time. this allows coop to work.
although, yes, if your game is strictly singleplayer then a global is fine - is your mod strictly single player? :s
either way, make sure its NOT a local! that might help explain how its always 0.

thirdly, counting frames is flawed. think functions are generally scheduled 10 times a second. that means your limit of 4 gives 0.4 (or so, it can actually vary a little) seconds of sprinting... which isn't much at all.
if instead your concept of frames is that of playerprethink/playerpostthink, then you're getting noticably more frames than that (72 or so, but can vary)! counting frames is just unreliable however you look at it.
the solution is to add/subtract frametime and work purely in actual time, rather than in frames.
yes, this means that you can somewhat easily over-spend. just make sure the player still has to pay (in downtime) and your half-frame overspend is unlikely to be a problem.
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: Sprinting

Postby Cobalt » Sun Aug 17, 2014 4:08 pm

Just a quick note on frames / frametimes.

The cvar: sys_ticrate decides the "think rate" so to speak.

DP default is: 0.01388889 seconds. Thats the time it takes for (1) frame to complete. Take the inverse of that (1 / sys_ticrate) and you get close to 72 FPS....the DP 'default'. Other engines may vary.

Alot of mods I have seen float that cvar into the progs , and I came up with a way thats pretty simple if you want say an ent to think every (x) frames, as opposed to its nextthink being set to a constant time + x type value.

Code: Select all

ent.nextthink = time + (sys_ticrate * 40); // Think every 40 frames, or a little over half a second.


Has its uses if you are experimenting with animations and want them to happen at a rate that the eye can really pick up , as opposed to it happening too quickly. Also lets say en ent stops thinking because of a code bug....you can easily get it thinking on the very next frame immediately by:
Code: Select all
if (ent.nextthink < time)
ent.nextthink = time + sys_ticrate;
User avatar
Cobalt
 
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA

Re: Sprinting

Postby ScatterBox » Mon Aug 18, 2014 3:44 am

Thanks for all the replys!

Ok, I made the float a .float in defs.qc so now everything is like this

Code: Select all
if (self.speed)
   {
      self.sprint_time += 1;


Code: Select all
      if(self.sprint_time <= 400 && self.sprint_time >= 1)
      {
         self.sprint_time -= 1;
      }


and I fixed the bprint
Code: Select all
bprint(ftos(self.sprint_time), "\n");
it gives me a warning, but it works. : p

Still though, self.sprint_time stays at 0. Hmm. It's really starting to bother me..
User avatar
ScatterBox
 
Posts: 50
Joined: Sun Oct 13, 2013 7:53 pm

Re: Sprinting

Postby Cobalt » Mon Aug 18, 2014 4:04 am

Well on the first check, it would be = 1, so then it will see it = 1, and take one, so you get 0
User avatar
Cobalt
 
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA

Re: Sprinting

Postby ScatterBox » Mon Aug 18, 2014 4:18 pm

Cobalt wrote:Well on the first check, it would be = 1, so then it will see it = 1, and take one, so you get 0


Oh, sorry I forgot to mention that the second check (if(self.sprint_time <= 400 && self.sprint_time >= 1)) is in the else statement, so it only gets checked if the player isn't holding the sprint button. :)
User avatar
ScatterBox
 
Posts: 50
Joined: Sun Oct 13, 2013 7:53 pm

Re: Sprinting

Postby r00k » Mon Aug 18, 2014 5:26 pm

Ok so, in weapoons.qc under ImpulseCommands, i tested using this

Code: Select all
   if (self.impulse == 13)
   {
      if (!(self.sprinting))
      {
         self.sprint_time = time + 4;
         centerprint(self,"run");
         self.sprinting = TRUE;
      }
   }
         
   if (self.impulse == 14)
   {
      self.sprint_time = time;
      centerprint(self,"walk");
      self.sprinting = FALSE;      
   }      

then.. in client.qc, PlayerPreThink i did this...
Code: Select all
   if (self.sprint_time > time)
   {
      if ((self.flags) & FL_ONGROUND)
      {
         centerprint(self,"run");   
         if ((self.sprint_time - time) <= 1)
          sound (other, CHAN_VOICE, "AMB/breath.wav", 1, ATTN_STATIC);
      }
   }
   else
   {
      if ((self.flags) & FL_ONGROUND)
      {
         centerprint(self,"walk");
         self.velocity_x = (1 - (10 * frametime)) * (self.velocity_x);
         self.velocity_y = (1 - (10 * frametime)) * (self.velocity_y);
         self.sprinting = FALSE;
      }
   }


This toggles run/walk and even holding run for 4 seconds will stop running.

basically, if you have always run on, it works how you'd think it should (about 208 walk, 318 for run)...

edit: obviously this isnt the only way to do this. Depends how you want it to behave. You could have a value for sprint_count and each second sprinting subtract that counter, then each second not sprinting increase the counter. This would limit the time you can run full speed, but also
require some time to build up some stamina too. :)


edit #2:
okay i added the cool down timer of 4 seconds. So, you can only run 4 seconds then you have to wait 4 seconds before your can sprint again
Code: Select all
   if (self.impulse == 13)
   {
      if (!(self.sprinting))
      {
         if ((time - self.sprint_time) > 4)//wait 4 seconds until they can sprint again...
         {
            self.sprint_time = time + 4;
            centerprint(self,"run");
            self.sprinting = TRUE;
         }
      }
   }
         
   if (self.impulse == 14)
   {
      self.sprint_time = time;
      centerprint(self,"walk");
      self.sprinting = FALSE;      
   }      


it's not PERFECT, but gives you a method of timing, and how to control player velocity thru the server's physics.
r00k
 
Posts: 1110
Joined: Sat Nov 13, 2004 10:39 pm

Re: Sprinting

Postby Cobalt » Tue Aug 19, 2014 9:56 pm

[quote="r00k"]

Whats the frametime float doing?


Code: Select all
   {
      if ((self.flags) & FL_ONGROUND)
      {
         centerprint(self,"walk");
         self.velocity_x = (1 - (10 * frametime)) * (self.velocity_x);
         self.velocity_y = (1 - (10 * frametime)) * (self.velocity_y);
         self.sprinting = FALSE;
      }
User avatar
Cobalt
 
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA

Re: Sprinting

Postby r00k » Wed Aug 20, 2014 6:35 pm

im not 100% sure but i think it is 1 at the end of a frame
otherwise 0 so its not updating too often
this is how the player is slowed down in the watermove code
so i just modified it for this use.
r00k
 
Posts: 1110
Joined: Sat Nov 13, 2004 10:39 pm

Re: Sprinting

Postby Spike » Wed Aug 20, 2014 7:23 pm

looks like it'll spasm if you drop below 10fps.
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Next

Return to QuakeC Programming

Who is online

Users browsing this forum: No registered users and 1 guest