c_move(ENTITY* entity,VECTOR* reldist,VECTOR* absdist,var mode)
Moves an entity over a certain distance while performing collision detection,
triggering collision events, and gliding along obstacles.
The first vector reldist gives a relative distance
and direction in rotated entity coordinates, i.e. the direction that the entity
is facing. The second vector absdist gives an absolute
distance and direction in world coordinates. The resulting movement is a combination
of both distances. Normally the first vector is used for the propelling speed
of the entity, and the second one for external forces, like gravity and drift.
To zero one of these vectors, the predefined nullvector
can be given.
The function returns the amount of distance covered. If the entity could not
move at all due to being blocked by obstacles, it returns a negative value or
0. If collision events are enabled, they are triggered for the entity as well
as for the obstacle. During a collision event, several predefined variables
are set to indicate the type of collision (see below). They can be evaluated
in the event function for setting collision or ricocheting behavior of any kind.
||Entity to be moved.
||Move distance vector in entity coordinates
||Move distance vector in world coordinates
||Collision mode, see below.
The following mode flags can be combined:
you entity on collision detection (see remark).
||A7.10 Ignores all
entities with FLAG2 set.
Ignores all passable blocks and entities.
||Ignores all level blocks and terrains.
||Ignores all map entities.
||Ignores all models.
||Ignores all sprites.
||Ignores all entities with lower push or
and triggers the EVENT_PUSH collision
event on those entities.
For ignoring several groups, call c_ignore before c_move.
||Ignores the content of the move origin. The function is
faster, but water entities (see above) are not detected.
||Enables EVENT_TRIGGER during
the motion. Can be slow.
||A7.10 Triggers the EVENT_PUSH collision
event on the hit entity, regardless of the push
value. If the event function returns
1, the hit entity is considered an obstacle; if the event function returns
0, it is ignored. This way individual collision groups can be defined.
||A7.10 Enables EVENT_SHOOT triggering of the hit entity, and prevents all other collision events. This flag lets c_move behave similar to c_trace.
||A7.10 Enables EVENT_SONAR triggering of the hit entity, and prevents all other collision events. This flag lets c_move behave similar to c_trace.
|| Uses a polygonal hull of all target entities even if their POLYGON flag is not set.
||Glides along walls and entities on impact.
||Exclude entity groups from collision detection.
||Friction factor for gliding along walls.
||Maximum step height (OBB only).
||Disables gliding up slopes (OBB only).
| > 0
Entity could not be moved, f.i. due to obstacles.
entity.x, y, z
||Position of one of the the collision contacts (there can be several).
||Normal vector of one of the colliding surfaces.
||The HIT_TARGET macro is nonzero when something was hit.
||Type of event when one was triggered.
The following collision events can be triggered by c_move collisions. Moving entity: EVENT_BLOCK on collisions with the level, and EVENT_ENTITY on collisions with entities. Hit entity: EVENT_IMPACT, EVENT_PUSH, EVENT_SONAR, or EVENT_SHOOT depending on c_move modes .
collision geometry in a way that entities don't get entangled. Entities
that move can get stuck when colliding with polygonal objects with 'hooks'
or 'gaps' of just the size of the moving entity. Use a non-polygonal
hull or the PASSABLE flag for any object that is shaped in a way that
entities can get entangled with it.
Set the POLYGON flag for all other models - such as ramps or stairs - that don't move and should serve as obstacles.
The speed of the function depends on whether a collision is detected or not, and whether the entity is gliding or not. When moving an entity,
take care to keep its bounding box (min_x/max_x) small enough for avoiding unnecessary collisions. !! A usual beginner's mistake is extending the entities' bounding box all the way to its feet and then apply gravity for pressing the entity against the ground. This is not only slow due to the permanent collisions with the ground, it will also cause the entity to get stuck in any little crack in the floor, and prevent it from ascending stairs or slopes. Rather, use c_trace for keeping the entities' bounding box at a distance from the ground, and apply gravity only above that distance. See the example below for a simple movement function.
To prevent an entity from moving
up a stair you will have to either reduce its forward momentum or increase
the gravity. If on the other hand you want to make an entity climb a particularly high
stair you would have to increase its forward velocity or reduce gravity.
Given sufficiently large velocities an entity can climb any stair that
is up to half its size (assuming that c_setminmax was
used to set the ellipsoid to be as tall as the entity). For these reasons
the Template Scripts use two c_move calls instead. One
to handle gravity, the second to handle the forces acting on the player.
This is a more flexible solution that allows for more control but is of
course slower than a single c_move call.
- If GLIDE is set, the function will try to move as far as possible
by gliding along surfaces or making way around obstacles. The move_friction
variable (range 0..1, default 0.25) determines the friction for gliding along
surfaces. At 0 there is no friction at all, at 1 the entity sticks to the
surface and does not glide at all. The predefined variable disable_z_glide
if set to non-zero disables gliding up slopes or other objects.
- With GLIDE set, the internal movement function is executed
multiple times (up to 6 times) to allow the model to glide along walls and
other objects. Thus GLIDE can slow down the movement and
make the model end up at unexpected places. Never set GLIDE
for doors, lifts, etc.
- If ACTIVATE_TRIGGER is set, a nearby entities' event function
is triggered when it's ENABLE_TRIGGER flag
is set and either a) the sum of the moved entities' trigger_range
and the nearby entities' trigger_range
is bigger than the distance between the move target position and the nearby
entities' origin, or b) the nearby entities' trigger_range
is zero, its passable flag is set and
the move target position is inside it's bounding box.
- The variables move_min_z and move_friction determine the maximum
step height and gliding behavior.
- IGNORE_YOU can be used to prevent that a bullet gets stuck immediately
within the barrel or within the entity that fired it.
!! A usual beginner's mistake is setting the IGNORE_YOU flag but not setting the you pointer. This causes the collision detection to
return unwanted results because you is modified by many functions, and thus can point to a different entity than expected.
Physics entities, use
pXent_moveglobal or apply a force or velicity.
// simple function for walking over ground
// control the player with the WASD keys
// player origin must be at the model center
// bounding box must be smaller than the player!
// if the entity has a non standard size, make sure that the bounding box does not drag along the floor
if ((my.eflags&FAT) && (my.eflags&NARROW)) // when FAT+NARROW are both set
my.min_z *= 0.5;
var speed_down = 0; // downward speed by gravity
var anim_percent = 0; // animation percentage
vec_for_min(vFeet,me); // vFeet.z = distance from player origin to lowest vertex
// rotate the player using the [A] and [D] keys
my.pan += 5*(key_a-key_d)*time_step;
// determine the ground distance by a downwards trace
if (c_trace(my.x,vector(my.x,my.y,my.z-5000),IGNORE_ME | IGNORE_PASSABLE | USE_BOX) > 0)
dist_down = my.z + vFeet.z - target.z; // get distance between player's feet and the ground
dist_down = 0;
// apply gravity when the player is in the air
if (dist_down > 0) // above floor, fall down with increasing speed
dist_down = clamp(dist_down,0,accelerate(speed_down,5,0.1));
else // on or below floor, set downward speed to zero
speed_down = 0;
// move the player using the [W] and [S] keys
var dist_ahead = 5*(key_w-key_s)*time_step;
dist_ahead = sign(dist_ahead)*(abs(dist_ahead) + 0.5*dist_down); // adapt the speed on slopes
c_move(me,vector(dist_ahead,0,0),vector(0,0,-dist_down),IGNORE_PASSABLE | GLIDE); // move the player
// animate the player according to its moved distance
if (dist_ahead != 0) // player is moving ahead
anim_percent += 1.3*dist_ahead; // 1.3 = walk cycle percentage per quant
ent_animate(me,"walk",anim_percent,ANM_CYCLE); // play the "walk" animation
else // player stands still
anim_percent += 5*time_step;
ent_animate(me,"stand",anim_percent,ANM_CYCLE); // play the "stand" animation
move_min_z, move_friction, collisions,