TraceLine: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
m (otherlang2'd)
(Re-wrote most of the page to be more Wiki-like and clearer about how to use traces. Will add more detailed TraceLine stuff tomorrow.)
Line 6: Line 6:
The line starts from the barrel (Point A) and ends at an arbitrary spot 8000 or more units in front of it. If it hits the world (a wall, a box, something solid) the code tells it to display an effect (for example, a smoke puff coming from the wall, or sparks from metal...). If it hits a player, it'll do damage.
The line starts from the barrel (Point A) and ends at an arbitrary spot 8000 or more units in front of it. If it hits the world (a wall, a box, something solid) the code tells it to display an effect (for example, a smoke puff coming from the wall, or sparks from metal...). If it hits a player, it'll do damage.


== Getting the origin point and end point ==
== TraceLines ==
To create a simple trace, call '''UTIL_TraceLine( const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask, const IHandleEntity *ignore, int collisionGroup, trace_t *ptr )'''.
Where:
:*'''&vecAbsStart''' is a Vector such as GetAbsOrigin(); The "const" part of the function tells you that the function will not modify the variable.
:*'''&vecAbsEnd''' is a Vector such as GetAbsOrigin() + (vecForward * 192); Tracing 192 units in front of the Absolute Origin of the caller.
:*'''mask''' is a bitmask of valid MASK_ types to hit. See: [[MASK_Types]]. Since it is a bitmask, you can use multiple types and as such MASK_VISIBLE | MASK_SHOT will count as an acceptable mask.
:*'''<nowiki>*</nowiki>ignore''' is a IHandleEntity, or a pointer to a CBaseEntity. It is common to use "this" as to keep the TraceLine from hitting the object you're tracing from.
:*'''collisionGroup''' is a COLLISION_ type that the TraceLine is. See: [[COLLISION_Types]]Certain COLLISION_ types will not 'collide' with each other. Use COLLISION_GROUP_NONE to have your TraceLine hit everything.
:*'''<nowiki>*</nowiki>tr''' is a trace_t class. The results of the UTIL_TraceLine are written to this.


There are a few more functions we need to know to be able to start tracing from different start points. Usually traces involve players, or more generally, entities.
== Common vecAbsStart and vecAbsEnd Methods ==
'''vecAbsStart'''
:*'''GetAbsOrigin()''': This returns a Vector of the entity's Origin in World Space coordinates. For Player's this is their feet, and for most entities :*it is either the center or bottom of the model.
{{Note|This is visible in [[HLMV]] under the '''Bones''' tab, by checking '''Highlight Bone''' of the first bone.}}
{{todo|Is this accurate?}}
:*'''Weapon_ShootPosition()''': Point in World Space where the player's weapon's shot originates.
{{Note|Only available in the Player class}}
:*'''GetAttachment( int attachment, Vector &origin, QAngle &angles );''': Returns the position of an attachment point. Used in conjunction with...
:*'''LookupAttachment( char *pAttachmentName )''': The name (as a char*) of the attachment point you wish to use.


'''GetAbsOrigin()''': This point's origin is at the "feet" of the player. For other model entities, it generally is either at the "bottom" of the model, or sometimes at the center.


'''Weapon_ShootPosition()''': Only for the player class. This is the point where the weapon's shots originate. This position is approximate. If you need to display effects (for example, tracers, which are visible lines going outwards from a barrel) you'll need to use:
'''vecAbsEnd'''
:*'''EyeAngles()''': Returns a QAngle of the angle the player is currently looking at.
{{Note|Only avaible in the Player class}}
:*'''GetAbsAngles()''': Returns a QAngle of the angle the player is facing at. Often does not match the z rotation of EyeAngles, as the player only turns in 15-30 degree increments to avoid jitter and increase realism.


'''GetAttachment() and LookupAttachment()''': GetAttachment() returns the position of an attachment point on a model entity. An attachment is a point on the model, defined by the modeler in the QC file. You can see a list of attachment points for models in [[HLMV]]. The list starts at zero. LookupAttachment() takes a char* (the name of the attachment) and returns the number of the corresponding attachment, or -1 if it wasn't found.


Now that we have "point A" we need "point B".
'''QAngles'''
QAngles is a Vector of Angles -- That is to say it stores rotation in X, Y and Z.
Often, we will want to turn a QAngle into a vector. To do this, we can use AngleVectors(), which takes a QAngle and can return 1 or 3 pointers to Vectors. For example:


The Source SDK provides powerful math functions to convert angles into vectors. What it basically does is turn, say, "45 pitch, 90 yaw, 0 roll" into a vector pointing left and up 45 degrees. The function is called AngleVectors(), the relevant overloads take a QAngle, as well as either one or three pointers to Vectors as parameters. The resulting vectors are forward, which is the direction towards where the angles are pointing, up, which is forward rotated 90 degrees "up", and right, which is forward rotated 90 degrees "right".
<source lang=cpp>
Vector vecForward, vecRight, vecUp;
AngleVectors( EyeAngles(), &vecForward, &vecRight, &vecUp); </source>
Will return a vector facing to the "front", a vector to the "right", and a vector to the "up" of your QAngle. You can then use vecForwards to get where EyeAngles() is looking at.


You use it with the following functions:
To get a point infront of your 'end' position, you can do:
<source lang=cpp>Vector vecForward;
AngleVectors( pPlayer->EyeAngles(), &vecForward ) //Assign vecForward a vector facing the EyeAngles
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + ( vecForward * MAX_TRACE_LENGTH, ...)
</source>


'''EyeAngles()''': For players only. This returns their current view angles.
'''MAX_TRACE_LENGTH''' is the longest trace you can have, or ~56,754 units.
{{Note|This is a trace from one corner to the opposite corner of the world, assuming a cubic world.}}


'''GetAbsAngles()''': For players, this is the direction that their character model is facing (watch out, this is usually out of sync with the EyeAngles, because the player model only rotates by 15 or 30 degrees increment so it doesn't look jittery). For other entities, this is their orientation.
== trace_t &tr ==
'''trace_t''' is a special class that stores the results of our TraceLine. It has the following values.
:*'''startpos''': The start of the TraceLine as a Vector.
:*'''endpos''': The end of the TraceLine as a Vector.
:*'''plane''': Struct. Contains the information about the plane you hit.
{{Warning|plane can be invalid! If the trace never leaves a solid, there will be no valid plane.}}
:*'''fraction''': How far the trace went before hitting something. 0-1, 1.0 means it didn't hit anything.
:*'''allsolid''': Boolean. If true, the plane is not valid. (You never left the solid you started in)
:*'''startsolid''': Boolean. If true, the initial point was in a solid area.
:*'''hitbox''': Returns the int of the hitbox hit.
:*'''m_pEnt''': The entity hit. Can be invalid.
{{Tip| Use "if ( tr.m_pEnt )" to validate it!}}
:*'''DidHit()''': Boolean. Returns true if it hit something.
:*'''IsPlayer()''': Boolean. Returns true if the entity hit was a player.
:*'''IsNPC()''': Boolean. Returns true if the entity hit was an NPC.
:*'''GetClassname()''': Returns the name of the entity hit.
{{Note|Returns strange results on the client if the entity is not predicted.}}
:*'''GetTeamNumber()''': Returns the team number that the entity is on. Returns UNASSIGNED for entities not on a team.
:*'''contents''': Contents on the other side of the surface it hit
{{Todo|What exactly is this? Has properties such as tr.surface.name, tr.surface.flags, tr.surface.surfaceProps, etc.}}


== Examples ==
Trace from the player's eyes to the max trace distance.


Now that we know how to fetch point A, as well as the direction, it's a matter of simple math to figure out that point B is "point a + direction * X," where X is how far you want to trace.
<source lang=cpp>
 
For things that NEED to hit, such as a bullet trace, X is very high (often <code>MAX_TRACE_LENGTH</code>, defined in <code>worldsize.h</code>), so that we will almost always hit something (a wall somewhere on the map, the skybox, a distant player, et cetera).
 
== The trace itself ==
 
There are two lists or parameters which you can pass to a TraceLine, one of them involves a TraceFilter, which i have never used yet, so i will be talking about the second one:
 
'''UTIL_TraceLine( const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask, const IHandleEntity *ignore, int collisionGroup, trace_t *ptr )'''
 
Let's look at it parameter by parameter:
 
'''const Vector& vecAbsStart''': point A. The "const" here tells you that the vector will not be modified by the function.
 
'''const Vector& vecAbsEnd''': point B. This will not be modified by the function either.
 
'''unsigned int mask''': This one is interesting. This int is a [http://www.vipan.com/htdocs/bitwisehelp.html bitmask] specifying what you want the trace to hit. The list of definitions for MASKs is in '''bspflags.h'''. The ones I personally find myself most commonly using are MASK_SHOT, MASK_SOLID_BRUSHONLY and MASK_SOLID.
You could also make your own MASKs if you wanted to, by putting together different CONTENTS_ flags.
 
'''const IHandleEntity *ignore''': This is the entity you want to ignore. For this you can pass a pointer to CBaseEntity, the result is the same anyways. I usually use the "this" pointer if i'm tracing from inside my model entity, because it is likely that i will hit myself "on the way out" of my own box. If your traces keep hitting near your start point, you'll want to use the ignore entity.
 
'''int collisionGroup''': This specifies which collision group you want the trace to be, the constants are defined in const.h. Certain groups will only hit certain other groups. Setting this to COLLISION_GROUP_NONE should make the trace hit everything (this is from my own experience).
 
'''trace_t *ptr''': This is the traceline class (I know it looks like it's a structure but it really is a class!) that you need to pass to be filled. This is basically your "results sheet" of what the trace has done. Just declare it like so: "trace_t tr;" and pass "&tr"...
 
== Analysing and using the results ==
 
Ok, so you've run the trace, now what?!
Well, just above I said we had to pass our trace_t. Now, this is what we are going to use to see how the trace went, and possibly extract data out of it. I'm not going to go over each function and variable of trace_t, but only over those I most commonly use. Go to the definition of trace_t or CGameTrace if you want more.
 
'''startpos, endpos''': startpos is point A, endpos is where the trace hit something and stopped. In our example, this is where the bullet hit the wall, so to speak. Watch out, this is exactly where you hit, not a few units in front, not a few units in the back, so if you need to say, spawn something, make sure you have room to do so.
 
'''fraction''': This is a float between 0 and 1 which is the percentage of distance between point A and point B that was "traveled" before the trace hit something. This is very useful for many reasons, the main one being that if tr.fraction == 1.0, then your trace didn't hit anything, and if tr.fraction != 1.0, your trace did hit something.
 
'''startsolid and allsolid''': startsolid is a boolean that says whether you were "inside" something (usually the world) when the trace started (point A), allsolid tells you if you ever got out of the "inside" or not.
 
'''plane''': This is information about the surface you hit. Watch out, if allsolid is true, then that means the plane is not valid. This is in itself a structure which has lots of information about the plane you hit. I mostly use plane.normal here, which is the surface normal of the plane you hit, which is very useful for many things, the most common one is "pulling out" from the place you hit by using endpos + plane.normal* how many units you want to come out.
 
'''surface''': This has more info about the plane you hit, including the name of the surface, props (never used that, but I believe they are defined in the material) and most important: flags. You can see a list of surface flags in '''bspflags.h'''. The one I use primarily is SURF_SKY, which means the surface is a skybox. It's useful for many things, for example, you don't want to emit a smoke puff from a gun shot on the skybox, well this is how you would check whether you hit a skybox or not.
 
'''m_pEnt''': Ah, this is very important. This is the entity you hit! Usually if it's the world this will be NULL, or it will also be NULL if you didn't hit anything, so make sure you check if it is valid before using it. This can be used for many things, but usually the most important thing you want to know from the entity you hit is: What is it?
Well CBaseEntity has a few functions which help you out with that:
 
'''IsXXXX()''': IsPlayer(), IsNPC(), etc. All those are defined in CBaseEntity.h and then overriden by derived classes.
 
'''GetClassname()''': Returns the name of the entity which you hit. Now on the server, this most usually is the name that is passed in LINK_ENTITY_TO_CLASS(), but on the Client, this name only shows if you made the entity predictable! Otherwise it will return the name of the class, which looks like "class C_BaseMyClass" for example. If i'm not sure of the name of certain entities, i use Warning() to output the classname of all entities surrounding me (I actually made a console Command that does it client-side) and then i know what the correct string is.
 
'''GetTeamNumber()''': This is the last important one: This is the number of the team in which this entity is. For non-players, this usually is UNASSIGNED, unless if the entities are related to a player (weapons, grenades, etc etc).
 
== Conclusion ==
 
Well this concludes this non-exhaustive article about TraceLines. I hope you've enjoyed it, if you have comments leave them on the discussion page and i'll try to reply in a timely fashion.
 
== Example ==
 
This will traceline from the players eyes to the furthest trace distance possible ([[MAX_TRACE_LENGTH]]) trying to hit NPCs on the way.
 
<pre>
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if ( !pPlayer )
  return; //Always validate a pointer


// set up the vectors and traceline
//Create our trace_t class to hold the end result
trace_t tr;
trace_t tr;
Vector vecStart, vecStop, vecDir;


// get the angles
//Create Vectors for the start, stop, and direction
AngleVectors( pPlayer->EyeAngles( ), &vecDir );
Vector vecAbsStart, vecAbsEnd, vecDir;


// get the vectors
//Take the Player's EyeAngles and turn it into a direction
vecStart = pPlayer->EyePosition();
AngleVectors( pPlayer->EyeAngles(), &vecDir );
vecStop = vecStart + vecDir * MAX_TRACE_LENGTH;


// do the traceline
//Get the Start/End
UTIL_TraceLine( vecStart, vecStop, MASK_ALL, pPlayer, COLLISION_GROUP_NPC, &tr );
vecAbsStart = pPlayer->EyePosition();
vecAbsEnd = vecAbsStart + (vecDir * MAX_TRACE_LENGTH);


// check to see if we hit a NPC
//Do the TraceLine, and write our results to our trace_t class, tr.
UTIL_TraceLine( vecAbsStart, vecAbsEnd, MASK_ALL, pPlayer, COLLISION_GROUP_NONE, &tr );
 
//Do something with the end results
if ( tr.m_pEnt )
if ( tr.m_pEnt )
{
{
if ( tr.m_pEnt->IsNPC() )
    if ( tr.m_pEnt->IsNPC() )
{
    {
// yay we hit a NPC!
        Msg("TraceLine hit an NPC!\n");
}
    }
    if ( tr.m_pEnt->IsPlayer() )
    {
        Msg("TraceLine hit a Player!\n");
    }
}
}
</pre>
</source>
 
{{todo|More Examples!}}


[[Category:Programming]]
[[Category:Programming]]
[[Category:UTIL]]

Revision as of 01:31, 7 April 2011

Template:Otherlang2 Tracing is the process of going from a point A towards another point B, and finding out the first thing that we "hit" on our way from A to B (TraceLine). The most common example for the use of TraceLines would be weapons. Weapons use TraceLines to determine where they hit (and if they hit someone).

The line starts from the barrel (Point A) and ends at an arbitrary spot 8000 or more units in front of it. If it hits the world (a wall, a box, something solid) the code tells it to display an effect (for example, a smoke puff coming from the wall, or sparks from metal...). If it hits a player, it'll do damage.

TraceLines

To create a simple trace, call UTIL_TraceLine( const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask, const IHandleEntity *ignore, int collisionGroup, trace_t *ptr ). Where:

  • &vecAbsStart is a Vector such as GetAbsOrigin(); The "const" part of the function tells you that the function will not modify the variable.
  • &vecAbsEnd is a Vector such as GetAbsOrigin() + (vecForward * 192); Tracing 192 units in front of the Absolute Origin of the caller.
  • mask is a bitmask of valid MASK_ types to hit. See: MASK_Types. Since it is a bitmask, you can use multiple types and as such MASK_VISIBLE | MASK_SHOT will count as an acceptable mask.
  • *ignore is a IHandleEntity, or a pointer to a CBaseEntity. It is common to use "this" as to keep the TraceLine from hitting the object you're tracing from.
  • collisionGroup is a COLLISION_ type that the TraceLine is. See: COLLISION_TypesCertain COLLISION_ types will not 'collide' with each other. Use COLLISION_GROUP_NONE to have your TraceLine hit everything.
  • *tr is a trace_t class. The results of the UTIL_TraceLine are written to this.

Common vecAbsStart and vecAbsEnd Methods

vecAbsStart

  • GetAbsOrigin(): This returns a Vector of the entity's Origin in World Space coordinates. For Player's this is their feet, and for most entities :*it is either the center or bottom of the model.
Note.pngNote:This is visible in HLMV under the Bones tab, by checking Highlight Bone of the first bone.
Todo: Is this accurate?
  • Weapon_ShootPosition(): Point in World Space where the player's weapon's shot originates.
Note.pngNote:Only available in the Player class
  • GetAttachment( int attachment, Vector &origin, QAngle &angles );: Returns the position of an attachment point. Used in conjunction with...
  • LookupAttachment( char *pAttachmentName ): The name (as a char*) of the attachment point you wish to use.


vecAbsEnd

  • EyeAngles(): Returns a QAngle of the angle the player is currently looking at.
Note.pngNote:Only avaible in the Player class
  • GetAbsAngles(): Returns a QAngle of the angle the player is facing at. Often does not match the z rotation of EyeAngles, as the player only turns in 15-30 degree increments to avoid jitter and increase realism.


QAngles QAngles is a Vector of Angles -- That is to say it stores rotation in X, Y and Z. Often, we will want to turn a QAngle into a vector. To do this, we can use AngleVectors(), which takes a QAngle and can return 1 or 3 pointers to Vectors. For example:

Vector vecForward, vecRight, vecUp;
AngleVectors( EyeAngles(), &vecForward, &vecRight, &vecUp);

Will return a vector facing to the "front", a vector to the "right", and a vector to the "up" of your QAngle. You can then use vecForwards to get where EyeAngles() is looking at.

To get a point infront of your 'end' position, you can do:

Vector vecForward;
AngleVectors( pPlayer->EyeAngles(), &vecForward ) //Assign vecForward a vector facing the EyeAngles
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + ( vecForward * MAX_TRACE_LENGTH, ...)

MAX_TRACE_LENGTH is the longest trace you can have, or ~56,754 units.

Note.pngNote:This is a trace from one corner to the opposite corner of the world, assuming a cubic world.

trace_t &tr

trace_t is a special class that stores the results of our TraceLine. It has the following values.

  • startpos: The start of the TraceLine as a Vector.
  • endpos: The end of the TraceLine as a Vector.
  • plane: Struct. Contains the information about the plane you hit.
Warning.pngWarning:plane can be invalid! If the trace never leaves a solid, there will be no valid plane.
  • fraction: How far the trace went before hitting something. 0-1, 1.0 means it didn't hit anything.
  • allsolid: Boolean. If true, the plane is not valid. (You never left the solid you started in)
  • startsolid: Boolean. If true, the initial point was in a solid area.
  • hitbox: Returns the int of the hitbox hit.
  • m_pEnt: The entity hit. Can be invalid.
Tip.pngTip: Use "if ( tr.m_pEnt )" to validate it!
  • DidHit(): Boolean. Returns true if it hit something.
  • IsPlayer(): Boolean. Returns true if the entity hit was a player.
  • IsNPC(): Boolean. Returns true if the entity hit was an NPC.
  • GetClassname(): Returns the name of the entity hit.
Note.pngNote:Returns strange results on the client if the entity is not predicted.
  • GetTeamNumber(): Returns the team number that the entity is on. Returns UNASSIGNED for entities not on a team.
  • contents: Contents on the other side of the surface it hit
Todo: What exactly is this? Has properties such as tr.surface.name, tr.surface.flags, tr.surface.surfaceProps, etc.

Examples

Trace from the player's eyes to the max trace distance.

CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if ( !pPlayer )
   return; //Always validate a pointer

//Create our trace_t class to hold the end result
trace_t tr;

//Create Vectors for the start, stop, and direction
Vector vecAbsStart, vecAbsEnd, vecDir;

//Take the Player's EyeAngles and turn it into a direction
AngleVectors( pPlayer->EyeAngles(), &vecDir );

//Get the Start/End
vecAbsStart = pPlayer->EyePosition();
vecAbsEnd = vecAbsStart + (vecDir * MAX_TRACE_LENGTH);

//Do the TraceLine, and write our results to our trace_t class, tr.
UTIL_TraceLine( vecAbsStart, vecAbsEnd, MASK_ALL, pPlayer, COLLISION_GROUP_NONE, &tr );

//Do something with the end results
if ( tr.m_pEnt )
{
    if ( tr.m_pEnt->IsNPC() )
    {
        Msg("TraceLine hit an NPC!\n");
    }
    if ( tr.m_pEnt->IsPlayer() )
    {
        Msg("TraceLine hit a Player!\n");
    }
}
Todo: More Examples!