1
\$\begingroup\$

How do i make an Enemy move in random directions? I tried this but it doesnt work how i want it to.. thats why im asking what i need to do, to achieve that? (Pseudo code would be cool)

 void Enemy::Update()
{
    Timer += Timer->GetTimeElapsed();
    int Random = (rand() % 4);
    if (Timer >= 2.0f)
    {
        switch (Random)
        {
        case(0):
            m_xPos += 10.0f * Timer->tElapsed();
        case(1):
            m_xPos -= 10.0f  * Timer->tElapsed();
        case(2):
            m_yPos -= 10.0f  * Timer->tElapsed();
        case(3):
            m_yPos += 10.0f  * Timer->tElapsed();
        }
    }
}

Thank you

Edit: This is the movement i get so far.

Enemy movement

\$\endgroup\$
2
  • 2
    \$\begingroup\$ When asking a question, please make sure you put as many details as possible. "it doesnt work how i want it to" lacks some details: how does it work now, and what do you want instead? \$\endgroup\$
    – Vaillancourt
    Feb 16, 2020 at 14:22
  • \$\begingroup\$ oh im sorry, well it moves only a littlebit and always in left down position. Added Gif to show what i mean by that \$\endgroup\$
    – Logci
    Feb 16, 2020 at 14:48

1 Answer 1

6
\$\begingroup\$

First of all, you don't have breaks in your switch statement, that means that if your random number is for example 1, after subtracting the speed value from the position, the program will keep running inside the switch statement executing the case 2 and 3, so the case 3 will be always executed, making the object move to the bottom and the case 1 will be more executed than the 0 so the object would tend to move the left too.

Now your code would be like this:

    switch (random)
    {
    case(0):
        m_xPos += 10.0f * Timer->tElapsed();
        break;
    case(1):
        m_xPos -= 10.0f  * Timer->tElapsed();
        break;
    case(2):
        m_yPos -= 10.0f  * Timer->tElapsed();
        break;
    case(3):
        m_yPos += 10.0f  * Timer->tElapsed();
        break;
    }

But now if you run the code you will notice that your enemy tends to be just shaking in the same position, and that's actually moving to random directions each frame but I guess that is not your desired movement, and to have a "better" movement you can do a lot of different things that will give you different results.

One solution: Moving the players towards a random target in a limited range, and when it reaches the target, set a new one.

I will use the Vector2f struct to do simple calculations with points, MoveTowards is for moving a point into the target direction step by step and return true when reaches the target, and RandomPointInRangereturns a point within a circle of a defined range.

struct Vector2f {
  float x;
  float y;

  Vector2f operator+(Vector2f other) {
    return Vector2f{x + other.x, y + other.y};
  }
  Vector2f operator-(Vector2f other) {
    return Vector2f{x - other.x, y - other.y};
  }
  Vector2f operator*(Vector2f other) {
    return Vector2f{x * other.x, y * other.y};
  }
  Vector2f operator/(Vector2f other) {
    return Vector2f{x / other.x, y / other.y};
  }

  Vector2f operator*(float scale) { return Vector2f{x * scale, y * scale}; }
  Vector2f operator/(float scale) { return Vector2f{x / scale, y / scale}; }

  float magnitude() { return std::sqrt(std::pow(x, 2) + std::pow(y, 2)); }
};

bool MoveTowards(Vector2f& originPosition, Vector2f targetPosition,
                 float deltaDistance) {
  Vector2f dif = targetPosition - originPosition;
  float mag = dif.magnitude();
  if (mag <= deltaDistance || mag == 0) {
    originPosition = targetPosition;
    return true;
  }
  originPosition = dif / mag * deltaDistance + originPosition;
  return false;
};

constexpr double PI() { return std::acos(-1); }
float randomFrom0To1() {
  return static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
}

Vector2f RandomPointInRange(Vector2f origin, float range) {
  float r = range * std::sqrt(randomFrom0To1());
  float alpha = randomFrom0To1() * 2 * PI();
  return Vector2f{origin.x + r * std::cos(alpha),
                  origin.y + r * std::sin(alpha)};
}

Then you can do something like this in your code and even change your position to be a Vector2f:

float m_velocity;
const float m_maxStraightMove = 60.f;
Vector2f m_targetPosition =
    RandomPointInRange(Vector2f{m_xPos, m_yPos}, m_maxStraightMove);

void Enemy::Update() {
  Vector2f position{m_xPos, m_yPos};
  if (MoveTowards(position, m_targetPosition, m_velocity))
    m_targetPosition = RandomPointInRange(position, m_maxStraightMove);
  m_xPos = position.x;
  m_yPos = position.y;
};

Another solution would be let the enemy move in a random direction and change the direction in t + randomOffset time which is probably a bit more easy than doing what I did but I leave that for you if you want to try.

\$\endgroup\$
1
  • \$\begingroup\$ Adding a vector2 struct is definitely a good for keeping things organized and easier to develop. Adding breaks to the original switch should fix the original bug too. It looks like the result of switch on Rand was: Rand 0: x = x + 10 - 10, y = y + 10 - 10 (or x = x , y = y) Rand 1: x = x - 10 , y = y + 10 - 10 (or x -= 10, y = y) Rand 2: x = x , y = y + 10 - 10 (or x = x, y = y) Rand 3: x = x , y = y + 10 (or x = x, y = y + 10) As a result stuff could only move in the left (x - 10) and down (y + 10). \$\endgroup\$ Feb 27, 2020 at 7:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .