Sign in to follow this  
shadetype

Advance Wars type movement

Recommended Posts

Hello I'm having a problem and I'm hoping that someone here can point me in the right direction. I am currently using C# with the XNA framework, and I'm currently attempting to make myself a turn based strategy game, akin to advance wars, however I'm having a fair amount of trouble learning how to get the tiles that are within the units movement range. I'm currently using three arrays, the int[,] ones, as maps. The first one basically just puts the graphics down on the board, nothing to special. The second one puts the player down onto the board and the third one is where if nodes are found in the movement range of the unit, this array will have all of the 0's in that range set to 1's so that a visible path will light up. Basically, what I'm stuck on is the fact that I can't get this sort of movement: ---*--- --***-- -**1**- --***-- ---*--- 1 being the player, and the stars being the places I wish to light up to show that you can move there. The - are just so I could show the style of movement that I wanted, since in the preview of this post it the stars automatically went to the left side of the post when it was simply spaces. I created a struct which assigns a movement cost to each integer in the first array. It'll probably be a tad easier to explain if I put the code that I'm using up:
public TileEffects[,] te = new TileEffects[H_LENGTH, W_LENGTH];

        public Map()
        {
/*This goes through all of the tiles and assigns a movement cost, depending on their value in the array.*/
            for (int x = 0; x < W_LENGTH; x++)
            {
                for (int y = 0; y < H_LENGTH; y++)
                {
                    if (MAP[y, x] == 0)
                    {
                        te[y,x].tileMovementCost = 1;
                        te[y,x]._RealTileLoc = new Vector2(x, y);
                        te[y,x].TileLoc = new Vector2(x * 32, y * 32);

                    }
                    else if (MAP[y, x] == 1)
                    {
                        te[y,x].tileMovementCost = 2;
                        te[y,x]._RealTileLoc = new Vector2(x, y);
                        te[y,x].TileLoc = new Vector2(x * 32, y * 32);
                    }
                    else if (MAP[y, x] == 2)
                    {
                        te[y,x].tileMovementCost = 3;
                        te[y,x]._RealTileLoc = new Vector2(x, y);
                        te[y,x].TileLoc = new Vector2(x * 32, y * 32);
                    }
                    else if (MAP[y, x] == 3)
                    {
                        te[y,x].tileMovementCost = 4;
                        te[y,x]._RealTileLoc = new Vector2(x, y);
                        te[y,x].TileLoc = new Vector2(x * 32, y * 32);
                    }
                }
            }
        }

public struct TileEffects
    {
//Movement cost assigned to a tile
        public int tileMovementCost;
//The tile location * 32 (32 because that's how large the perimiter on each side is)
        public Vector2 TileLoc;
//The x and y value of where the tile is on the map, starting from 0
        public Vector2 _RealTileLoc;
    }

Those are the two elements that I'm using to build the over all map, and here is the method in which the player clicks on the unit and the path should appear.
TileEffects CurrentTile = new TileEffects();
                List<TileEffects> t = new List<TileEffects>();
                foreach (TileEffects n in map.te)
                {
//Checks for the tile that the player is currently occupying
                    if (n._RealTileLoc.X == (float)TileX && n._RealTileLoc.Y == (float)TileY)
                    {
                        CurrentTile = n;
                        MessageBox.Show(Convert.ToString(CurrentTile._RealTileLoc.X + ":" + CurrentTile._RealTileLoc.Y));
                    }
                }

/*This is where I'm having my main problem. This algorithm scrolls through each of the tiles on the map to check whether they're in range. If they are, I add them into a list. Unfortunately, it isn't exactly producing desireable results.*/
                for (int XX = 0; XX < 20; XX++)
                {
                    for (int YY = 0; YY < 20; YY++)
                    {
                        if (map.te[YY, XX]._RealTileLoc.X == (float)TileX && map.te[YY, XX]._RealTileLoc.Y == (float)TileY)
                        {
                        }
                       else if (Math.Abs(CurrentTile._RealTileLoc.X - (XX - map.te[YY, XX].tileMovementCost) - 1) +
                            Math.Abs(CurrentTile._RealTileLoc.Y - (YY - map.te[YY, XX].tileMovementCost) - 1) <= p.Movement)
                        {
                            t.Add(map.te[YY, XX]);
                        }
                    }
                }
                foreach (TileEffects K in t)
                {
/*Goes through the list which was filled in above and sets the int[,] movepattern elements to 1 if they are in the list.*/
                    if (map.MovePattern[(int)K._RealTileLoc.Y, (int)K._RealTileLoc.X] == 0)
                        map.MovePattern[(int)K._RealTileLoc.Y, (int)K._RealTileLoc.X] = 1;
                }
And here are the int[,] arrays that I'm using, where H_LENGTH and W_LENGTH = 20:
public int[,] MAP = new int[H_LENGTH, W_LENGTH]
        {
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,3,3,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0},
            {0,0,2,2,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0},
            {0,0,2,2,1,1,1,0,0,0,0,0,2,2,1,1,1,0,0,0},
            {0,0,2,2,1,1,1,0,0,0,0,0,2,2,1,1,1,0,0,0},
            {0,0,2,2,1,1,1,0,0,0,0,0,2,2,1,1,1,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,2,2,1,1,1,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,2,2,1,1,1,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,2,2,1,1,1,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,3,3,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0},
            {0,0,2,2,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0},
            {0,0,2,2,1,1,1,0,0,0,0,0,2,2,1,1,1,0,0,0},
            {0,0,2,2,1,1,1,0,0,0,0,0,2,2,1,1,1,0,0,0},
            {0,0,2,2,1,1,1,0,0,0,0,0,2,2,1,1,1,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,2,2,1,1,1,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,2,2,1,1,1,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,2,2,1,1,1,0,0,0}
        };
        public int[,] PLAYERS = new int[H_LENGTH, W_LENGTH]
        {
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
        };
        public int[,] MovePattern = new int[H_LENGTH, W_LENGTH]
        {
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
        };
I apologize for such a large post, but I just don't understand why I can't get a proper tactical type movement for my grid going. If anyone could point me in the right direction, I'd be very appreciative. I'm not looking for someone to code the solution for me just so I can copy, I'm after some logic that I can use to get some Advance Wars type movement happening. Thank you in advance.

Share this post


Link to post
Share on other sites
Quote:
Original post by shadetype
The second one puts the player down onto the board (...)

What exactly do you mean with that?

Quote:
Basically, what I'm stuck on is the fact that I can't get this sort of movement:

---*---
--***--
-**1**-
--***--
---*---

Look up 'manhattan distance' if your terrain is not going to have any obstacles. You'll probably want to handle obstacles though, but you're likely still going to need manhattan distance calculations to determine what tiles can be hit by long-range units. Tip: use [code] [/code] tags for monospace text.

Look up on 'flood-fill' algorithms if you want to determine all possible destination tiles. You'll want to keep track of how many 'movement' the 'paint' has left while filling tiles, so you know when to stop filling tiles.

Once you've picked a destination, you can use a pathfinding algorithm (such as A* (A-star)) to determine how to get there (or, during the flood-fill pass, keep track of what tile 'points' to what other tile, so it comes down to following a reversed linked list of tiles).

Share this post


Link to post
Share on other sites
Some additional comments on your code:
  1. To replace that long if-else code block, and to make adding new terrain types easier, consider writing a TerrainType class that holds movement speed (and cover value and other useful terrain properties). Then, for each unique terrain type, create an instance of that class and store them in a list (your database of terrain types). Then replace that 2D array of ints by a 2D array of TerrainType references, and let each tile refer to the TerrainType instance that represents it's terrain type. Looking up movement cost and other properties is now easy, as is adding new terrain types.

  2. Instead of using 2D arrays, consider using List<..>s for your map data. This will make it easier to handle maps of different sizes later on.

  3. Why create a list of TileEffects structs if you can store the same data in the 2D grid (see point 1)?

  4. Why check screen coordinates if you're working with grid-based movement? Just stick to grid coordinates for your pathfinding/visibility/fire range/destination checks.

  5. Iterating over the grid, as you're doing now, is not going to work for determining all possible destination tiles. You'll need a flood-fill approach. Now, there's two possibilities: either keep track of an open and closed list of nodes, or 'paint' the results into one of your 2D arrays immediately. There's probably not much reason to do both at the same time though.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this