[Solved] How to make character walk when I click near and run when I click far?

Started by isatche, Mon 06/04/2020 08:09:28

Previous topic - Next topic

isatche

Hi,
I have a small character in a large scene.
I would like him to walk when I click let's say up to 150 pixels away from him, but run when I click further than that.
What is the best way to do that?
Thank you!

Laura Hunt

Haven't tested this with actual code, but my theoretical approach would be to capture the mouse coordinates in the on_mouse_click(MouseButton button) function, calculate the distance between these coordinates and the player character (square root of (x2âˆ'x1)2+(y2âˆ'y1)2), and set walking speed to your "fast/run" value if the result is over 150 and the "slow/walk" value if it's below that.

isatche

Thank you for your reply Laura,
I'm affraid that technically I am not there yet to understand your answer. I tried modifying TwoClickHandler.asc to make it work, but to no success.
I'll concentrate on other things and come back to it when I learn a bit more.

Laura Hunt

I've never used any templates, but this might work...

In that template, you have a block that looks like this:

Code: ags
if (player.ActiveInventory == null)
    {
      // left click to walk
      Room.ProcessClick(mouse.x, mouse.y, eModeWalkto);
    }


This section of code makes the character walk to a point of the screen if the player hasn't clicked on anything else (hotspot, object, inventory item, etc), so here is where we need to "intercept" the mouse click and set the player's speed before they actually start walking. I'll add the code to this block in several steps so it's clear what I'm trying to do (emphasis on trying! :-D)

So we can try first defining variables where we'll store the x and y coordinates of the player (a1 and b1) and the x and y coordinates of the spot where the player has just clicked (a2 and b2):

Code: ags
if (player.ActiveInventory == null)
    {
      // left click to walk
      float a1 = IntToFloat(player.x);
      float b1 = IntToFloat(player.y);
      float a2 = IntToFloat(mouse.x);
      float b2 = IntToFloat(mouse.y);
      Room.ProcessClick(mouse.x, mouse.y, eModeWalkto);
    }


Now that we have the variables, we just need to apply the formula to calculate the distance between two points:

Code: ags
if (player.ActiveInventory == null)
    {
      // left click to walk
      float a1 = IntToFloat(player.x);
      float b1 = IntToFloat(player.y);
      float a2 = IntToFloat(mouse.x);
      float b2 = IntToFloat(mouse.y);
      int playerdistance = FloatToInt(Maths.Sqrt(Maths.RaiseToPower(a1-a2,2.0) + Maths.RaiseToPower(b1-b2,2.0)));
      Room.ProcessClick(mouse.x, mouse.y, eModeWalkto);
    }


... and finally, before actually triggering the walk interaction, we set the player's walking speed depending on whether the distance is greater than 150 pixels or not:

Code: ags
if (player.ActiveInventory == null)
    {
      // left click to walk
      float a1 = IntToFloat(player.x);
      float b1 = IntToFloat(player.y);
      float a2 = IntToFloat(mouse.x);
      float b2 = IntToFloat(mouse.y);
      int playerdistance = FloatToInt(Maths.Sqrt(Maths.RaiseToPower(a1-a2,2.0) + Maths.RaiseToPower(b1-b2,2.0)));

      player.StopMoving();
      if (playerdistance > 150) player.SetWalkSpeed(4,4); // assuming for example that your "fast" speed is 4; if not, adjust to taste
      else player.SetWalkSpeed(2,2); // this would be your regular walking speed, again adjust to your own case

      Room.ProcessClick(mouse.x, mouse.y, eModeWalkto);
    }



And... If I have the formula right, I think this should work?

Edit: Added player.StopMoving(); because you can't call SetWalkSpeed while the character is moving.

Snarky

The formula looks correct to me and should work just fine. It can be simplified and optimized some, though:

Code: ags
  if(player.ActiveInventory == null)
  {
    player.StopMoving();

    int dx = mouse.x - player.x;
    int dy = mouse.y - player.y;
    if(dx*dx + dy*dy > 22500) // distance is more than 150 (22500=150*150)
      player.SetWalkSpeed(4,4);
    else
      player.SetWalkSpeed(2,2);

    Room.ProcessClick(mouse.x, mouse.y, eModeWalkto);
  }


Since you're just comparing the two distances to see which one is bigger, instead of taking the square root of one side of the equation (which is slow), you can simply square the other, which is comparatively fast â€" and since it's a fixed number, you can even precalculate it. This also means you can work in ints throughout.

Of course, in order to get a good run you'll probably want to switch to a different animation, not just make the the character move twice as far in each step.


isatche

Laura, Snarky,
You guys are the best!
Thank you for the detailed explanation.

Looking at the code I realize how far I was in my assumption and how much more I have to learn.

It works flawlessly on a static screen, however it is glitching on a scrolling map, but it may be something I did.
In some cases on a scrolling map, it runs when I click close or ot walks when I click very far.

Laura Hunt

Right. If you have scrolling rooms (or rooms larger than your game resolution / screen size, even if they don't scroll) you have to account for the fact that player coordinates are relative to the room but mouse coordinates are relative to the screen. You would make up for this by adding the viewport coordinates to your mouse coordinates.

Adding this to Snarky's code:

Code: ags
  if(player.ActiveInventory == null)
  {
    player.StopMoving();
 
    int dx = mouse.x + GetViewportX() - player.x;
    int dy = mouse.y + GetViewportY() - player.y;

    if(dx*dx + dy*dy > 22500) // distance is more than 150 (22500=150*150)
      player.SetWalkSpeed(4,4);
    else
      player.SetWalkSpeed(2,2);
 
    Room.ProcessClick(mouse.x, mouse.y, eModeWalkto);
  }


IMPORTANT: if you're using AGS 3.5.0, use Camera.X and Camera.Y instead of GetViewPort.



SMF spam blocked by CleanTalk