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

Login with username, password and session length

 
Advanced search

877367 Posts in 32861 Topics- by 24299 Members - Latest Member: aarkling

May 19, 2013, 10:58:28 AM
TIGSource ForumsDeveloperTechnical (Moderators: Glaiel-Gamer, ThemsAllTook)Wall Walking
Pages: [1] 2
Print
Author Topic: Wall Walking  (Read 2885 times)
Paul Eres
Level 10
*****


Also known as RinkuHero.

RinkuHero
View Profile WWW Email
« on: January 20, 2009, 06:08:13 PM »

My friend has struggled with this for months, and I thought she was just bad at programming, thinking the issue is simple, so I asked to look at the problem, and I can't solve it either after a day of trying. It's something which on the face of it seems like it should be trivially simple, yet no method I've tried works. The issue is...



How do you code movement like the geemer's there?

Basically we want this behavior:

Move right if you're on the floor. Move up if you come to a wall. Move on the ceiling if you come to that. Move down if you come to another wall.

E.g. If a geemer is surrounded in a cage, it would go around the inside of that cage in a counter-clockwise manner.

E.g. If a geemer is on a block floating in space, it would go around that block in a clockwise manner.

We've tried various methods, some of which seem to work but fail for some special cases. Here is one method. The code is GML but you should be able to work it out regardless.

Code:
if sprite_index == spr_geemer_top
   if not place_free(x, y+1)
   {
      if not place_free(x+1, y)
         sprite_index = spr_geemer_left
      x += 1;
   } else sprite_index = spr_geemer_right

if sprite_index == spr_geemer_right
   if not place_free(x-1, y)
   {
      if not place_free(x, y+1)
         sprite_index = spr_geemer_top
      y += 1;
   } else sprite_index = spr_geemer_bottom

and etc. for the other directions. This is her (komera's) method. This works for three directions but fails for one -- always the direction that's placed first in the script. I conceptually understand why one of the four must always fail, but can't put it into words.

Another method:
Code:
move_contact_solid(direction-90, -1);
if ((old_x == x) and (old_y == y))
   direction += 90;
speed = 1;
old_x = x; old_y = y;

switch (direction)
{
   case 180: sprite_index = spr_geemer_bottom; break;
   case 0: sprite_index = spr_geemer_top; break;
   case 270: sprite_index = spr_geemer_right; break;
   case 90: sprite_index = spr_geemer_left; break;
}

I thought that would work, but it doesn't. They never change direction.

Another method:
Code:
if not place_free(x,y-1) direction=180;
if not place_free(x,y+1) direction=0;
if not place_free(x-1,y) direction=270;
if not place_free(x+1,y) direction=90;

speed=1;

if (vspeed>0 and not place_free(x,y+vspeed))
   move_contact_solid(270,-1);
if (vspeed<0 and not place_free(x,y-vspeed))
   move_contact_solid(90,-1);
if (hspeed>0 and not place_free(x+hspeed,y))
   move_contact_solid(180,-1);
if (hspeed<0 and not place_free(x-hspeed,y))
   move_contact_solid(0,-1);

switch (direction)
{
   case 180: sprite_index = spr_geemer_bottom; break;
   case 0: sprite_index = spr_geemer_top; break;
   case 270: sprite_index = spr_geemer_right; break;
   case 90: sprite_index = spr_geemer_left; break;
}

This one works for the inside of cages, but doesn't for the outside of blocks. They never change direction when on the outside of a block, but it works perfectly for the inside of cages.

Help! This problem is crazy! It should be simple to do this, I mean, Metroid did it, so why can't we figure it out?
« Last Edit: January 20, 2009, 06:11:41 PM by rinkuhero » Logged

Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW Email
« Reply #1 on: January 20, 2009, 06:29:01 PM »

I think the key is to check collision on the corners of the sprite e.g. (x + 16, y + 16) rather than the centres of each edge (x, y + 16) etc.

Logged

Gnarf
Level 10
*****



View Profile WWW
« Reply #2 on: January 20, 2009, 06:39:10 PM »

Code:
if sprite_index == spr_geemer_top
   if not place_free(x, y+1)
   {
      if not place_free(x+1, y)
         sprite_index = spr_geemer_left
      x += 1;
   } else sprite_index = spr_geemer_right

if sprite_index == spr_geemer_right
   if not place_free(x-1, y)
   {
      if not place_free(x, y+1)
         sprite_index = spr_geemer_top
      y += 1;
   } else sprite_index = spr_geemer_bottom

and etc. for the other directions. This is her (komera's) method. This works for three directions but fails for one -- always the direction that's placed first in the script. I conceptually understand why one of the four must always fail, but can't put it into words.

Make it else if sprite_index == spr_geemer_right instead of just if? Way it is now, it looks as if if the direction is changed in the first if-test, then it might change again in a later one.

This one works for the inside of cages, but doesn't for the outside of blocks. They never change direction when on the outside of a block, but it works perfectly for the inside of cages.

Sure that's not a problem for the other ones as well? If you're only checking if the tile you're moving into (not) is open when deciding if you have to turn, then I'd imagine it only works when you turn because you crash into something (i.e. when you're in a cage). For moving around things, I think you need to check if the tile to your right (that is, to the right when you're moving up, underneath when you're moving right, etc.) is empty space; if it is you need to turn to your right.
Logged

This is IT -- the missing link in the chain of my existence. Rondo's SPINNING BUDDHA is what I need to make me complete.
Paul Eres
Level 10
*****


Also known as RinkuHero.

RinkuHero
View Profile WWW Email
« Reply #3 on: January 20, 2009, 06:40:50 PM »

The four corners method sounds great, will try experimenting with that tomorrow.

I tried the if else thing actually, and then for some reason they didn't move at all. I'm not sure why.
Logged

Glaiel-Gamer
Moderator
Level 10
******


Stoleurface!


View Profile WWW Email
« Reply #4 on: January 20, 2009, 06:42:05 PM »

rather than checking for points, you want to use a tile map and move one tile at a time. After each tile, you recheck your surroundings.

Case 1: tile below you, to the left, to the right, or above. Keep moving the same direction.

Case 2: you're on a corner. Turn CLOCKWISE and continue.

Case 3: You are in the middle of nowhere (i.e. diagionally away from a tile). Turn COUNTER CLOCKWISE and continue.

Case 4: Tiles on 3 sides. Turn CLOCKWISE TWICE and continue

Those are the only 4 cases, and keep in mind that rotating is a cyclic function. Rotating clockwise 4 times should give you your original direction.

This should work in all the cases I can think of, and any exceptions should be easy to implement specifically.
Logged

Gnarf
Level 10
*****



View Profile WWW
« Reply #5 on: January 20, 2009, 07:14:41 PM »

This should work in all the cases I can think of, and any exceptions should be easy to implement specifically.

If you have tight (1 tile wide) open spaces in the map and such, then you still have to check different tiles depending on the direction you're moving in.

E.g.,
Code:
ooooo
o x
o  oo
o  oo

You're either moving along the tiles above you, and can carry on (case 1), or you were moving along tiles to the right, and must turn (case 3).

(At least I think what you meant was that you could do the same checks no matter what direction you were going in.)



Seems to me that the first thing to check is if you're still standing on something, if something is under your feet. If you're moving to the right, that means checking if the y+1 tile is solid; down, x-1; left, y-1; up, x+1. If that tile is empty space, you turn clockwise. If it was not empty space, you have to check if you're crashing into something instead. If you're moving up, you then check the y-1 tile, etc. If that tile is solid, you have to turn counter-clockwise (possibly twice).
Logged

This is IT -- the missing link in the chain of my existence. Rondo's SPINNING BUDDHA is what I need to make me complete.
Farbs
Man
Level 9
*



View Profile WWW Email
« Reply #6 on: January 20, 2009, 07:32:44 PM »

rather than checking for points, you want to use a tile map and move one tile at a time. After each tile, you recheck your surroundings.

Agreed. Solving the problem in a non-tile manner adds a lot of unnecessary complexity.
Logged
Glaiel-Gamer
Moderator
Level 10
******


Stoleurface!


View Profile WWW Email
« Reply #7 on: January 20, 2009, 08:51:01 PM »

This should work in all the cases I can think of, and any exceptions should be easy to implement specifically.

If you have tight (1 tile wide) open spaces in the map and such, then you still have to check different tiles depending on the direction you're moving in.

E.g.,
Code:
ooooo
o x
o  oo
o  oo

You're either moving along the tiles above you, and can carry on (case 1), or you were moving along tiles to the right, and must turn (case 3).

Ok, then it's still a simple exception. Also you can realize that since you know his orientation, you don't need to check the state of the tiles behind or above him (except in case 4, which you could think of as 2 steps of case 2).

In this case the problem becomes

o<

which could be considered a case 5. You only (save case 4 again) need to check the tiles underneath and in front of him.

so:
Code:
o<
 o

is turn clockwise

Code:
<
o
is go forward

Code:
<

is turn counter-clockwise

and

Code:
o<
 
is turn counter-clockwise (you need not ever turn counter clockwise here, because that would require your character to flip and not simply rotate. Think of the bottom of the < having feet, not the top)


Note, I am not marking the tiles above or behind the <. You have to think direction independently for this, rotate your head for the other cases and swap the clockwise/counterclockwise if you want him going the other direction.

Case 4 above becomes 2 iterations of the first case in this method.
« Last Edit: January 20, 2009, 08:58:59 PM by Glaiel-Gamer » Logged

Alex May
...is probably drunk right now.
Level 10
*


hen hao wan


View Profile WWW Email
« Reply #8 on: January 21, 2009, 12:48:09 AM »

Without having read the thread, you want to check the two tiles (ahead of) and (ahead of and below) the entity.
Pixel 11Pixel 11Pixel 11Pixel 11
Pixel 11Pixel 06Pixel 0BPixel 11
Pixel 0FPixel 0FPixel 0BPixel 11

Your guy is Pixel 06 and moving right. You will check the two Pixel 0B tiles.

Your guy wants to hug the wall. so you want to be checking a) if it can move in that direction and b) if the floor is missing ahead. You check the two green tiles. So you need to know which direction is "down" for your guy.

If the top green tile is occupied it will turn the corner in its current square and be going up, with its own version of down pointing right.

Otherwise, since we know that top green square is empty, if the floor is missing ahead then it will turn that corner onto the following edge of the tile it's on. i.e. it will transition through the top green tile and onto the bottom green tile, moving down and with its own version of "down" pointing left.

So in the case where there's a wall obstruction, you don't move, and turn anticlockwise if you're moving clockwise. In the case where the floor is missing, you skip a tile and turn clockwise if you're moving clockwise.
« Last Edit: January 21, 2009, 12:53:29 AM by haowan » Logged

Ishi
Pixelhead
Level 10
******


coffee&coding


View Profile WWW Email
« Reply #9 on: January 21, 2009, 01:30:52 AM »

Pixel 11Pixel 11Pixel 11Pixel 11
Pixel 11Pixel 06Pixel 0BPixel 11
Pixel 0FPixel 0FPixel 0BPixel 11

Yeah I'd probably go for this actually, no redundant checking.
Logged

mirosurabu
Guest
« Reply #10 on: January 21, 2009, 02:31:17 AM »

Have you thought about using movement vectors to represent your orientation in the map? From what you mentioned in your original post, it doesn't seem so.

You can use UP and RIGHT vectors to represent the orientation. UP vector would tell you what's up for your object at the time. This could help you to determine whether your object should be vertically flipped or not. It can also be helpful if you want to implement jumping. RIGHT vector would tell you where your object should move when you press right key. The opposite of this vector would be where your object should move when you press left key.

Vectors could be represented by two-element array where first element represent X component and second Y component. Using vectors would require you to change both movement code and collision checking code. So instead of having code like:

Code:
if (key right pressed)
    x++;
if (key left pressed)
    x--;

You'll have something like:

Code:
if (key right pressed)
{
    x += right[0];
    y += right[1];
}
if (key left pressed)
{
   x -= right[0];
   y -= right[1];
}

By using vectors you could easily locate tiles for collision checking and in general for "can I move/rotate" queries.

Further, changing direction would be very simple. Pseudocode would look something like this:

Code:
if (key pressed right)
{
    obstruction_type = can_i_move(right)
    if (move == "YOU CAN")
       move(right);
    else
       if (can_i_rotate(right, obstruction_type))
           rotate(right, obstruction_type);
}

can_i_move(vector) is a function which queries whether you can move using actual movement vectors. Returns "YOU CAN" if you can or "BLOCKED" or "MISSING" depending on what's the reason why you can't move. It takes vector (delta movement) parameter because it's necessary to know where you are trying to move.

move() is function which performs moving. Updates position of your object and do some other stuff.

can_i_rotate(vector, obstruction, obstruction) is a functions which asks whether you can change your direction (whether you can rotate/change your vectors). There are situations in which you cannot so you have to call this function.

If you can rotate, then you call rotate(vector) to update vectors and new position of your object.
« Last Edit: January 21, 2009, 02:41:05 AM by mirosurabu » Logged
Gnarf
Level 10
*****



View Profile WWW
« Reply #11 on: January 21, 2009, 04:21:41 AM »

which could be considered a case 5. You only (save case 4 again) need to check the tiles underneath and in front of him.

And checking the tiles underneath and in front of you will do the trick in any case anyway (though you may or may not want to check the tile in front of you again after turning counter-clockwise).

Without having read the thread, you want to check the two tiles (ahead of) and (ahead of and below) the entity.
Pixel 11Pixel 11Pixel 11Pixel 11
Pixel 11Pixel 06Pixel 0BPixel 11
Pixel 0FPixel 0FPixel 0BPixel 11

That's pretty much what I suggested. Only I'd do,

Pixel 11Pixel 11Pixel 11Pixel 11
Pixel 11Pixel 06Pixel 0BPixel 11
Pixel 0FPixel 0BPixel 11Pixel 11

instead. And check the tile underneath first instead of the one ahead first. Then you wouldn't need do any tile skipping business when turning clockwise. For whatever that's worth.
Logged

This is IT -- the missing link in the chain of my existence. Rondo's SPINNING BUDDHA is what I need to make me complete.
Alex May
...is probably drunk right now.
Level 10
*


hen hao wan


View Profile WWW Email
« Reply #12 on: January 21, 2009, 04:32:12 AM »

Without having read the thread, you want to check the two tiles (ahead of) and (ahead of and below) the entity.
Pixel 11Pixel 11Pixel 11Pixel 11
Pixel 11Pixel 06Pixel 0BPixel 11
Pixel 0FPixel 0FPixel 0BPixel 11

That's pretty much what I suggested. Only I'd do,

Pixel 11Pixel 11Pixel 11Pixel 11
Pixel 11Pixel 06Pixel 0BPixel 11
Pixel 0FPixel 0BPixel 11Pixel 11

instead. And check the tile underneath first instead of the one ahead first. Then you wouldn't need do any tile skipping business when turning clockwise. For whatever that's worth.
This would also work but relies on the entity moving into the next square first. It might look smoother if as soon as they are about to enter the next square, they do the check, and then play the animation that takes them through that square and around the corner. But yes, not that big a difference in the end probably.

With my method you can do both checks at the same time: the moment the entity moves from one square into the next.
Logged

BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #13 on: January 21, 2009, 04:38:38 AM »

By using vectors you could easily locate tiles for collision checking and in general for "can I move/rotate" queries.
Good logic, but you're implementation is funny. Does GameMaker not actually have a vector type? Or a vector library?
Logged
mirosurabu
Guest
« Reply #14 on: January 21, 2009, 05:08:21 AM »

Actually, I know very little about GM. I tried to make above code as much PL-independent as possible.

Oh, and I just 'noticed' that you (rinku) was actually trying to implement wall walking for non-player characters and not for player-controlled. Well, I should pay more attention to OP's in the future. And I should strikethrough all the pseudocode I wrote in previous post.
Logged
Pages: [1] 2
Print
Jump to:  

Theme orange-lt created by panic