📜 ⬆️ ⬇️

Ways of moving computer characters (part 2)

In the previous article I talked about the types of movements and movements in the tile world. Today I will tell you more about vector methods. Like last time I will tell the theory, explain the essence and show an example of the implementation of movements in the C ++ language.

Moving along a vector is one way to implement motion. The world is no longer divided into cells, and provides much more freedom for movement. Coordinates can be set with great accuracy (not only integer values, but also float values), which allows realizing very realistic movements. The vector is the direction in which our agent will move. The easiest way is to set it in two values, for example, V (10,5). This means that when moving a point located in the coordinate A (1,1) along the vector V (10.5), the position of the object will be in A + V = C (1 + 10.1 + 5) = C (11.6) . Vector values ​​can also be negative.

To change the direction of motion is enough to add the current vector with a new one. For example, having the vector V1 (2.6) we want to change it by adding the vector V2 (3, -3), the new motion vector will be V1 + V2 = V3 (2 + 3), 6 + (- 3)) = V3 ( 5.3). Graphically, this can be represented as follows:

Let's return to the movements on the vector. As already said, to move an object by a certain vector, you need to add the coordinates of the object with the values ​​of the vector: Pos (x, y) + V (a, b) = NewPos (x + a, y + b). The larger the vector, the further our object will move. This can create a number of difficulties associated with “slipping” obstacles. Having a large enough step, an object can easily skip small objects. There are many different ways to eliminate this drawback, but they are not included in the scope of the article.

Nevertheless, such an approach may well have the right to exist, and I will give an example of the implementation of the movement of an object along a vector. Let's make a vector class - containing two values. And the mob class, which will move on a given vector. To change the motion vector, create a function where we will place the new vector.
class Vector { public: float x, y; }; class Mob { public: float x,y;// float,      Vector Way;//  void AddVector(Vector NewWay); void Move(); }; void Mob::AddVector(Vector NewWay) {//   Way.x+=NewWay.x;//   Way.y+=NewWay.y; } void Mob::Move() {//    x+=Way.x; y+=Way.y; } 

In some cases, the vector only indicates the direction, and the speed is set by an additional variable. Then it is impossible to add a vector to the coordinates, and the vector itself should be normalized , that is, a length equal to one. Let's set, for example, the direction of movement by a unit vector, and the speed will be only a multiplier that increases the length of the vector. To add a new vector to the existing one, you need to do a number of steps:
  1. Translate the vector into a non-normalized form (multiply vector values ​​by speed).
  2. Do the same with the new vector, if this has not been done before.
  3. Add vectors in the usual way.
  4. Calculate the length of the resulting vector is our new speed.
  5. Normalize the vector.

In this case, our class and functions of adding a vector and movement take the form:
 class Mob { public: float x,y;// float,      float Speed; Vector Way;//  void Normalize(); void AddVector(Vector NewWay); void Move(); }; void Mob::Normalize() { Speed = sqrt(Way.x*Way.x + Way.y*Way.y);//   Way.x *= 1/Speed;//  Way.y *= 1/Speed; } void Mob::AddVector(Vector NewWay, float NewSpeed) { Vector MobVec, NewVec;//   MobVe.x = Way.x * Speed;//   MobVe.y = Way.y * Speed; NewVec.x = Way.x * NewSpeed;//   NewVec.y = Way.y * NewSpeed; Way.x = MobVe.x + NewVec.x;//  Way.y = MobVe.y + NewVec.y;//    Normalize();//  } void Mob::Move() {//    x += Way.x * Speed; y += Way.y * Speed; } 

I emphasize that this is one of the possible implementation options, provided exclusively for a better understanding of how to implement. In the previous article, I pointed out that there are two main ways of movement - situational and target. Consider the features of their implementation in the case of vector motion.
')
Situational way

Our mob has a motion vector in which it will move until it encounters an obstacle. Then he will change it in a certain way and continue moving in a new direction. This can be implemented by the usual conditions, neural networks, etc. The miscalculations in the vector world are a bit more complicated than in the tile world, so we omit their calculations. Suppose there is some function that tells us whether there is an obstacle ahead or not (bool CanMove ()). In this case, the set of actions of our mob can be the addition of a vector that turns it in some direction from the obstacle, at a speed proportional to the distance to the obstacle (float DistanceToBarrier ()). The movement function will take the form:
 void Mob::Move() { if(CanMove()==true) {//   -  x += Way.x * Speed; y += Way.y * Speed; } else {//   -  Vector Turn;//   Turn.x = 1;//    Turn.y = 0; AddVector(Turn, DistanceToBarrier());//  } } 

Of course, the vector of rotation in my example is not quite true, because the direction of rotation when adding the vector V (1.0) will depend on the current direction of movement. But the essence, I think, is clear.

Targeted methods

To implement the target methods, templates (blanks), key points (waypoints), etc. are also used. Templates are a regular array of vectors along which our object moves. But it is inconvenient to designate each step with its vector because of the size of the path, so they use key points. The bottom line is that the agent moves along a vector for a certain time (up to a certain point), then changes its direction of movement to a new vector, and so on to the next point. Add an array of points and an array of directions, for convenience we will use the same Vector class.
 class Mob { public: float x,y;// float,      float Speed; Vector Way;//  Vector Points[10];//   Vector PointsVec[10];//    int Position;//    void Normalize(); void AddVector(Vector NewWay); void Move(); }; void Mob::Move() { if(x==Points[Position].x && y==Points[Position].y) {//    Position++;//    Way.x = PointsVec[Position].x;//    Way.y = PointsVec[Position].y; } else{//   -   x += Way.x * Speed; y += Way.y * Speed; } } 

The vector method has several advantages:
  1. Smoother movements
  2. Natural movements
  3. The ability to implement physics (friction, acceleration, rotation, attraction, ...)

But you can highlight a number of drawbacks:
  1. Sometimes very resource intensive (calculating the root in normalization, etc.)
  2. Complex functions that require a good understanding of the basics (especially with regard to the implementation of physics)
  3. Difficulties in obtaining information about the world (you need to calculate the collision with all potential objects)


This method is widely used, especially where it is necessary to more accurately and beautifully convey movement. And this is the majority of modern 3D games. In the next, and last, article I will talk about the mixed ways of movement realization, combining both tiled and vector displacements.

Source: https://habr.com/ru/post/164563/


All Articles