For a more in-depth descriptions of specific entities, refer to
2.1. Basic properties¶
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.
All entities in Half-Life has an associated movement type, or movetype. These are all the possible movetypes in Half-Life:
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_Nonehandles the physics of this movetype. This function simply calls the
Thinkfunction associated with the entity and returns.
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).
This is the primary movetype used by monsters, analogous to
MOVETYPE_WALKfor the player.
SV_Physics_Stephandles this movetype, and it computes water buoyancy physics, gravity by Euler’s method (see Gravity), friction, position update, and also runs the entity’s
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_Tossis responsible of this movetype. It runs the
Thinkfunction, perform other checks, and compute position update and collisions.
This movetype is used by gibs, dead monsters, and certain pickup items such as the battery and suit. Similar to
SV_Physics_Tossfunction handles this movetype, though with gravity.
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_Pusherruns the physics for this movetype, which calls either
SV_PushMoveat some point, and calls the
Thinkfunction of the entity.
Entities with this movetype does not experience gravity, and does not clip with any other entity.
SV_Physics_Noclipis responsible of the physics, which consists of running the
Thinkfunction, computing position and angle update, and
This movetype is not found to be used by any entity in vanilla Half-Life. The
SV_Physics_Tossfunction is responsible of its physics.
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
SV_Physics_Tossis called for this movetype, but with the bounce coefficient (see Collision) computed by .
MOVETYPE_FLYMISSILE, this movetype does not seem to be used by any entity in the unmodded Half-Life.
SV_Physics_Tossis also called for this movetype, and the bounce coefficient (see Collision) is set to be , independent of entity gravity.
Entities of this movetype tracks the movement of the entity given by
pev->aiment. For example, the
CBasePlayerItemclass, 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_Followruns its physics code, and consists of calling
Thinkand copying the
aimentorigin and angles, along with
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_PUSHSTEPuses a slightly different way to collide with other entities.
This movetype does not seem to be used.
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.
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 .
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
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.
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
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.
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.