TraceLine: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
m (minor typographical and grammatical corrections up to "IsXXXX()", link for bitmasks)
mNo edit summary
Line 1: Line 1:
[[Category:Programming]]
[[Category:Programming]]
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?''' ==
== '''What is tracing?''' ==



Revision as of 11:24, 16 July 2005

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 (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

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 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:

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".

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".

You use it with the following functions:

EyeAngles(): For players only. This returns 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 other entities, this is their orientation.


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.

For things that NEED to hit, such as a bullet trace, X is very high, 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 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 will not be modified by the function either.

unsigned int mask: This one is interesting. This int is a 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 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 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, 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 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 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).

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.

Imperio59.