Inside3D!
     

Difference between PlayerPreThink and PlayerPostThink

 
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming
View previous topic :: View next topic  
Author Message
Nash



Joined: 19 Oct 2007
Posts: 95
Location: Kuala Lumpur, Malaysia

PostPosted: Sat Oct 27, 2007 6:55 pm    Post subject: Difference between PlayerPreThink and PlayerPostThink Reply with quote

I am having trouble understanding the real difference between the two functions. "Run before physics" and "run after physics" doesn't really tell me much.

I'm coding the player movement from scratch and I find that placing my player-related stuff in either function will work. So why is there a need for the two?

Enlighten me, please.
Back to top
View user's profile Send private message Visit poster's website Yahoo Messenger MSN Messenger
scar3crow
Inside3D Staff


Joined: 18 Jan 2005
Posts: 837
Location: Las Vegas, NV

PostPosted: Tue Oct 30, 2007 2:33 am    Post subject: Reply with quote

*bump* for being very useful information request.
Back to top
View user's profile Send private message AIM Address
Urre



Joined: 05 Nov 2004
Posts: 1073
Location: Sweden

PostPosted: Tue Oct 30, 2007 9:05 am    Post subject: Reply with quote

Well... I looked at this thread a couple of times, pondering if I should answer, and decided not to, simply because I don't know as much about the subject that I'd like to. I've asked this myself, and both LordHavoc and FrikaC haven't been able to give proper answers. Maybe it was due to my obnoxiousness, or because there really isn't much more to it than what you said yourself, that they're run before, and after physics. If it means anything to you, then hey, that's great. In the past it didn't make any difference to me, but a couple of times when I needed to set some things that would be used by the physics this frame, I had to put in in Pre, otherwise it'd be lagged one frame all the time. Also, if you want to extract some info after physics have been processed this frame, you'd put it in Post. I'm sure these things sound really abstract and funky, but that's cause they are. Most of the time these things don't matter. The more important thing, that does matter, is that impulses aren't read until after physics, so you can't have ImpulseCommands in Pre. And like you said, you're doing your own physics (I also do, everyone should), in that case Pre or Post won't matter either. A word of advice though: if you're doing your own physics for other things than clients, you should not do it in their think function, since the timing is irregular. Make a list of all physics entities, and scroll through them in StartFrame and apply their physics there, independent from their think. On another note, think functions are run between PlayerPre and PlayerPost.

Hope that helps to some extent, and I hope Frik can hop in to give a better answer.
_________________
Look out for Twigboy
Back to top
View user's profile Send private message Visit poster's website
Nash



Joined: 19 Oct 2007
Posts: 95
Location: Kuala Lumpur, Malaysia

PostPosted: Tue Oct 30, 2007 12:49 pm    Post subject: Reply with quote

Urre, your intepretation does kind of make sense. However I still do not see how one situation would better in which part of the code (other than the obvious ones you mentioned, like the impulses).

Is this similar in concept to graphics programming where you draw to a buffer then flip the page?
Back to top
View user's profile Send private message Visit poster's website Yahoo Messenger MSN Messenger
Sajt



Joined: 16 Oct 2004
Posts: 1026

PostPosted: Tue Oct 30, 2007 3:27 pm    Post subject: Reply with quote

Well, in PreThink you would do something like modify the velocity if it's supposed to take effect this very frame. An example from the id1 qc is jumping. You want the jump's velocity boost to take effect starting this frame, not the next frame.

PostThink is where you can read back the current velocity, including player movement input and jumping from this frame. An example from the id1 qc is falling damage...

If you're using Darkplaces, look into the SV_PlayerPhysics function, which replaces the engine physics in between. It's a part of the DP_SV_PLAYERPHYSICS extensions. The example in DPmod replicates the engine behaviour as far as I know.
_________________
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.


Last edited by Sajt on Tue Oct 30, 2007 3:32 pm; edited 1 time in total
Back to top
View user's profile Send private message
Urre



Joined: 05 Nov 2004
Posts: 1073
Location: Sweden

PostPosted: Tue Oct 30, 2007 3:29 pm    Post subject: Reply with quote

Nash wrote:
Is this similar in concept to graphics programming where you draw to a buffer then flip the page?


Not really. Doublebuffering is to avoid image tearing, it has a very specific use. This is just about a specific order of certain functions. For example, the function called StartFrame is the very first function called each frame, before PlayerPre, before any think functions or whatever.

Let's make it even more abstract. Imagine you have a function called Init(). You also have the functions Calc(), and Result(). They all go through a certain set of globals, in each turn Initializing some values, Calculating the values, and presenting the Result, in that order.

Now the dilemma. Imagine that Calc() is part of a closed library, which you can't see or affect, other than by setting some values in Init(). The only thing you can do is see what happened inside Calc() by checking Result().

That's pretty close to how it works. Replace Init with PlayerPreThink, Calc with SV_FlyMove or whatever it's called in the engine, Result with PlayerPostThink.

Most of the time it doesn't matter, like we said. But I did actually recall one instance where it does matter.

Sometimes you need/want to "attach" entities by simply using setorigin each frame. There are other ways to do it, which are generally better, but let's just roll with it (I don't feel like going into the cases when you *need* to do it by setorigin). If you setorigin something each frame on a client in PlayerPreThink, you will get lagged results, because after you've setorigined your entity on the client, the client does his physics, and when the frame gets rendered, the entity which tries to be attached to the player will constantly pop around after the player because it's lagged one frame. If you instead do it in PlayerPostThink, it will get the new updated origin from physics each frame.

HF
_________________
Look out for Twigboy
Back to top
View user's profile Send private message Visit poster's website
FrikaC
Site Admin


Joined: 08 Oct 2004
Posts: 947

PostPosted: Tue Oct 30, 2007 3:35 pm    Post subject: Reply with quote

I think it's all been covered fairly well.

PreThink is called before the physics are run, the physics are run, velocity and origin are changed based on player input and outside interactions (e.g. hitting the ground/wall/gravity), the .think function is run if nextthink has run out, then PostThink is run. You can then examine the results of all the things that happened during the physics and think.

I often use the two for input capturing like in Prydon Gate or Arcade, as the difference between the origin, velocity and angles between the two depends largely on player input.
Back to top
View user's profile Send private message Send e-mail
LordHavoc



Joined: 05 Nov 2004
Posts: 243
Location: western Oregon, USA

PostPosted: Tue Oct 30, 2007 3:45 pm    Post subject: Reply with quote

the engine does basically this each server frame (boiled down from a lot of code):
Code:
float() RunThink =
{
  local float svtime = time;
  if (self.nextthink <= 0)
    return;
  if (self.nextthink > svtime + frametime)
    return;
  time = self.nextthink;
  if (time < svtime)
    time = svtime;
  self.nextthink = 0;
  self.think();
  time = svtime;
  return !wasfreed();
};

void() ClientMove =
{
    SV_PlayerPhysics();
    PlayerPreThink();
    RunThink();
    MoveEntity();
    PlayerPostThink();
};

void() ServerFrame =
{
  ReadPackets(); // this calls ClientMove() for each move packet

  StartFrame();

  for (clients)
    ClientMove();

  for (entities)
  {
    if (self.movetype == MOVETYPE_PUSH)
    {
      if (self.nextthink < self.ltime + frametime)
        movetime = self.nextthink - self.ltime;
      else
        movetime = frametime;
      if (movetime > 0)
        PushMove();
      RunThink();
    }
    else
    {
      if (RunThink())
        MoveEntity();
    }
  }

  EndFrame();

  SendPackets();
};

That's probably more detail than you wanted.

The basic rule is:
SV_PlayerPhysics, PlayerPreThink, think, touch (due to movement), PlayerPostThink

However as shown above, DarkPlaces calls the client physics for each packet received from a player if they are using cl_movement 1, such packets may have a lower frametime value than the normal physics (depending on cl_netinputpacketsperserverpacket cvar) and may be called several times per frame, if this option is on, frametime will be 0 in the later ClientMove() call.

The calls with frametime 0 are to ensure that your PlayerPreThink/PlayerPostThink will be called every server frame even if the client is lagging badly (jerky movement, etc).
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2004 phpBB Group