Sprinting

Discuss programming in the QuakeC language.
ScatterBox
Posts: 50
Joined: Sun Oct 13, 2013 7:53 pm

Sprinting

Post by ScatterBox »

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
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Sprinting

Post by Spike »

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
gnounc
Posts: 428
Joined: Mon Apr 06, 2009 6:26 am

Re: Sprinting

Post by gnounc »

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.
gnounc
Posts: 428
Joined: Mon Apr 06, 2009 6:26 am

Re: Sprinting

Post by gnounc »

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
ScatterBox
Posts: 50
Joined: Sun Oct 13, 2013 7:53 pm

Re: Sprinting

Post by ScatterBox »

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:
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Sprinting

Post by Cobalt »

Maybe you need to strzone the string.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Sprinting

Post by Spike »

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.
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Sprinting

Post by Cobalt »

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;
ScatterBox
Posts: 50
Joined: Sun Oct 13, 2013 7:53 pm

Re: Sprinting

Post by ScatterBox »

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..
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Sprinting

Post by Cobalt »

Well on the first check, it would be = 1, so then it will see it = 1, and take one, so you get 0
ScatterBox
Posts: 50
Joined: Sun Oct 13, 2013 7:53 pm

Re: Sprinting

Post by ScatterBox »

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. :)
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Re: Sprinting

Post by r00k »

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.
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Sprinting

Post by Cobalt »

[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;
		}
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Re: Sprinting

Post by r00k »

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.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Sprinting

Post by Spike »

looks like it'll spasm if you drop below 10fps.
Post Reply