From Valve Developer Community
Revision as of 17:00, 2 July 2005 by Imperio59 (talk | contribs) (A brief overview of TraceLines and their uses.)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Hi, One of the basic things a MOD programmer for Half Life 2 must be able to do with ease is use TraceLines correctly. I'm going to attempt to give you a few basics about this here, as well as some tips.

What is tracing?

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. 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 (some smoke puff coming from the wall from example, or sparks from the metal...). If it hits a player for example, it'll do damage.

Getting The Origin Point and End Point

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. 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 from where the weapon shoots. This position is actually approximative. If you need to display effects (Tracers for examples, wich are visual lines going from a barrel outwards) you'll need to use: GetAttachment() and LookupAttachment(): GetAttachment() returns the position vector of an atachment point on a model entity. An attachment is a point in the model, defined by the modeller in the QC file. You can see a list of attachment points for models in ModelViewer. The list starts at 0, and so on. LookupAttachment() takes a char* (the name of the attachment) and returns either the number of the corresponding attachment or -1 if it didn't find it.

Now that we have "point A" we need "point B".

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(), it takes an angle as a parameter as well as one or 3 pointers to vectors. The resulting vectors are forward, wich is the direction towards where the angles are "pointing", up, wich is forward rotated 90 degrees "up", and right wich is forward rotated 90 degrees "right".

You use it with the following functions:

EyeAngles(): For players only. This is their current view angles.

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 general entities, this is their orientation.

So now that we know how to fetch point A, the direction, it's a matter of simple math to figure out that point B is simply point a + direction * X, where X is how far you want to trace.

Now for things that NEED to hit, say a bullet shot, X is very high, so that we will almost always hit something (a wall somwhere on the map, the skybox, a player very far away, etc. But for certain traces, where you need to see for example if you have enough space between a player and a wall for example, you can put the number to something lower.

== The Trace Itself ==

There are two lists or parameters wich you can pass to a TraceLine, one of them involves a TraceFilter, wich 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 is also not going to be modified by the function.

unsigned int mask: This one is interesting. This int here is a description of what you "allow" 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 you 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 is wich collision group you want the trace to be, the constants are defined in const.h . Certain groups will only hit certain other groups. Putting this as 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 ran 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 spawn it there. fraction: This is a float between 0 and 1 wich 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 reason, 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 wether 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 wich has lots of info on the plane you hit (read the surface you hit). I mostly use plane.normal here, wich is the normal of the plane you hit, wich 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 primarly use is SURF_SKY, wich 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 wether 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 wich helps you out for 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 wich 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 predicatble! Otherwise it will return the name of the class, wich 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 wich this entity is. For non-players, this usually is UNASSIGNED, unless if the entities are related to a player (weapons, grenades, etc etc).

Well this concludes this non-exhaustive article about TraceLines. I hope you've enjoyed it, if you ave comments leave them on the discussion page and i'll try to reply in a timely fashion.