Jibble

Author Topic: Issues with character movement angles/speeds  (Read 1606 times)

Problem

  • Cavefish
    • I can help with animation
    • I can help with characters
    • I can help with making music
    • I can help with play testing
    • I can help with scripting
    • I can help with translating
Issues with character movement angles/speeds
« on: 09 Apr 2014, 07:58 »
I'm not quite sure if this is the right forum, but I have a few more advanced questions about character movement.

1. Usually AGS moves the characters freely in all directions at all possible angles. This can look bad, especially if you use diagonal walkcycles. If, in your animation, the character walks at 45°, but AGS decides to move the character at 55°, it looks really awkward, as if the character is sliding. I know it's a common problem in many adventures, but are there any ways around this?
More precisely, can you limit a character's movement to 8 fixed angles somehow through scripting? I'm not talking about keyboard controls, but point & click. It doesn't look too bad at low resolutions without diagonal walkcycles, but if you make a highres game with smooth animations and diagonal views, it looks plain wrong most of the time.

2. When I set UniformMovementSpeed to false, I can have different speed values for X and Y. But this doesn't seem to apply to diagonal movement? You can test this easily by trying to set a very low value (such as -5, which would translate to 1/5) for MovementSpeedY. Now you get a much faster y-speed by moving diagonally than by moving vertically, which just doesn't make any sense. Normally you wouldn't set the speed levels that low, so you won't always notice it, but it makes the movement inconsistent when using different speed settings for X and Y. Is this behaviour intended, or is it a bug?

So generally, while the speed settings work fine for low resolutions and simple animations, I recently got the feeling that character movement in AGS can get quite messy when you try to make it look more real. Does anyone know of any good workarounds? Have I missed something?
« Last Edit: 09 Apr 2014, 08:09 by Problem »

Billbis

  • R, what else?
    • I can help with translating
Re: Issues with character movement angles/speeds
« Reply #1 on: 09 Apr 2014, 09:07 »
Regarding point 1, which is reported here, it will require somehow (source code, module or plugin) to recode the entire pathfinder to do to it properly.
However I have coded a unperfect module that mostly do the job. It is still in development and still not published in this forum, but I'm using it in my two games, and some French members are also using it. All explanation are present in the header, it simply contain an alternative walk function, and by default intercept clicks in eModeWalkTo.
Download link (~5ko)
Header:
Spoiler: ShowHide
Code: Adventure Game Studio
  1. // Header for module 'IsoWalk', version 1.0
  2. //
  3. // Author: Billbis & Pidem
  4. //
  5. // Abstract:
  6. //
  7. //   Enforce walking on the 8 standard directions when possible to avoid gliding effects.
  8. //   Contain an alternative walk function (IsoWalk(int x, int y, optional BlockingStyle, optional WalkWhere))
  9. //   Usage: cEgo.IsoWalk(100,100);
  10. //   By default, hacks mouse click in WalkTo mode to perform a player.IsoWalk().
  11. //  
  12. // Dependencies:
  13. //
  14. //   AGS version required:  build for AGS 3.2.1 and 3.3.0, not sure if it works with older versions.
  15. //
  16. //   AGS setting required: No particular configuration is needed. Not very useful if you do not have
  17. //   8 directions in your walking views.
  18. //
  19. // Configuration:
  20. //
  21. //   Optional:
  22. //    
  23. //     IsoW_HACKWALK is define as true.
  24. //     Define it as false to use normal mouse comportment for eModeWalkTo (or delete the corresponding section in IsoWalk Script).
  25. //    
  26. //     You can import a few variables to do some customizations. (Uncomment these following lines)
  27.  
  28. /*
  29. import bool IsoW_moduleON;
  30. import bool IsoW_UseAlternateHorizontalPath;
  31. import bool IsoW_UseAlternateVerticalPath;
  32. import int IsoW_MinDistance;
  33. */
  34.  
  35. //     IsoW_moduleON can be set to false to force IsoWalk to act as normal walk function.
  36. //     (useful to set this module as optional).
  37. //     IsoW_UseAlternateHorizontalPath and IsoW_UseAlternateVerticalPath will change waypoints priorities.
  38. //     (see code comments for details).
  39. //     IsoW_MinDistance is the minimum distance (in pixel) to activate the module.
  40. //
  41. // Caveats:
  42. //
  43. //   This module works better on large free WalkeableAreas than on small busy ones.
  44. //   If no alternative way is found, characters will use normal walk comportment.
  45. //
  46. // Revision history:
  47. //   version 1.0: 2014/03/04
  48. //      Name change from Use 8 Direction to IsoWalk. Addition of a few '+3' to avoid a strange comportment of
  49. //      AGS pathfinder.
  50. //      The module now support multi-character non blocking IsoWalks !
  51. //   version beta 0.4: 2013/06/27
  52. //      Bug correction: WA detection now take in account viewport.
  53. //   version beta 0.3: 2013/06/12
  54. //      Addition of Minimal Distance setting.
  55. //   version beta 0.2: 2013/03/08
  56. //      Code cleaning + more customizations possibles.
  57. //   version beta 0.1: 2013/03/03
  58. //      First release.
  59. //  
  60. // License:
  61. //
  62. //   IsoWalk is publish under the terms of the
  63. //   Do What The Fuck You Want To Public License, Version 2
  64. //
  65. // This program is free software. It comes without any warranty, to
  66. // the extent permitted by applicable law. You can redistribute it
  67. // and/or modify it under the terms of the Do What The Fuck You Want
  68. // To Public License, Version 2, as published by Sam Hocevar. See
  69. // http://sam.zoy.org/wtfpl/COPYING for more details.
  70. //
  71. // Thanks:
  72. //
  73. //   Kitai, valoulef
  74. //
  75. // Defines
  76. #define IsoW_HACKWALK true //Override Mouse Click in mode WalkTo to use IsoWalk for the main character
  77. //
  78. // Function imports
  79.  
  80. /// Walk to point (x,y) using only the 8 directions if possible.
  81. import void IsoWalk(this Character*, int x, int y, BlockingStyle = eNoBlock, WalkWhere = eWalkableAreas);

Script:
Spoiler: ShowHide
Code: Adventure Game Studio
  1. // IsoWalk module script
  2. //Variables declaration & definition
  3. bool IsoW_moduleON = true; //if turn OFF, IsoWalk act as normal Walk. To set the module as an option.
  4.  
  5. bool IsoW_UseAlternateHorizontalPath = false; //exchange waypoint priorities. False = vertical / horizontal first
  6. bool IsoW_UseAlternateVerticalPath = false; //True = Diagonnale first
  7.  
  8. int IsoW_MinDistance = 20; //Minimum distance (in pixel) for the Module to be active.
  9.  
  10. // Stuff for non blocking walk
  11. bool IsoW_noBlock = false; // optimisation of the repeatedly_execute() checks.
  12. int IsoW_x[];
  13. int IsoW_y[];
  14. bool IsoW_eWalkableAreas[];
  15. bool IsoW_character[];
  16.  
  17. function game_start () {
  18.   IsoW_x = new int[Game.CharacterCount];
  19.   IsoW_y = new int[Game.CharacterCount];
  20.   IsoW_eWalkableAreas = new bool[Game.CharacterCount];
  21.   IsoW_character = new bool[Game.CharacterCount];
  22. }
  23.  
  24. //Functions definition
  25.  
  26. // Return absolute value
  27. function IsoW_Abs(int x) {
  28.  if (x >= 0) return x;
  29.   else return -x;
  30. }
  31.  
  32. // Main module function
  33. void IsoWalk(this Character*, int x, int y, BlockingStyle BStyle, WalkWhere WWhere) { // Alternative walk function. Core of the module.
  34.   int ID = this.ID;
  35.  
  36.  
  37.   int DeltaX = IsoW_Abs(x - this.x); //Distances between character and destination
  38.   int DeltaY = IsoW_Abs(y - this.y);
  39.   if ((IsoW_moduleON)&& ((DeltaX*DeltaX + DeltaY*DeltaY) > (IsoW_MinDistance*IsoW_MinDistance))) { //If Distance > MinDistance (Pythagore)
  40.     int Signe;
  41.     int xP1, xP2, xWP, yP1, yP2, yWP;
  42.     //Calculating Waypoint Coordinates
  43.     if (DeltaX >= DeltaY) { //Diagonal + Horizontal movement needed
  44.       if (this.x - x >= 0) {
  45.         Signe = 1;
  46.       } else {
  47.         Signe = -1;
  48.       }
  49.       if (!IsoW_UseAlternateHorizontalPath) {
  50.         xP1= x + DeltaY*Signe;  //Waypoint coordiantes
  51.         yP1= this.y;
  52.         xP2= this.x + DeltaY*(-Signe); //Alternative waypoint coordinate
  53.         yP2= y;
  54.       } else {// if IsoW_UseAlternateHorizontalPath P1 <-> P2
  55.         xP1= this.x + DeltaY*(-Signe);  //Waypoint coordiantes
  56.         yP1= y;
  57.         xP2= x + DeltaY*Signe;//Alternative waypoint coordinate
  58.         yP2= this.y;
  59.       }
  60.     } else { //DeltaX < DeltaY Diagonal + Vertical movement needed
  61.       if (this.y - y >= 0) {
  62.         Signe = 1;
  63.       } else {
  64.         Signe = -1;
  65.       }
  66.       if (!IsoW_UseAlternateVerticalPath) {
  67.         xP1= this.x;  //Waypoint coordiantes
  68.         yP1= y + DeltaX*Signe;
  69.         xP2= x; //Alternative waypoint coordinate
  70.         yP2= this.y + DeltaX*(-Signe);
  71.       } else {// if IsoW_UseAlternateVerticalPath P1 <-> P2
  72.         xP1= x;  //Waypoint coordiantes
  73.         yP1= this.y + DeltaX*(-Signe);
  74.         xP2= this.x; //Alternative waypoint coordinate
  75.         yP2= y + DeltaX*Signe;
  76.       }
  77.     }
  78.     //Walking
  79.     if ( WWhere == eWalkableAreas) {
  80.       if (GetWalkableAreaAt(xP1-GetViewportX(), yP1-GetViewportY()) !=0) { //if P1 is in a WA, we use it
  81.         xWP = xP1 + 3; // +3 are small hacks to force AGS to use Djikstra
  82.         yWP = yP1 + 3;
  83.       } else if (GetWalkableAreaAt(xP2-GetViewportX(), yP2-GetViewportY()) !=0) { //if P2 is in a WA but not P1, we use P2
  84.         xWP = xP2 + 3;
  85.         yWP = yP2 + 3;
  86.       } else { //is not P1 neither P2 are in a WA, then we give up
  87.         xWP = x;
  88.         yWP = y;
  89.       }
  90.       if (BStyle == eBlock) {
  91.         this.Walk(xWP, yWP, eBlock, eWalkableAreas);
  92.         this.Walk(x, y, eBlock, eWalkableAreas);
  93.       } else { //BStyle == eNoBlock
  94.         this.Walk(xWP, yWP, eNoBlock, eWalkableAreas);
  95.         IsoW_x[ID] = x;
  96.         IsoW_y[ID] = y;
  97.         IsoW_character[ID] = true;
  98.         IsoW_eWalkableAreas[ID] = true;
  99.         IsoW_noBlock = true; // 2nd part of the movement in Repeatidly_execute
  100.       }
  101.     } else { // WWhere == eAnywhere
  102.       if (BStyle == eBlock) {
  103.         this.Walk(xP1, yP1, eBlock, eAnywhere);
  104.         this.Walk(x, y, eBlock, eAnywhere);
  105.       } else { //BStyle == eNoBlock
  106.         this.Walk(xP1, yP1, eNoBlock, eAnywhere);
  107.         IsoW_x[ID] = x;
  108.         IsoW_y[ID] = y;        
  109.         IsoW_character[ID] = true;
  110.         IsoW_eWalkableAreas[ID] = false;
  111.         IsoW_noBlock = true; // 2nd part of the movement in Repeatidly_execute
  112.       }
  113.     }
  114.   } else { //if module is OFF, or if too short distance
  115.     this.Walk(x, y, BStyle, WWhere);
  116.   }
  117. }
  118.  
  119. //Non blocking movement
  120. function repeatedly_execute() {
  121.   if (IsoW_noBlock) {
  122.     int i = 0;
  123.     while (i < Game.CharacterCount) {
  124.       if (IsoW_character[i] == true && character[i].Moving == false) {
  125.         if (IsoW_eWalkableAreas[i]) {
  126.           character[i].Walk(IsoW_x[i], IsoW_y[i], eNoBlock, eWalkableAreas);
  127.         } else {
  128.           character[i].Walk(IsoW_x[i], IsoW_y[i], eNoBlock, eAnywhere);
  129.         }
  130.         IsoW_character[i] = false;
  131.       }
  132.       i++;
  133.     }
  134.     IsoW_noBlock = false;
  135.     i = 0;
  136.     while (i < Game.CharacterCount) {
  137.       if (IsoW_character[i]) {
  138.         IsoW_noBlock = true;
  139.       }
  140.       i++;
  141.     }
  142.   }
  143. }
  144.  
  145. function on_mouse_click(MouseButton button) {  // intercept left clicks in eModeWalkto to order a IsoWalk
  146.   if (IsoW_HACKWALK) {
  147.     if (button == eMouseLeft) {
  148.       if (Mouse.Mode == eModeWalkto) {
  149.           player.IsoWalk(mouse.x + GetViewportX(), mouse.y + GetViewportY(), eNoBlock, eWalkableAreas);
  150.           ClaimEvent();
  151.       }
  152.     }
  153.   }
  154. }
  155.  
  156. //You may want to import these in (see Header)
  157. export IsoW_moduleON, IsoW_UseAlternateHorizontalPath, IsoW_UseAlternateVerticalPath, IsoW_MinDistance;

Usage:
Code: Adventure Game Studio
  1. cEgo.IsoWalk(x, y, optional BlockingStyle, optional WalkWhere)
« Last Edit: 09 Apr 2014, 09:09 by Billbis »

Problem

  • Cavefish
    • I can help with animation
    • I can help with characters
    • I can help with making music
    • I can help with play testing
    • I can help with scripting
    • I can help with translating
Re: Issues with character movement angles/speeds
« Reply #2 on: 09 Apr 2014, 10:02 »
Thanks, this looks very interesting! I'll give it a try and report back if it works for me. :)

Billbis

  • R, what else?
    • I can help with translating
Re: Issues with character movement angles/speeds
« Reply #3 on: 10 Apr 2014, 20:49 »
For posterity reason (:grin:), I'll repeat here a few things we discussed in PM:

1)
To use my module with the Lightweight BASS Template v2.0 that come with AGS:
-Import the module and put the IsoWalk script upper the TwoClickHandler script in the project tree.
-Line 76 of IsoWalk header, replace:
Code: Adventure Game Studio
  1. #define IsoW_HACKWALK true
by:
Code: Adventure Game Studio
  1. #define IsoW_HACKWALK false
(alternatively, delete this line in the header and line 145-154 in the script body)
-Line 47 of TwoClickHandler script, replace:
Code: Adventure Game Studio
  1. ProcessClick(mouse.x, mouse.y, eModeWalkto);
by:
Code: Adventure Game Studio
  1. player.IsoWalk(mouse.x+GetViewportX(), mouse.y+GetViewportY(), eNoBlock, eWalkableAreas);
If you aren't doing to many fancy things in your game, it should do the job.

Alternatively, change nothing code-wise, put IsoWalk bellow TwoClickHandler and call a ClaimEvent after each ChangeRoom:
Code: Adventure Game Studio
  1. function hDoor_Interact()
  2. {
  3.     player.IsoWalk(935, 610, eBlock);
  4.     player.ChangeRoom(2, 260, 590);
  5.     ClaimEvent();
  6. }

2) If I understand correctly, TheBitPriest has recoded a pathfinder for Heroine's Quest. Was it for addressing the same issue (avoid gliding effect during walks)? How he did it, module, plugin or directly in the AGS source code? Do the HQ team plan to share his work with the community? There solution might be less broken than mine. :-X

3)
I have still no simple idea for your original point 2, despite handling walking in another custom function.

Problem

  • Cavefish
    • I can help with animation
    • I can help with characters
    • I can help with making music
    • I can help with play testing
    • I can help with scripting
    • I can help with translating
Re: Issues with character movement angles/speeds
« Reply #4 on: 10 Apr 2014, 22:40 »
Once again, thank you! ;-D

I've posted the second point in the bug tracker. I guess the movement speed calculations should better be fixed in AGS at some point in the future, and it's not that important. After playing around with the settings I got satisfactory results - not perfect, but good enough.

The fixed movement angles were more important, and thanks to your IsoWalk module the walking animations look much better than before.


Khris

  • having to deal with what games are going through
    • Lifetime Achievement Award Winner
    • I can help with play testing
    • I can help with scripting
    • I can help with translating
    • Khris worked on a game that was nominated for an AGS Award!
Re: Issues with character movement angles/speeds
« Reply #5 on: 30 May 2014, 11:43 »
I'm digging this up to show something I'm currently working on:

[embed=640,430]http://www.youtube.com/watch?v=UzadZlGZlic[/embed]

The basic idea is to add nodes to walkable areas where their edge bends inwards, because any path that itself bends will touch those. I'm currently doing this by hand; not sure this can be automated in an efficient way.
Then I'm adding nodes at the start and end of the walk.
The next step is to move outwards in all 8 directions from each node, creating a grid of intersections.
Dijkstra finds the shortest path, and finally I'm optimizing it by removing intermediate nodes and finding paths of equal length with less corners (this is deliberately slowed and shown in the video; it only takes a fraction of a second).

You might notice a glitch at around 1:10, the path contains a small "bulge". This is one of the kinks I'm still working out :)

Slasher

  • slasher
    • I can help with AGS tutoring
    • Lifetime Achievement Award Winner
    • I can help with scripting
    • I can help with story design
    • Slasher worked on a game that was nominated for an AGS Award!
Re: Issues with character movement angles/speeds
« Reply #6 on: 30 May 2014, 12:51 »
Hi Khris,

Lovely idea and would be much welcomed. I do hope you iron out the wrinkles and make it available sometime.

Cheers ;)




Billbis

  • R, what else?
    • I can help with translating
Re: Issues with character movement angles/speeds
« Reply #7 on: 30 May 2014, 15:52 »
Awesome, Khris! Good luck with it.

Problem

  • Cavefish
    • I can help with animation
    • I can help with characters
    • I can help with making music
    • I can help with play testing
    • I can help with scripting
    • I can help with translating
Re: Issues with character movement angles/speeds
« Reply #8 on: 30 May 2014, 19:32 »
That's great, Khris! Looks like an interesting approach. I hope you can finish it, I'd love to give it a try. :)