2. Entities

For a more in-depth descriptions of specific entities, refer to

2.1. Basic properties

2.2. Camera

Not all entities can see, but for those that do, the eyes are the windows to the soul. In the Half-Life universe, an entity is called the camera or the view. The camera is located at a fixed position relative to the entity’s origin, at least at server side. At client side, the player’s camera position is slightly complicated by view bobbing if enabled.

2.3. Movement

All entities in Half-Life has an associated movement type, or movetype. These are all the possible movetypes in Half-Life:

MOVETYPE_NONE

The entity never moves and not subjected to gravity. Examples of entities using this movetype are triggers, RPG laser spots, env_explosion, etc. In the engine code, SV_Physics_None handles the physics of this movetype. This function simply calls the Think function associated with the entity and returns.

MOVETYPE_WALK

This movetype is only used by the player and gives rise to the familiar air and ground movement physics of the player (see Player movement basics).

MOVETYPE_STEP

This is the primary movetype used by monsters, analogous to MOVETYPE_WALK for the player. SV_Physics_Step handles this movetype, and it computes water buoyancy physics, gravity by Euler’s method (see Gravity), friction, position update, and also runs the entity’s Think function.

MOVETYPE_FLY

This movetype is used by entities that do not experience gravity, but has collision. For example, the Nihilanth (Nihilanth) uses this movetype, so does the tripmine (Tripmine), crossbow bolt, and many others. Notably, this movetype is also used by the player on a ladder (Ladders). The SV_Physics_Toss is responsible of this movetype. It runs the Think function, perform other checks, and compute position update and collisions.

MOVETYPE_TOSS

This movetype is used by gibs, dead monsters, and certain pickup items such as the battery and suit. Similar to MOVETYPE_FLY, the SV_Physics_Toss function handles this movetype, though with gravity.

MOVETYPE_PUSH

This movetype is used by entities that can push and crush other entities, but does not clip to worldspawn. These are buttons, doors, func_breakable (func_breakable, but not func_pushable), func_rotating, func_wall, func_pendulum, etc. SV_Physics_Pusher runs the physics for this movetype, which calls either SV_PushRotate or SV_PushMove at some point, and calls the Think function of the entity.

MOVETYPE_NOCLIP

Entities with this movetype does not experience gravity, and does not clip with any other entity. SV_Physics_Noclip is responsible of the physics, which consists of running the Think function, computing position and angle update, and SV_LinkEdict.

MOVETYPE_FLYMISSILE

This movetype is not found to be used by any entity in vanilla Half-Life. The SV_Physics_Toss function is responsible of its physics.

MOVETYPE_BOUNCE

This movetype is used by entities that can bounce off other entities. A prominent example is the hand grenade (Hand grenade), but satchel charges (Satchel charge), MP5 greandes, and others use this movetype as well. Similar to MOVETYPE_TOSS, SV_Physics_Toss is called for this movetype, but with the bounce coefficient (see Collision) computed by .

MOVETYPE_BOUNCEMISSILE

Just like MOVETYPE_FLYMISSILE, this movetype does not seem to be used by any entity in the unmodded Half-Life. SV_Physics_Toss is also called for this movetype, and the bounce coefficient (see Collision) is set to be , independent of entity gravity.

MOVETYPE_FOLLOW

Entities of this movetype tracks the movement of the entity given by pev->aiment. For example, the CBasePlayerItem class, subclassed by all player weapons, follows the player and is set to this movetype. Entities of this movetype does not experience gravity or collision. SV_Physics_Follow runs its physics code, and consists of calling Think and copying the aiment origin and angles, along with SV_LinkEdict.

MOVETYPE_PUSHSTEP

This entity seems to only be used by func_pushable. The physics of this movetype is very similar to that of MOVETYPE_PUSH, except that MOVETYPE_PUSHSTEP uses a slightly different way to collide with other entities.

TODO

MOVETYPE_COMPOUND

This movetype does not seem to be used.

2.4. Gravity

Some entities in Half-Life experience gravity. A func_pushable box, when pushed off an edge, falls to the ground. However, the game computes gravity for these entities in a way that differs from the gravity for the player (see Gravity). Entity gravity is integrated using the simplest form of the Euler’s method. Suppose an entity has vertical velocity and position at the start of a frame. Provided this entity’s movetype is set to experience gravity, the new vertical velocity and position after one frame are given by

where is the effective gravitational acceleration and is the game frame rate (see Frame rate). Readers familiar with Newtonian mechanics will note the fact that differs from what one would expect. A simple rewriting yields

where the terms in the brackets match the prediction from Newtonian mechanics. Indeed, after frames of gravity, we can write

Let be the Newtonian prediction after frames of gravity. Then we first observe that . In addition, the difference is directly proportional to the frame time and inversely proportional to the frame rate . The lower the frame rate, the smaller becomes relative to , which manifests as lower vertical positions in the game. This may be understood in many different ways. For example, a falling entity would reach the ground sooner. An MP5 grenade would travel a shorter horizontal distance before landing.

2.5. Friction

Entities of movetype MOVETYPE_STEP experience ground friction in a similar way as the player. The friction is similar to what is given in Ground friction, except with edgefriction .

2.6. Collision

Many entities in Half-Life collide with one another. The velocity of the colliding entity usually changes as a result, while the position and velocity of the entity receiving the collision usually stay constant, countering real world Newtonian physics. The process of changing the velocity is usually referred to as velocity clipping. Collision is one of the most common events in Half-Life, so it is worthwhile to study its physics.

Collisions occur in the position update step of an entity. The player entity’s position update is described in Position update. A collision is detected by performing a player trace and checking if the trace strikes a plane. Let be the plane normal and let be the velocity at the instant of collision. Let be the bounce coefficient which, in general, depends on sv_bounce (denoted as ) and (see Friction). The bounce coefficient controls how the velocity is reflected akin to a light ray. If is the velocity resulting from the collision, then the general collision equation (GCE) can be written as

(2.1)

Before we proceed, we must point out that this equation may be applied multiple times per frame when computing the position update for an entity.

_images/collision-overbounce.svg

Fig. 2.1. Depiction of a general case of collision, where the player collides with plane with normal at with velocity .

Collisions in the general case is depicted in Fig. 2.1.. The point at which collision occurs is , and let the arrow the velocity vector . Then, the length of represents the dot product , and is a projection of onto the line , which is parallel to the plane normal. In general, this dot product is scaled by , causing the final velocity vector to point out of the plane, shown by . If instead, then would be the final vector.

In most cases, players have because in the default Half-Life settings and if there is no func_friction that modifies it (see func_friction). The value of and its dependence on and for the player is described in Position update. The case of is more common for other entities. For example, snarks have and . In general, if the movement type of an entity is designated as MOVETYPE_BOUNCE, then .

Care must be taken when . To understand why, we first observe that , because otherwise there would not be any collision events. With

we see that if then the angle between the resultant velocity and the plane normal is obtuse. As a result, collisions will occur indefinitely with an increasing . To prevent this, the game utilises a safeguard immediately after the line tracing process in the respective FlyMove functions to set .

Hence, assuming we employ the following trick to quickly find : write and expanding each in the RHS to give

where is the smallest angle between and confined to . Observe that the resulting speed is strictly increasing with respect to in . In fact, the curve of resultant speed against is hyperbolic provided and . When does equal zero, the resultant speed will be linear in like so:

Again, this result assumes . On the other hand, for the very common case of we have

Observe that the resultant velocity is always parallel to the plane, as one can verify that is indeed true.

2.6.1. Speed preserving circular walls

In Half-Life, we can sometimes find concave walls made out of multiple planes that approximate a circular arc. Examples can be found in some Office Complex maps such as the wall shown in Fig. 2.2.. Circular walls can be a blessing for speedrunners because they allow making sharp turns without losing too much speed. In fact, if the number of planes increases, the approximation will improve, and so the speed will be better preserved.

_images/speed-preserving-c1a2.jpg

Fig. 2.2. An example of a “speed preserving” wall in the Office Complex map of c1a2, which is made up of multiple planes to approximate an arc.

_images/circular-wall.svg

Let be the number of walls and let be the angle subtended by the arc joining the midpoints of every wall. For example, with the first and the last walls will be perpendicular, and with they will be opposite and parallel instead. Let be the velocity immediately after colliding with the -th wall, and assuming is parallel to and coincident with the first wall. Assume also that , which means that the angle between adjacent planes cannot be acute. If the velocity does not change due to other external factors throughout the collisions, then

The general equation at frame is simply

It can be verified that

This demonstrates the speed preserving property of circular walls. Observe also that the final speed is completely independent of the radius of the arc. Perfectly circular walls are impossible in Half-Life due to the inherent limitations in the map format, so some amount of speed loss is unavoidable. Nevertheless, even with and we can still preserve half of the original speed.

This is somewhat analogous to uniform circular motion in the real world. In the real world, an object rotating around a point in a circular path experiences centripetal acceleration with constant angular speed . The velocity of the rotating body changes its direction continuously to keep up with the circular arc, but crucially, the magnitude or speed remains constant throughout. In theory, there is no restriction on how small the radius of curvature can be.