Walkable areas like BASS - no diagonal movement.

Started by markbilly, Sun 06/01/2008 19:04:08

Previous topic - Next topic

markbilly

How do I/can I change the AGS walkable areas so that my characters only walk through purely vertical and horizontal lines?
If you have played Beneath a Steel Sky then that is what I'm after really.

The closest I've come is by drawing a 'grid' for the areas, rather than using the fill feature. This however doesn't give the right effect, as the character jitters around a lot.

I wasn't sure if this was a beginners question or not, please feel free to move it.

Any suggestions, solutions or known plugs-ins that do this would be a great help.

Thanks,
markbilly
 

Galen

Hmm... you can't disable diagonal walking automatically so you could try getting the x/y of the mouse click and making the character walk along the y axis then when it's done move along the x axis, of course this would prevent pathfinding, unless you have your rooms laid out without and real obstacles.

markbilly

I need pathfinding really. This is why it has been so difficult to find a solution.

Also, my game is in full flow development wise, with a lot of rooms/objects/scripting complete. I had ignored it up until now, but it was something I always intended for it.
 

monkey0506

#3
Well like Crazy says, it would mess up the path finding unless your walkable areas are rectangular...but even then if you have any solid Characters or Objects that could still throw it off.

You could perhaps set up an invisible dummy Character to find out the proper (x, y) co-ordinates...

It's probably not perfect, and it's definitely untested...but...it might...do something. Maybe.

Edit: Actually, it's not necessary to use a dummy Character at all. You can just use the PlaceOnWalkableArea function to find the proper (x, y) point. See my post below for the code.

markbilly

That's great of you to write it and I'll definitely test it, can you tell me where exactly to put the code in the global script and whether there is anything I need to delete? etc. Thanks.
 

monkey0506

#5
Since this is a function, you just put it anywhere in your global script outside of all other functions. You can just put it at the top of your global script if you'd like.

Then instead of using commands like player.Walk(X, Y) you would use CharacterWalk(player, X, Y).

The function is designed for use with a dummy character. This should just be a blank character with no sort of interactions or anything, and a blank sprite for the normal view. It also probably wouldn't be a bad idea to just remove the dummy character from the room once you're done with the walking (I'll update the above code).

It might be possible to do this without a dummy character, but I'm unsure if the character might flash to the (x, y) and then back before walking. If you would like to remove the need for a "dummy" character, you could try doing this instead:

Code: ags
function CharacterWalk(Character* theCharacter, int x, int y) {
  if (theCharacter == null) return;
  theCharacter.StopMoving(); // make sure theCharacter isn't moving
  int prev_x = theCharacter.x; // store the old co-ordinates
  int prev_y = theCharacter.y;
  theCharacter.x = x;
  theCharacter.y = y;
  theCharacter.PlaceOnWalkableArea(); // make sure theCharacter is on a walkable area
  if (theCharacter.Room != player.Room) { // if theCharacter is in a different room
    return; // abort the function; theCharacter has already teleported to the new co-ordinates
  }
  int new_x = theCharacter.x; // grab the new co-ordinates
  int new_y = theCharacter.y;
  theCharacter.x = prev_x; // move theCharacter back to the starting position
  theCharacter.y = prev_y;
  prev_x--; // offset these values to make sure the loop is run at least once
  prev_y--;
  while (((theCharacter.x != new_x) || (theCharacter.y != new_y)) && ((prev_x != theCharacter.x) || (prev_y != theCharacter.y))) {
    prev_x = theCharacter.x; // store previous x co-ordinate
    prev_y = theCharacter.y; // store previous y co-ordinate
    theCharacter.WalkStraight(x, theCharacter.y, eBlock);
    theCharacter.WalkStraight(theCharacter.x, y, eBlock);
  }
}


This may show the Character instantaneously teleporting to the new (x, y) co-ordinates, then teleporting back before walking. As I still haven't tested my code out, I can't say. But this might remove the need for the "dummy" character.

Edit: I've now updated this code because the WalkStraight command is non-blocking by default and it wasn't working along the x-axis. Now it should be functioning exactly as designed, though note this function is blocking which means that you can't do anything else while the Character is walking this way; not even tell him somewhere else to go. I'm currently developing a module to allow a non-blocking alternative.

Stupot

You could make the Walkable Area 1 pixel wide so that the character cannot move about diagonally.
As long as each pixel is flush with the ones either side of it there shouldn't be a problem.  And if you only want him to go horizontally or vertically then you just have to reflect this in the walkable area.
MAGGIES 2024
Voting is over  |  Play the games

Radiant

Quote from: Stupot on Sat 12/01/2008 09:43:14
You could make the Walkable Area 1 pixel wide so that the character cannot move about diagonally.

As I recall, that hampers the AGS pathfinding algorithm.

monkey0506

#8
Actually Stupot (as Radiant said (he beat me!!! >:( :=)) this wouldn't work...the character wouldn't be able to move at all. AGS internally scales the walkable area mask down by a factor of three, and then if you click on a non-walkable area it only scans every 5th pixel to find the nearest walkable area:

Quote from: Pumaman on Mon 07/01/2008 22:20:47Pathfinding is quite an expensive thing to do, and AGS scales down the walkable area mask by a factor of 3 before calculating it, in order to improve the speed.

But also, if you click in a non-walkable area, for performance reasons AGS scans every 5th pixel moving outwards to find the nearest walkable area, so potentially with <5-pixel thick areas it might skip over the walkable area altogether; maybe this is causing your problem. I'll investigate whether this can be improved.

So making walkable areas less than five pixels wide could lead to occasions where the characters wouldn't be able to move at all.

This is the reason why such a function would be necessary. As I've said, I still haven't taken the time to test my own code, but the idea is that by using WalkStraight one can ensure no diagonal movement; but that because WalkStraight obeys walkable areas, it might not find the entire path at once. So I set up a loop such that as long as the character moved at least one pixel in any direction it will continue trying to move toward the given (x, y) point (rather, toward the nearest point on a walkable area relative to the given (x, y)). Note however that this behaviour does inherently make the function blocking. An alternative to make the function non-blocking would be to store these variables outside the function and then update them (as necessary) from within rep_ex, but this could easily become unwieldy...though if the code does in fact work a module could probably do the trick.

markbilly

Sorry, I'm not so good at the code side of things unless I'm writing it myself (and its simple). 'theCharacter' in the script, what does that refer to? Surely if I replace this with the playable character, then any other characters will walk diagonally.

I'm a little confused :P

Say I had a character, ROGER with script name 'cEgo' and I pasted this code into the global script, what would I need to do next? That's basically what I want to know. Thanks again.
 

monkey0506

theCharacter is a Character pointer (Character*). That means that it can point to any Character in the game. So if you wanted to make theCharacter point to the player Character you could do:

Code: ags
CharacterWalk(player, X, Y);


To use cEgo you would use:

Code: ags
CharacterWalk(cEgo, X, Y);


And so forth. You shouldn't need to modify the function at all unless I messed up somewhere and there's a bug in the code.

markbilly

Oh, OK that makes sense. Its just in the normal course of the game (i.e point and click) the character still walks diagonally as normal.

What am I doing wrong, I presume I don't have to change the ProcessClick(...) bit do I?
 

monkey0506

#12
In the on_mouse_click function, you'll need to capture the walk mode like this:

Code: ags
if (button == eMouseLeft) {
  // blah blah blah any extra codes here
  if (mouse.Mode == eModeWalkto) CharacterWalk(player, mouse.x, mouse.y);
  else ProcessClick(mouse.x, mouse.y, mouse.Mode);
}


ProcessClick will automatically use the normal, built-in Walk function. You have to tell AGS to use your custom function instead for walking. ;)

Other than that, I have just for the first time tested my code. Apparently it was canceling the WalkStraight command for the X-axis as soon as it encountered the WalkStraight for the Y-axis (because the calls were non-blocking).

However, I have verified that the second code snippet I came up with (that doesn't require a dummy character) works fine (in a blocking manner, so long as I make sure both WalkStraight commands block!). I'll update the code now, so make sure you grab the newest version (I'll add a note once I've updated it so you know it's been changed).

markbilly

OK, that works. It is a little jittery in small walkareas however, and occasionally if a walk action is scripted, or when walking to an object it reverts back to the default style.

Still, I'll try and iron those things out if you like. It'll give me some practice.

Thanks again, that was a big help.
 

SMF spam blocked by CleanTalk