Search code examples
c#xnagame-physics

Xna Isometric point to click movement


I'm working on an isometric game (diamond grid) and I've stumbled across a minor problem regarding a character movement.

I'm using A* to find a path between 2 points and then I want to move my character from point A to point B going through all the tiles which form the path but I can't find a way to do this , I mean a simpler and accurate method.

So far I've scrapped this piece of code but it's kinda "rusty"

public void Destination(tile destination)
{ 
    for (int i = 0; i < 8; i++)
    {
        if (AdjacentTile[i] == destination)
        {
            characterDirection = i; 
        }
    }
    animation.changeSpriteDirection(characterDirection); //After I found which adjacent tile is the next destination I change the character direction based on it's position (1 = North , 2 = Nort Est etc) .. so the Y of the Animation_sourceRectangle it's changed//

    Vector2 Position;
    Position.X = current_characterTile.X - destination.X;
    Position.Y = current_characterTile.Y - destination.Y;

    rotation = (float)Math.Atan2(-Position.X, Position.Y); 

    moveVector = (Vector2.Transform(new Vector2(0, -1), Matrix.CreateRotationZ(rotation))) * characterSpeed;
    movingCommand = 1; // the character is supposed to be moving..
    Move(); //this function moves the sprite until the *tile.i and tile.j* of the character is the same as tile.j and tile.i of the destination

    //something like this  
    if ( characterTile.i == destination.i && characterTile.j == destination.j) 
       movingCommand = 0 //stop
    else 
       character_Position += moveVector; 
}

If anyone could give me a hint on what to do or help me I'll be very grateful.
Thank You.


Solution

  • Possibilities:

    • At each tile, determine the character's speed vector and also determine how much time it will take for the character to move to next tile. When that time elapses, immediately begin moving to the next tile. (This is what I implemented below.)
    • At each tile, determine the character's speed vector. Then, when the character is sufficiently close to the next tile (say, the difference between their X and Y coordinates is less than 2 pixels?), snap it to the tile and begin moving to the next tile. This will causes artifacts and be in general less precise.

    A solution:

    Let's assume you already ran your pathfinding algorithm and found a linked list of a tiles that you must go through to arrive at target. Let's also assume those tiles cannot become blocked partway through the movement (it is simple to modify the algorithm if they can, though).

    I usually do something like this to handle this problem:

    1. Run the pathfinding algorithm, which returns a List, if a path exists.
    2. character.Path = theListThatAStarReturned;
    3. character.beginMovingToTarget(character.Path[0]);
    4. character.Path.RemoveAt(0);

    The beginMovingToTarget() method will determine the velocity vector and also determine the the time needed to arrive at the tile. When the time is reached, we immediately go to the next tile, until the Path is empty. Let's call this time variable character.timeToArrival.

    Update():

    if (!character.Moving) return; // Or just don't execute the rest of this code.
    character.position += character.speed * elapsedSeconds;
    character.timeToArrival -= elapsedSeconds;
    // Did the character arrive in a tile?
    if (character.timeToArrival <= 0)
    {
      // This will ensure the character is precisely in the tile, not a few pixels veered off.
      character.position = character.movingToTile.position; 
      if (character.Path.Count == 0)
      {
        character.Moving = false;
        // We are at final destination.
      }
      else
      {
        character.beginMovingToTarget(character.Path[0]);
        character.Path.RemoveAt(0);
      }
    }
    

    And the beginMovingToTarget(targetTile) function:

    this.movingToTile = targetTile;
    Vector2 direction;
    direction = targetTile.position - this.position;
    this.timeToArrival = direction.Length() / this.speedPerSeconds;
    direction.Normalize();
    direction *= this.speedPerSeconds;
    this.speed = direction;
    // Here, you may also want to change the character's animation, if you want to, or you may do that directly in the Draw() method based on its speed vector.
    

    Make sure the division is in floats, not integers.