Random waypoints on the path

Started by Kooky123, Fri 05/08/2011 22:20:56

Previous topic - Next topic

Kooky123

I was thinking, how can one add random waypoints on a path for my main player?? Imagine we click on a walkable area, but before getting there, the main player character strays off from the linear path going to random waypoints, non-blocking if doable, before finally arriving to destination (where we previously clicked)?

monkey0506

There's the Character.AddWaypoint function, but that doesn't specify if it's blocking or not. I would assume not because otherwise you could just write out multiple Walk commands in succession and achieve the same thing. Note that the AddWaypoint function ignores walkable areas though, so you might want to be careful in how you implement it.

You can use the Random function to generate completely random coordinates, but it might be better (since AddWaypoint ignores walkable areas) to use a set of predefined waypoints and select from them randomly. For example, if you had 20 random waypoints:

Code: ags
// GlobalScript.ash
import void SetWaypoint(int waypoint, int x, int y);
import void RandomWalk(this Character*, int x, int y);

// GlobalScript.asc
int waypointX[]; // stores the waypoint x-coordinates
int waypointY[]; // stores the waypoint y-coordinates
int waypointCount = 20; // stores the total number of waypoints

void SetWaypoint(int waypoint, int x, int y)
{
  // sets the coordinates of the given waypoint to (x, y)
  if ((waypoint < 0) || (waypoint >= waypointCount)) return; // invalid waypoint specified
  waypointX[waypoint] = x;
  waypointY[waypoint] = y;
}

function game_start()
{
  waypointX = new int[waypointCount]; // initialize our dynamic arrays
  waypointY = new int[waypointCount];
  // now, set up your waypoints as needed
  // this might actually make more sense in room_Load since you're not likely to want the same waypoints for every room!
  SetWaypoint(0, 10, 150); // waypoint 0: (10, 150)
  SetWaypoint(1, 285, 92); // waypoint 1: (285, 92)
  // ...
  SetWaypoint(19, 130, 20); // waypoint 19: (130, 20)
}

void RandomWalk(this Character*, int x, int y)
{
  int waypoints = 3; // we'll use 3 waypoints each time
  int rands[] = new int[waypoints];
  int i = 0;
  while (i < waypoints)
  {
    // loop through and set each waypoint to a unique random one
    rands[i] = Random(waypointCount - 1); // random 0-19
    bool used = (i > 0); // for any value of 'i' other than 0, we need to run the following loop at least once
    while (used)
    {
      // check for used waypoints (so each one will be different)
      used = false; // this will be set inside the next loop as needed
      int j = 1;
      while ((i - j) >= 0)
      {
        if (rands[i] == rands[i - j]) used = true; // if the current waypoint matches a previous one, then we'll need a new one
        j++;
      }
      if (used) rands[i] = Random(waypointCount - 1); // waypoint was already in-use, generate a new one
    }
    i++;
  }
  this.Walk(waypointX[rands[0]], waypointY[rands[0]], eNoBlock); // walk to the first of the random waypoints...
  i = 1;
  while (i < waypoints)
  {
    // loop through the remaining waypoints and add them
    this.AddWaypoint(waypointX[rands[i]], waypointY[rands[i]]);
    i++;
  }
  this.AddWaypoint(x, y); // ...and finally go where they were supposed to be going in the first place!
}


Then, instead of using the normal Walk function, you could call the RandomWalk one like this:

Code: ags
  player.RandomWalk(x, y);


Don't worry if the code seems a bit daunting, it looks more complex than it really is. I've tried to explain what I'm doing, and I've done it fairly generically so that you should be able to change the values of waypointCount and the waypoints variables without breaking anything. As I noted in the code, you'd probably want to call SetWaypoint in room_Load of each room so you can establish room-specific waypoints. Oh, and I didn't test any of this, so hopefully it doesn't make your computer explode. :P

Kooky123

Salutations monkey_05_06!

This does work great I must say, I followed your code exactly and all is well. You are quite good I might add, without even testing??.

Now I was pondering if the following was doable aswell, although bare in mind that its only if you are still willing to help me out further! ;D



Imagine we are point A and we click to point B. Depending on the distance between the two points, there will be anywhere from 0  to 4 random waypoints (long distance gets 4 waypoints, 0 waypoints for a very very short distance between point A and point B, and 1,2 or 3 for any other distances in between).

Each waypoint plotted between point A and B should remain within the same triangle region, random, but always moving in succession towards the target point (where we clicked our mouse, point B).

What say you sir?

Kooky123

I did a better picture here, pardon the crappiness (done with paint)



So if the distance is long enough (between point A and B), we can create a region divided into 4 zones, with an angle of 45 degrees starting from point A (which represents the current player position), and point B is the coordinate where the player clicked the walk icon.

So the first random waypoint (R1) would be placed somewhere randomly in area 1, then the next random waypoint (R2) somewhere in area 2, etc...until finally the player reaches point B, the final destination

Of course depending on the length of distance between point A and point B, there would be 0, 1, 2, 3 or maximum of 4 random waypoints/areas.

Kooky123

I guess this is too hard!! lol

back 2 the drawing board :'(

Khris

The problem is that using AGS's built-in pathfinder, there are two ways of doing this:

Using Character.AddWaypoint():
-downside: walkable areas are ignored

Using a succession of Character.Walk() commands
-downside: no smooth walking, character stops, then resumes

In essence you'd have to completely replace the pathfinding and walking to make this work with walkable areas and look good.

From a mathematical viewpoint, this is almost trivial.
I tried to code a function for this by using a recursion that would send the player to their target in steps by randomly changing the angle of walking every time they walked for 30 pixels, homing in on the target.

I haven't coded the non-blocking part yet and it isn't thoroughly tested:

Code: ags
#define threshold 30
#define angle 30

int _destx, _desty, _cdestx, _cdesty;
bool _is_walking;

void Lurch(this Character*, int x, int y, BlockingStyle blocking) {
  
  if (!_is_walking) { // initial call
    _destx = x;
    _desty = y;
    _is_walking = true;
  }
  
  float dx = IntToFloat(x - this.x), dy = IntToFloat(y - this.y); // vector to target
  float d = Maths.Sqrt(dx*dx + dy*dy);  // distance
  float t = IntToFloat(threshold);
  
  if (d < t) {  // distance too small
    this.Walk(x, y, blocking);
    _is_walking = false;
    return;
  }
  
  // change direction randomly, +/- angle
  float a = Maths.RadiansToDegrees(Maths.ArcTan2(dy, dx));
  a = a + IntToFloat(Random(angle*2) - angle);
  if (a > 180.0) a -= 360.0;
  else if (a <= -180.0) a += 360.0;
  a = Maths.DegreesToRadians(a);
  
  _cdestx = this.x + FloatToInt(Maths.Cos(a)*t);
  _cdesty = this.y + FloatToInt(Maths.Sin(a)*t);
  
  if (blocking == eBlock) { // recursion
    this.Walk(_cdestx, _cdesty, eBlock);
    this.Lurch(_destx, _desty, eBlock);
  }
}


The two defines at the start:
If the player's distance from their target is less than threshold, they walk there directly. Otherwise they stray from the path by a random value of -angle to +angle, every threshold pixels.

monkey0506

Sorry I didn't respond. I've been busy with a few other things. But that's why we have an entire forum full of people and not just me. :P

Kooky123

Kooky is happy....:)

Thank-you for your help!!!!!

SMF spam blocked by CleanTalk