Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: How to implement character control with the mouse  (Read 3176 times)

0 Members and 1 Guest are viewing this topic.

Azazel

  • Newbie
  • *
  • Posts: 6
    • View Profile
How to implement character control with the mouse
« on: September 08, 2017, 11:30:37 am »
Hi, can u tell how to implement the control of the character with the mouse, as in Action/RPG games, such as Diablo, when holding the right click button and changing only the direction vector of the cursor, the sprites also change, i.e. when the character is running behind the cursor. I just can not figure out how to do it, can you tell me?

AlexAUT

  • Sr. Member
  • ****
  • Posts: 396
    • View Profile
Re: How to implement character control with the mouse
« Reply #1 on: September 08, 2017, 08:40:51 pm »
Basically you need to calculate a movement vector for your character based on the mouse position. So you need two things:
  • Angle from the character to the cursor, which is also the rotation of the character/sprite
  • The distance, so you can implement a "deadzone", i will explain later why this is needed

You get the angle by using simple geometry, as described here
Getting the distance is also not very difficult

With these two values we can move the character by calculating a movement vector. This can be easily derived from the angle

  sf::Vector2f movement = sf::Vector2f(cos(angle), sin(angle)) * movementSpeed;
where angle is a float (in radiant) from (1) and movementSpeed is a float which you can set.

Now you have everything you need to move your character
   yourCharacter.move(movement );
You need to take the frametime into account to get a nice and smooth result, as described here

Also there is another small problem, the character will start spinning around your cursor once it has reached it. Because it will move a bit to far "over" the cursor and in the next frame it will "bounce" back. To avoid this you need a deadzone, which should look like this (modify the part above with the deadzone):
  const float DEAD_ZONE = some_amount_based_on_movementSpeed
  if(distance > DEAD_ZONE)
  {
       yourCharacter.move(movement);
  }
 
Where distance is described in (2)


I hope this gives you a basic idea how to tackle this problem.

AlexAUT

Azazel

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: How to implement character control with the mouse
« Reply #2 on: September 13, 2017, 02:45:42 pm »
Basically you need to calculate a movement vector for your character based on the mouse position. So you need two things:
  • Angle from the character to the cursor, which is also the rotation of the character/sprite
  • The distance, so you can implement a "deadzone", i will explain later why this is needed

You get the angle by using simple geometry, as described here
Getting the distance is also not very difficult

With these two values we can move the character by calculating a movement vector. This can be easily derived from the angle

  sf::Vector2f movement = sf::Vector2f(cos(angle), sin(angle)) * movementSpeed;
where angle is a float (in radiant) from (1) and movementSpeed is a float which you can set.

Now you have everything you need to move your character
   yourCharacter.move(movement );
You need to take the frametime into account to get a nice and smooth result, as described here

Also there is another small problem, the character will start spinning around your cursor once it has reached it. Because it will move a bit to far "over" the cursor and in the next frame it will "bounce" back. To avoid this you need a deadzone, which should look like this (modify the part above with the deadzone):
  const float DEAD_ZONE = some_amount_based_on_movementSpeed
  if(distance > DEAD_ZONE)
  {
       yourCharacter.move(movement);
  }
 
Where distance is described in (2)


I hope this gives you a basic idea how to tackle this problem.

AlexAUT

Thanks for the answer. But I wrote it, a little bit differently:
for the character to follow the cursor, I get the distance, like this and move it
if ( Mouse::isButtonPressed(Mouse::Right) )
{  
    Vector2f totalMovement;
    totalMovement.x = Mouse::getPosition(window).x - hero_sprite.getPosition().x;
    totalMovement.y = Mouse::getPosition(window).y - hero_sprite.getPosition().y;
    hero_sprite.move(totalMovement * (1.f/1000.f));
}

then created a function for the sides
enum look
{
    right, left, down, up, downright, upright, upleft, downleft
};

and then I get the angle of my distance from this function
float mtrAngle_f(float x, float y)
{
    float dir;
    dir = acosf( x / sqrtf(powf(-x, 2.0f) + powf(-y, 2.0f)) );
    if (y > 0.0f)       { dir = MTR_2PI_F - dir; }
                       
    return dir * MTR_RADIAN_F;
}

and finally create a function that determine in what side the character looks at..
look lookAtMouse(int x, int y)
{
    float direction = mtrAngle_f(x, y);
    if (direction > angle && direction <= angle) // here we define the range of the angle
    {
        return look::right; // or left, or down, etc.
    }
}

in depending on the side in which he looks at, I drew the appropriate sprites
if (player.lookAtMouse(pos.x, pos.y) == look::up) // or left, or down, etc.
{
    player.animation();
}

Can you tell me how correctly or incorrectly I implemented it?

AlexAUT

  • Sr. Member
  • ****
  • Posts: 396
    • View Profile
Re: How to implement character control with the mouse
« Reply #3 on: September 13, 2017, 07:50:23 pm »
Can you tell me how correctly or incorrectly I implemented it?

Does your implementation work like you expect? Then you did it correctly :).

There is one thing which might not work like you expect: Your character will move faster when farther away from the cursor, because you take the distance vector and not the direction in your calculation. So I would recommend the following for your movement vector:
if ( Mouse::isButtonPressed(Mouse::Right) )
{  
    Vector2f totalMovement;
    totalMovement.x = Mouse::getPosition(window).x - hero_sprite.getPosition().x;
    totalMovement.y = Mouse::getPosition(window).y - hero_sprite.getPosition().y;
    // Create a unit vector, to move at constant speed
    totalMovement /= std::sqrt(std::pow(totalMovement.x, 2) + std::pow(totalMovement.y, 2))
    hero_sprite.move(totalMovement * (1.f/1000.f)); //1/1000 should then be choosen bigger like 5
}

This will make your character move at the same speed independent from the distance to the mouse


AlexAUT

Azazel

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: How to implement character control with the mouse
« Reply #4 on: September 14, 2017, 02:05:06 pm »
Can you tell me how correctly or incorrectly I implemented it?

Does your implementation work like you expect? Then you did it correctly :).

There is one thing which might not work like you expect: Your character will move faster when farther away from the cursor, because you take the distance vector and not the direction in your calculation. So I would recommend the following for your movement vector:
if ( Mouse::isButtonPressed(Mouse::Right) )
{  
    Vector2f totalMovement;
    totalMovement.x = Mouse::getPosition(window).x - hero_sprite.getPosition().x;
    totalMovement.y = Mouse::getPosition(window).y - hero_sprite.getPosition().y;
    // Create a unit vector, to move at constant speed
    totalMovement /= std::sqrt(std::pow(totalMovement.x, 2) + std::pow(totalMovement.y, 2))
    hero_sprite.move(totalMovement * (1.f/1000.f)); //1/1000 should then be choosen bigger like 5
}

This will make your character move at the same speed independent from the distance to the mouse


AlexAUT

If i do this, my character moving with freeze, by jerks, regardless of what I did set as speed 5 or 1/1000 or 100

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11035
    • View Profile
    • development blog
    • Email
Re: How to implement character control with the mouse
« Reply #5 on: September 14, 2017, 02:07:33 pm »
Please don't use full quotes on the forum, especially if it's obvious that you're replying to the previous post. Full quotes make threads hard to read and add no value to the discussion. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

AlexAUT

  • Sr. Member
  • ****
  • Posts: 396
    • View Profile
Re: How to implement character control with the mouse
« Reply #6 on: September 15, 2017, 11:06:42 am »
If i do this, my character moving with freeze, by jerks

Have you tried printing the totalMovement vector? The axis values should be <= 1.  If this is the case there should be not freezes/jerks, do you have a constant frametime (vsync or framelimit)?

If the freezes/jerks come from your variable frametime you should read this very well written articel on how to fix this "issue"

Azazel

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: How to implement character control with the mouse
« Reply #7 on: September 17, 2017, 07:04:35 pm »
If the freezes/jerks come from your variable frametime you should read this very well written articel on how to fix this "issue"

I have "sf::Clock clock" then i get time
float time = clock.getElapsedTime().asMicroseconds();
clock.restart();
time = time / 800;
and i pass the received time in method "animation()"
float frame = 0.f;
void animation(float &time, Vector2f &totalMovement, int scale)
{
        frame += 0.005 * time;
        if (frame > 8)
                frame -= 8;
        sprite.setTextureRect(IntRect(127 * int(frame), scale, width, height));
        cout << totalMovement.x << " " << totalMovement.y << " " << time << " " << speed << endl;
        sprite.move(totalMovement * time * speed); // time - getted early "time" , speed = 0.001f
}

And, yeah, i prined my totalMovement vector, its values <= 1 (like (x)0.95151 (y)0.307619 or (x)1 (y)-0.000951649), no, i dont have framelimit. But my character dont move at all, he only spins on the spot, and the animation starts with freezes.
« Last Edit: September 17, 2017, 07:22:50 pm by Azazel »