Sliding an Object and Pathfinding problem

Started by MiteWiseacreLives!, Wed 25/09/2013 04:33:11

Previous topic - Next topic

MiteWiseacreLives!

Hey guys!
It was recently brought up by Bilbis too from a editor dev. angle though..
I am trying to effectively use the pushing of objects and blocks around in a game for use in puzzles, sorta like Zelda.
I am pulling my hair out trying to isolate why the object does not travel in a straight path. Ex.
Code: ags

object[objPush].Move(WhatX + 60, WhatY, 4, eBlock, eWalkableAreas); 

Played with the walkable areas, baselines, blocking heights, location of my character.. I cannot make sense of it at all.
Found some clues on the forum which make me think perhaps it's because I have to figure out where, on the 3x3 grid AGS uses for pathfinding, I have to align my objects... or maybe it is trying to avoid walkbehinds???

1) Any insight into the behavior of the pathfinding that will help me to cooperate *with* it?
2) Should I create my own a loop using "object.SetPosition" in one pixel increments, and checking for walkable space each loop? (will that work any  better?)

Khris

If that command doesn't move the object in a straight path, the only reason I can think of is that WhatX and WhatY don't have the proper values (apart of course from a weird walkable area, as opposed to an area at least three pixels tall covering the distance).

Also, why aren't you using:
Code: ags
  object[objPush].Move(object[objPush].X + 60, object[objPush].Y, 4, eBlock, eWalkableAreas);

MiteWiseacreLives!

#2
Its because of the way I am caling the function, probably convoluted..
Code: ags

PushAnimate(1, object[objPush].X, object[objPush].Y); 
...
function PushAnimate(int Way, int WhatX, int WhatY)  

The object 75% of the time will move straight, the other times it likes to move in large U shapes, some times move several pixels away from the destination (if the walkable area is tight and usually to the left or right). I am moving these things around rooms with corners etc, the walkables are not all perpendicular lines, often curved.
I've found that making my own movement function is actually easier than deciphering the pathfinding voodoo. But the coding is more complex, trying to do it right with pointers this time.
Maybe you can have a look at what I've done, if it can be cleaned up the next person with this issue might find it useful.
Code: ags

function PushAnimate(int Way)  
  {
    roller = 0;
    Object *objSlide = object[objPush];
    Object *objStrike;
    int pushX = objSlide.X;
    int pushY = objSlide.Y;

    while (roller != 21)
        {
        
        if (Way == 1)  {  //Left 
          pushX -= 3;          
          spriteEdgeX = pushX - GetViewportX(); // Left edge
          spriteEdgeY = pushY - GetViewportY();  // Bottom edge
          }          
        else if (Way == 2)  {  //up
          pushY -= 3;        
          spriteEdgeX = pushX - GetViewportX(); // Left edge
          spriteEdgeY = (pushY - GetViewportY()) - (Game.SpriteHeight[objSlide.Graphic]/2); //Isometric Top edge
          }
        else if (Way == 3) {  //Right
          pushX += 3;
          spriteEdgeX = (pushX - GetViewportX()) + Game.SpriteWidth[objSlide.Graphic]; //Right edge
          spriteEdgeY = pushY - GetViewportY();  // Bottom edge
          }  
        else if (Way == 4)  {  //Down
          pushY += 3;
          spriteEdgeX = pushX - GetViewportX(); // Left edge
          spriteEdgeY = pushY - GetViewportY();  // Bottom edge
          }
          
        if (GetWalkableAreaAt(spriteEdgeX, spriteEdgeY))  
        // create something to scan across entire side for an object and use  are-objects-colliding...
          {
          if (Object.GetAtScreenXY(spriteEdgeX, spriteEdgeY) != null) {
            objStrike = Object.GetAtScreenXY(spriteEdgeX, spriteEdgeY);
            if (objStrike.Solid)    boink();               
            }
          else  {
            objSlide.SetPosition(pushX, pushY);
            roller ++;
            }            
          }
        else  
          boink();
          
        Wait(1);
        }    
  cEgo.UnlockView();
  hunger -= 3;
  }

I want to next make a little loop to scan across the entire leading side of the sprite for a solid object etc... just worried it might be too many loops per game-cycle or something... any help appreciated :)

Edit:
here's the other thread I referenced earlier,
http://www.adventuregamestudio.co.uk/forums/index.php?topic=48529.msg636462401#msg636462401

* So I've built a function that I am pretty happy with, it scans along the leading edge of the object for anything solid or un-walkable then moves the object, and repeat... I doesn't get messed up in tight spaces like the built in pathfinding, it just goes strait until it can't anymore (nor does it suffer slow-down, I underestimate computers). I would post it but there doesn't seem to be alot of interest in this topic :( , I searched like crazy for some answers when my objects moved like drunk sailors when they have no apparent reason to do so!

Billbis

Quote from: MiteWiseacreLives!I searched like crazy for some answers when my objects moved like drunk sailors when they have no apparent reason to do so!
Well, like you said : it's probably a bug in the "old pathfinder" algorithm which is called (instead of Dijkstra) on straight line movements.
You can post your solution here for future readers. :smiley:
One non-solution that can do the job in certain situations is to NOT trigger straight line movement by adding a +/-3 in your walk/move movements.
Code: AGS
object[objPush].Move(WhatX + 60, WhatY+3, 4, eBlock, eWalkableAreas); //note the "WhatY+3" 
If it's not called repeatidly and on high res game, you won't see any differences.

MiteWiseacreLives!

#4
That's kinda crazy, if you move on an angle it goes straight to target ??? maybe there is some reason for this (more interesting character paths?.. naw that's not it). Here's what I came up with to slide some blocks around a room that will move 60 pixels in a straight line unless something stops them, if then a little sparkle animation at point of impact. I removed the GUI part and the 'pushing' animations, so hopefully I didn't delete any crucial variables, but at least it can give some others ideas for a work-around.
(feel free to clean it up or let me know if I am going way beyond what is necessary... :) )

Code: ags

int spriteEdgeX;
int spriteEdgeY;
int roller;
int limit; 
int start;
bool obstruction;
Object *objStrike;
Object *objSlide;

void boink()    // Call this if object strikes something, will bring a 16x16 character named cBoink to the point of impact and animate
  {
  if (spriteEdgeX < 0) spriteEdgeX = 1;         // and keep it on the screen
  if (spriteEdgeY < 0) spriteEdgeY = 1;
  if (spriteEdgeX > 640) spriteEdgeX = 640;
  if (spriteEdgeY > 480) spriteEdgeY = 480;
  cBoink.ChangeRoom(player.Room, (spriteEdgeX + GetViewportX()), (spriteEdgeY + GetViewportY()) + 8);
  cBoink.Transparency = 20;
  cBoink.Animate(0, 3, eOnce, eBlock);
  cBoink.Transparency = 100;

  obstruction = true;
  }
  
bool checkStrikeY()        // runs along the Y-Axis of the sprite checking for obstructions
  {
    while (spriteEdgeY != limit)
      {     
        if (GetWalkableAreaAt(spriteEdgeX, spriteEdgeY))  // is it walkable?
          {
          if (Object.GetAtScreenXY(spriteEdgeX, spriteEdgeY) != null) // is there an object?
            {
            objStrike = Object.GetAtScreenXY(spriteEdgeX, spriteEdgeY); 
              if (objStrike.Solid)                      // it the object solid?
                {                
                boink();  // call the Boink function and set obstruction to true.
                spriteEdgeY = limit;            
                }
              
              else  {
                obstruction = false;   // nothing in the way!
                spriteEdgeY --;               
                }            
            }
          else
            {
            obstruction = false;   // nothing in the way!
            spriteEdgeY --;              
            }
          }
        else  {
          //  the pixel is not walkable, so call boink
          boink();
          spriteEdgeY = limit;           
          }
      }
      
    spriteEdgeY = start;        
  }
  
bool checkStrikeX()        // runs along the X-Axis of the sprite checking for obstructions
  {
    while (spriteEdgeX != limit)
      {
        if (GetWalkableAreaAt(spriteEdgeX, spriteEdgeY))  
          {
            if (Object.GetAtScreenXY(spriteEdgeX, spriteEdgeY) != null) 
              {
              objStrike = Object.GetAtScreenXY(spriteEdgeX, spriteEdgeY);
              if (objStrike.Solid && objStrike != objSlide)   
                {
                boink();     // there is an object there and it is solid!        
                spriteEdgeX = limit;              
                }
              
              else  {
                obstruction = false;
                spriteEdgeX ++;   // nothing in the way!
                } 
            }
          else  {
            obstruction = false;
            spriteEdgeX ++;   // nothing in the way!
            }             
          }
        else  {
          boink();          
          spriteEdgeX = limit;
          }
      }
    
    spriteEdgeX = start;
  }


  
function PushAnimate(int Way, int ObjPush)  
  {
    roller = 0;
    objSlide = object[objPush];
    
    int pushX = objSlide.X;
    int pushY = objSlide.Y;
    
    if (Way == 1) { // Left
      spriteEdgeY = (pushY - 1) - GetViewportY();  // Bottom edge
      start = spriteEdgeY;
      limit = (pushY - GetViewportY()) - (Game.SpriteHeight[objSlide.Graphic]/2) + 1; //Isometric Top edge
      
    }
    else if (Way == 2)  { // Up
      spriteEdgeX = (pushX - GetViewportX()) + 1; // Left edge
      start = spriteEdgeX;
      limit = ((pushX - GetViewportX()) + Game.SpriteWidth[objSlide.Graphic]) - 2; //Right edge     
      
    }
    else if (Way == 3) { //Right
      spriteEdgeY = (pushY - 1) - GetViewportY(); // Bottom edge
      start = spriteEdgeY;
      limit = (pushY - GetViewportY()) - (Game.SpriteHeight[objSlide.Graphic]/2) + 1; //Isometric Top edge
      
    }
    else if (Way == 4) { //Down
      spriteEdgeX = (pushX - GetViewportX()) + 1; // Left edge
      start = spriteEdgeX;
      limit = ((pushX - GetViewportX()) + Game.SpriteWidth[objSlide.Graphic]) - 2; //Right edge       
      
    }
    
    while (roller != 21)
        {
        
        if (Way == 1)  {  //Left 
          pushX -= 3;          
          spriteEdgeX = pushX - GetViewportX(); // Left edge
          checkStrikeY();
          }          
        else if (Way == 2)  {  //up
          pushY -= 3;        
          spriteEdgeY = (pushY - GetViewportY()) - (Game.SpriteHeight[objSlide.Graphic]/2); //Isometric Top edge
          checkStrikeX();
          }
        else if (Way == 3) {  //Right
          pushX += 3;
          spriteEdgeX = (pushX - GetViewportX()) + Game.SpriteWidth[objSlide.Graphic]; //Right edge          
          checkStrikeY();
          }            
            
        else if (Way == 4)  {  //Down
          pushY += 3;          
          spriteEdgeY = pushY - GetViewportY();  // Bottom edge
          checkStrikeX();         
          }       
        
        if (!obstruction)
          {
          objSlide.SetPosition(pushX, pushY);
          roller ++;          // If nothing in the way, advance the object.        
          }
        else 
          roller = 21;
          
        Wait(1);
        }    
  }

SMF spam blocked by CleanTalk