move along a grid in Unity - c#

I can set four different movement directions for my player
Vector2.up => (0,1)
Vector2.down => (0,-1)
Vector2.left => (-1,0)
Vector2.right => (1,0)
and I have a two dimensional array that contains Cell objects
public class Cell
{
public Cell(GameObject cellObject, bool isObstacle)
{
CellObject = cellObject;
IsObstacle = isObstacle;
}
public GameObject CellObject { get; set; }
public bool IsObstacle { get; set; }
}
My array is initialized by the size of the map.
private const int MAP_SIZE = 10;
private Cell[,] mapCells = new Cell[MAP_SIZE, MAP_SIZE];
I fill this array by using two loops, this will give me 100 cells.
for (int x = 0; x < MAP_SIZE; x++)
{
for (int y = 0; y < MAP_SIZE; y++)
{
Vector3 newCellPosition = new Vector3(x, 0, y);
GameObject newCellObject = Instantiate(cell, newCellPosition, cell.transform.rotation);
bool isObstacle = false; // TEST
Cell newCell = new Cell(newCellObject, isObstacle);
mapCells[x, y] = newCell;
}
}
When moving the player I want to return the Cell he has to move to. The movementDirection parameter will set the row and the column to search for.
If there is an obstacle cell the player should just move to this obstacle and stop.
public Cell GetTargetCell(Vector2 movementDirection)
{
Cell targetCell = null;
// get the path
// get the closest obstacle
return targetCell;
}
Is there an elegant way to calculate the correct target cell by a 2D direction?

I think the most elegant way of doing it is by using two separate for loops and the ?: operator.
//returns cell
Cell GetTargetCell(Vector2 dir)
{
if(dir.y == 0) //if we're going horizontal
{
//HorizontalMovement
for(int i = 0; i < ((dir.x==1)?mapSize-PLAYER_X:PLAYER_X); i++)
{
if(mapCells[(int)dir.x*i,PLAYER_Y].IsObstacle()) //if we encounter an obstacle
return mapCells[(int)dir.x*i,PLAYER_Y]; //return cell that's an obstacle
}
//if we didn't encounter an obstacle
return mapCells[(dir.x == 1)?mapSize:0,PLAYER_Y]; //return the cell
}
else if(dir.x == 0)
{
//VerticalMovement
//copy paste the previous for loop and edit the parameters, I'm too lazy :P
}
else
{
//NoMovement
Debug.Log("Please enter a valid Direction");
return mapCells[0,0];
}
}
Replace the PLAYER_X and PLAYER_Y values, with the x, and y values of the cell the player is currently in. I didn't check if the code contains any errors, but I think it should work.

Related

How do I convert between enums for passing between different functions

I have enums set up in a script called Grid, as follows:
public enum CellType
{
Empty,
Road,
Structure,
SpecialStructure,
None
}
Then I have a script called placement manager which adds data relevant to that enum like this:
internal void PlaceObjectOnTheMap(Vector3Int position, GameObject structurePrefab, CellType type, int width = 1, int height = 1)
{
StructureModel structure = CreateANewStructureModel(position, structurePrefab, type);
var structureNeedingRoad = structure.GetComponent<INeedingRoad>();
if (structureNeedingRoad != null)
{
structureNeedingRoad.RoadPosition = GetNearestRoad(position, width, height).Value;
Debug.Log("My nearest road position is: " + structureNeedingRoad.RoadPosition);
}
for (int x = 0; x < width; x++)
{
for (int z = 0; z < height; z++)
{
var newPosition = position + new Vector3Int(x, 0, z);
placementGrid[newPosition.x, newPosition.z] = type;
structureDictionary.Add(newPosition, structure);
DestroyNatureAt(newPosition);
}
}
}
finally, I have another script called Grid Helper which is supposed to call placement manager and add itself to the grid which I've set like this:
public enum CellType
{
Empty,
Road,
Structure,
SpecialStructure,
None
}
[SerializeField]
private CellType structureType = CellType.Empty;
public PlacementManager placementManager;
// Start is called before the first frame update
void Start()
{
placementManager.PlaceObjectOnTheMap(new Vector3Int(Mathf.FloorToInt(transform.position.x),
Mathf.FloorToInt(transform.position.y),
Mathf.FloorToInt(transform.position.z)), this.gameObject, structureType);
}
but I somehow keep getting told Argument3 cannot convert from GridHelper.CellType to CellType.
What am I doing wrong?
Remove your enum in the GridHelper or in the Grid class and use GridHelper.CellType or Grid.CellType in the other class depending on what you removed.

Ui button doesn't move correctly

I have a code for a crafting system that checks if the inventory has the ingredients needed to craft an item and adds a button to craft it. The problem is when I want to position my button it goes way off the canvas. I have seen some people saying that it has something to do with rect transform. I've been stuck with it for over an hour. Any help is appreciated.
I have tried
removing the setparent() function,
using anchoredPosition,
using localPosition
My code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Crafting : MonoBehaviour
{
public List<recipe> recipes = new List<recipe>();
public GameObject base_item, parent;
List<GameObject> items = new List<GameObject>();
public int y = 75;
public int x = -45;
public Inv inv;
private void Start()
{
inv = GetComponent<Inv>();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Tab))
{
checkitems();
Debug.Log("y = " + y + " x = " + (x - 40));
}
}
public void checkitems()
{
for (int i = 0; i < recipes.Count; i++)
{
recipe r = recipes[i];
for (int x = 0; x < r.ingredients.Count; x++)
{
if (!inv.hasitem(r.ingredients[x])){
return;
}
}
showitem(r.result);
}
}
public void onClick(int _slot)
{
recipe r = recipes[_slot];
for (int i = 0; i < r.ingredients.Count; i++)
{
inv.removeitem(inv.getitem(r.ingredients[i]));
}
inv.additem(inv.getFirstAvailable(), r.result, r.stack);
}
public void showitem(string name)
{
GameObject obj = Instantiate(base_item);
if (items.Count != 0)
{
if (((items.Count) % 3) != 0)
{
Debug.Log("first thing");
obj.GetComponent<RectTransform>().position = new Vector2(x, y);
obj.transform.SetParent(parent.transform);
obj.SetActive(true);
items.Add(obj);
x = x + 40;
Debug.Log("x + 40");
}
else if (((items.Count + 1) % 3) == 0)
{
Debug.Log("second thing");
x = -45;
Debug.Log("x + 40");
y = y + 40;
Debug.Log(" y + 40");
obj.GetComponent<RectTransform>().position = new Vector2(x, y);
obj.transform.SetParent(parent.transform);
obj.SetActive(true);
items.Add(obj);
}
}else
{
obj.GetComponent<RectTransform>().position = new Vector2(x, y);
obj.transform.SetParent(parent.transform);
obj.SetActive(true);
items.Add(obj);
x = x + 40;
Debug.Log("x + 40");
}
}
}
Blue circle where it spawns. Red circle where I want it to be
Seems you are confusing a bunch of terms for being the issue of your problem. Firstly I want to address the red X over your scroll bar. Whenever this occurs, it means that your RectTransform of this UI object has been dragged from its positive vertices to negative or vice versa, causing it to almost invert. I would correct this but it is not the reason your objects are not childing correctly.
Generally, with UI objects, I would never use LocalPosition, just AnchoredPosition. LocalPosition is a field from Transform which I believe RectTransform inherits from. As RectTransforms have a lot of modifications to their position from pivots, anchors, and anchored positions, the LocalPosition will most likely need to recalculate data to properly move the object, whereas AnchoredPosition has already done these calculations.
I believe the issue with your current code is how you are using SetParent. There is a second parameter of SetParent which governs whether the object keeps the same position based in world space after being childed. As you are not passing in a new bool for this parameter, it is defaulting to true. As you want your objects to be childed to the parent but not keep their world space positions, you would want to pass in false.
In your case, as it looks as if you want to set objects in a grid-like pattern childed to this ScrollRect, I would attach a GridLayoutGroup to the Content of your scroll and child the new objects to this object. You can set the max columns of this grid and spacing to give the same layout you are attempting to achieve in code.
To summarize, I would remove all the hand placement you are doing in code with LocalPosition and AnchorPosition and just attach a GridLayoutGroup. To fix the current positioning of your objects relative to the parent, change all lines of obj.transform.SetParent(parent.transform); to obj.transform.SetParent(parent.transform, false);. If you want to keep changing position locally in code instead of a layout element, use SetParent first, and use AnchoredPosition instead of LocalPosition as the SetParent with false passed in will override the position you set.

Issue detecting textures in Unity C#

I am trying to program a Unity Minesweeper game, and I am almost done, but as part of it I have a boolean variable called "isCovered" that works out if the square has been clicked or not.
This variable is used to see if the player has won yet. I have a 2d array that stores all of my squares, and to see if you win yet, it checks the array for covered, non mine squares, and if it finds them, concludes that you have not won yet.
However, when I test it, the code does not seem to find any squares that are covered, even though I can see that there are some. I concluded this because it outputs the "you win" message every time I click a safe square, even when I am not finished.
My variable is declared using this code:
public class Square : MonoBehaviour {
//creates boolean variable
public bool IsCovered() {
//checks if the square still uses the default texture.
return GetComponent<SpriteRenderer>().sprite.texture.name == "Default";
}
}
and the code to determine if you win, which is within a different class (that is my array/grid) is:
public static bool gameWon()
{
// Checks all squares in the array
foreach (Square elem in elements)
// Tries to find a covered, safe square
if (elem.IsCovered() && !elem.dangerous)
// The player is not finished
return false;
// The player has won
return true;
}
The asset that is used for the default texture is called "Uncleared proposed", rather than "Default" so I don't know if that is an issue.
My entire "Square" code for each individual tile is:
using System.Collections;
using UnityEngine;
public class Square : MonoBehaviour
{
//is this dangerous?
public bool dangerous;
//creates boolean variable
public bool IsCovered()
{
//checks if the square still uses the default texture.
return GetComponent<SpriteRenderer>().sprite.texture.name == "Default";
}
//different textures
public Sprite[] emptyTextures;
public Sprite dangerTexture;
// Use this for initialization
void Start()
{
//game randomly decides if square is dangerous
//10% chance for initial testing
//Chance likely to be changed at some point
dangerous = Random.value < 0.1;
//registers square in grid
int x = (int)transform.position.x;
int y = (int)transform.position.y;
Grid.elements[x, y] = this;
}
void OnMouseUpAsButton()
{
// It's dangerous
if (dangerous)
{
//uncover all mines
//calls for procedure "uncoverDanger()" from class "Grid"
Grid.uncoverDanger();
//ToDo: display loss screen
// game over
print("Game Over");
}
// It's not a mine
else
{
// change to correct texture
int x = (int)transform.position.x;
int y = (int)transform.position.y;
loadTexture(Grid.adjacentCount(x, y));
//Flood Fill nearby area
Grid.FFuncover(x, y, new bool[Grid.w, Grid.h]);
// checks if the player has won
if (Grid.gameWon() == true)
//tells the player they have won
print("you win");
}
}
//Loads a different texture
public void loadTexture(int adjacentCount)
{
//checks to see if square is dangerous
if (dangerous)
//Changes to correct texture using Unity Component
GetComponent<SpriteRenderer>().sprite = dangerTexture;
//if not dangerous
else
//changes to correct texture using Unity Component
GetComponent<SpriteRenderer>().sprite = emptyTextures[adjacentCount];
}
}
and my Grid class code is:
using System.Collections;
using UnityEngine;
public class Grid {
public static int w = 10; // w is the width of the grid (10)
public static int h = 13; // h is the height of the grid (13)
public static Square[,] elements = new Square[w, h];
//Uncover all mines when losing
public static void uncoverDanger() {
foreach (Square elem in elements)
if (elem.dangerous)
elem.loadTexture(0);
}
// Determines if the square is dangerous
public static bool dangerAt(int x, int y)
{
// Makes sure the square is in the range
if (x >= 0 && y >= 0 && x < w && y < h)
// Checks if the square is dangerous
return elements[x, y].dangerous;
return false;
}
// Counts number of dangerous squares adjacent
public static int adjacentCount(int x, int y)
{
int count = 0;
//checks adjacent squares
//potentially adds to counter
if (dangerAt(x, y + 1)) ++count; // top
if (dangerAt(x + 1, y + 1)) ++count; // top-right
if (dangerAt(x + 1, y)) ++count; // right
if (dangerAt(x + 1, y - 1)) ++count; // bottom-right
if (dangerAt(x, y - 1)) ++count; // bottom
if (dangerAt(x - 1, y - 1)) ++count; // bottom-left
if (dangerAt(x - 1, y)) ++count; // left
if (dangerAt(x - 1, y + 1)) ++count; // top-left
//outputs value
return count;
}
// Flood Fill (FF) safe squares
public static void FFuncover(int x, int y, bool[,] visited)
{
// Checks if coordinates in range
if (x >= 0 && y >= 0 && x < w && y < h)
{
// Filled already?
if (visited[x, y])
return;
//uncovers a square
elements[x, y].loadTexture(adjacentCount(x, y));
//checks if adjacencyCount is not 0
if (adjacentCount(x, y) > 0)
//ends algorithm
return;
// set square as visited
visited[x, y] = true;
// repeates with all 4 adjacent squares (not using diagonal)
FFuncover(x - 1, y, visited);
FFuncover(x + 1, y, visited);
FFuncover(x, y - 1, visited);
FFuncover(x, y + 1, visited);
}
}
public static bool gameWon()
{
// Checks all squares in the array
foreach (Square elem in elements)
// Tries to find a covered, safe square
if (elem.IsCovered() && !elem.dangerous)
// The player is not finished
return false;
// The player has won
return true;
}
}
I am a real newbie to unity so it might be something stupid, any help would be greatly appreciated.
Turns out I am an idiot, I just had to change the name of the texture in the variable code to "Uncleared proposed", I don't know why it didn't work earlier.

Tilemap Index out of bounds collision

I created my tile map and my player with movement.
I'm now trying to create the collision and I feel i'm on the right track.
Here is how I've created the map.
List<Texture2D> tileTextures = new List<Texture2D>();
int tileWidth = 60;
int tileHeight = 60;
public int[,] Map = new int[,]
{
{2,2,2,2,2,2,2,2,2,2},
{2,2,2,2,1,2,2,2,2,2},
{2,2,2,2,2,2,2,2,2,2},
{2,2,2,2,2,2,2,2,2,2},
};
public void Draw(SpriteBatch spriteBatch)
{
int tileMapWidth = Map.GetLength(1);
int tileMapHeight = Map.GetLength(0);
for (int x = 0; x < tileMapWidth; x++)
{
for (int y = 0; y < tileMapHeight; y++)
{
int textureIndex = Map[y, x];
Texture2D texture = tileTextures[textureIndex];
spriteBatch.Draw(
texture,
source = new Rectangle(x *myTile.Width,
y * myTile.Height,
tileWidth,
tileHeight),
Color.White);
}
}
}
I am checking the 2d array coords with this condition and checking to see if a specific tile is there, where I can then set my previous location if it is true.
I'm currently testing on 1 tile atm.
public void Update(GameTime gameTime)
{
prevPosition = position;
input(gameTime);
if(tiles.Map[(int)playerPosition.X/60,(int)playerPosition.Y/60] == 1)
{
position = prevPosition;
}
}
However my player position keeps going out of the index bounds of the 2D array and I believe I need to scale it down so that it stops this, I've tried dividing the play coords by the width of the tiles but that hasn't worked.
If anyone can help me with the correct scaling I would be very appreciative.
This will happen if your player's position is like -x,y or x,-y or maybe -x,-y. Yor approach might be better if you'd make a fuction like this one
public bool CollidesWithWall(int x, int y)
{
if(x < 0 || x > *matrix width* - 1) return false;
if(y < 0 || y > *matrix height* -1) return false;
if (Map[x,y] == 1) return true;
return false;
}
and use it insead of the line tiles.Map[(int)playerPosition.X/60,(int)playerPosition.Y/60]
Or, if you need the type of tile returned
public int CollidesWithWall(int x, int y)
{
if(x < 0 || x > *matrix width* - 1) return -1;
if(y < 0 || y > *matrix height* -1) return -1;
return Map[x,y];
}
By doing it this way, you'll know if you stumbled upon a health potion (just set it's ID to like 3) or a wall (with ID of 1 or something, that's tottaly up to you) and if it is 0, it's empty space (or maybe -1). Notice that the "-1" part is totaly up to you. Just write down a list of id's that youll have and which items they present.
Other suggestions
Try if(tiles.Map[(int)(playerPosition.X/60f),(int)(playerPosition.Y/60f)] == 1)

Map possible paths for amount of moves C#

I am re-creating the game of cluedo and I want to map the possible paths that the player can move after dice roll.
I have mapped the grid by drawing pictureboxes and naming them to their mapped location.
Here is my code so far for the possible paths:
int Roll;
private void RollDice()
{
ResetTiles();
JimRandom Random = new JimRandom();
//Roll DIce 1
int dice1 = Random.Next(1, 7);
//Roll DIce 2
int dice2 = Random.Next(1, 7);
Roll = dice1 + dice2;
//Set Dice images
pbDice1.BackgroundImage = Roller[dice1 - 1].Picture;
pbDice2.BackgroundImage = Roller[dice2 - 1].Picture;
btnRoll.Enabled = false;
Test(Roll);
//Show available moves
Control[] lCurrent = PnlBoard.Controls.Find("pnl" + CurrentPlauer, true);
Panel Current = null;
System.Drawing.Point CurrentLoc = new System.Drawing.Point(0, 0);
foreach (Control c in lCurrent)
{
Current = c as Panel;
CurrentLoc = new System.Drawing.Point(c.Location.X, c.Location.Y);
}
//Dynamic map
List<string> possiblities = new List<string>();
int currentRow = CurrentLoc.Y / tileWidth;
int currentCol = CurrentLoc.X / tileHeight;
//Find all possible start blocks
string down = String.Format("Col={0:00}-Row={1:00}", currentCol, currentRow + 1);
string up = String.Format("Col={0:00}-Row={1:00}", currentCol, currentRow - 1);
string left = String.Format("Col={0:00}-Row={1:00}", currentCol - 1, currentRow);
string right = String.Format("Col={0:00}-Row={1:00}", currentCol + 1, currentRow);
List<string> startBlocks = new List<string>();
//See if down is available
Control[] LPossible = PnlBoard.Controls.Find(down, true);
if (LPossible.Length > 0)
{
startBlocks.Add(down);
}
//See if Up is available
LPossible = PnlBoard.Controls.Find(up, true);
if (LPossible.Length > 0)
{
startBlocks.Add(up);
}
//See if left is available
LPossible = PnlBoard.Controls.Find(left, true);
if (LPossible.Length > 0)
{
startBlocks.Add(left);
}
//See if right is available
LPossible = PnlBoard.Controls.Find(right, true);
if (LPossible.Length > 0)
{
startBlocks.Add(right);
}
//possiblilities 1
foreach (string s in startBlocks)
{
Control[] lStarBlock = PnlBoard.Controls.Find(s, true);
PictureBox startBlock = lStarBlock[0] as PictureBox;
int sRow = startBlock.Location.Y / tileWidth;
int sCol = startBlock.Location.X / tileHeight;
//Rows
for (int row = sRow; row < sRow + Roll; row++)
{
//Columns
for (int col = sCol; col < sCol + Roll; col++)
{
possiblities.Add(String.Format("Col={0:00}-Row={1:00}", col, row));
}
}
}
//Show possible moves
foreach (string p in possiblities)
{
LPossible = PnlBoard.Controls.Find(p, true);
if (LPossible.Length > 0)
{
PictureBox active = LPossible[0] as PictureBox;
active.Image = Cluedo.Properties.Resources.TileActive;
System.Threading.Thread.Sleep(1);
Application.DoEvents();
}
//else
//{
// break;
//}
}
}
There's a lot of things I would do different here. This is more of a Code Review post, but there's a solution to your problem at the end, and perhaps the rest can help you to improve the overall state of your code.
Randomness
You're creating a new random generator instance for every method call:
JimRandom Random = new JimRandom();
This often results in the same values being generated if the method is called in rapid succession. Perhaps that's why you're using a cryptographic RNG instead of a PRNG? A PRNG should be sufficient for a game like this, as long as you reuse it.
Using the right types
You're determining the current player location with the following code:
//Show available moves
Control[] lCurrent = PnlBoard.Controls.Find("pnl" + CurrentPlauer, true);
Panel Current = null;
System.Drawing.Point CurrentLoc = new System.Drawing.Point(0, 0);
foreach (Control c in lCurrent)
{
Current = c as Panel;
CurrentLoc = new System.Drawing.Point(c.Location.X, c.Location.Y);
}
It looks like CurrentPlauer is a string. Creating a Player class that stores the name and current location of a player would make things much easier:
Point currentLocation = currentPlayer.Location;
Splitting game logic from UI code
You're checking for passable tiles by doing string lookups against controls:
string down = String.Format("Col={0:00}-Row={1:00}", currentCol, currentRow + 1);
// ...
Control[] LPossible = PnlBoard.Controls.Find(down, true);
if (LPossible.Length > 0)
{
startBlocks.Add(down);
}
Normally a 2D array is used for tile maps like these, possible encapsulated in a Tilemap or Map class. This makes working with tiles more natural, as you can work in tile coordinates directly instead of having to translate between UI and tile coordinates. It also breaks up the code more cleanly into a game-logic and a UI part (the code in your post is impossible to test without UI):
// TileMap class:
public bool IsPassable(int x, int y)
{
if (x < 0 || x >= Width || y < 0 || y >= Height)
return false;
return tiles[x][y] != Tile.Wall; // enum Tile { Wall, Ballroom, DiningRoom, Hall, ... }
}
// When called from your Board code:
if (map.IsPassable(currentLocation.X, currentLocation.Y + 1))
startBlocks.Add(new Point(currentLocation.X, currentLocation.Y + 1));
Reducing repetition
As for checking all direct neighboring tiles, there's no need to repeat the same code 4 times:
// Let's make a utility function:
public static IEnumerable<Point> GetNeighboringPositions(Point position)
{
yield return new Point(position.X - 1, position.Y);
yield return new Point(position.X, position.Y - 1);
yield return new Point(position.X + 1, position.Y);
yield return new Point(position.X, position.Y + 1);
}
// In the Board code:
foreach (Point neighboringPosition in GetNeighboringPositions(currentPosition))
{
if (map.IsPassable(neighboringPosition.X, neighboringPosition.Y))
startBlocks.Add(neighboringPosition);
}
Determining valid moves
Finally, we get to the code that determines which tiles the current player can move to:
//possiblilities 1
foreach (string s in startBlocks)
{
Control[] lStarBlock = PnlBoard.Controls.Find(s, true);
PictureBox startBlock = lStarBlock[0] as PictureBox;
int sRow = startBlock.Location.Y / tileWidth;
int sCol = startBlock.Location.X / tileHeight;
//Rows
for (int row = sRow; row < sRow + Roll; row++)
{
//Columns
for (int col = sCol; col < sCol + Roll; col++)
{
possiblities.Add(String.Format("Col={0:00}-Row={1:00}", col, row));
}
}
}
What this does is checking a rectangular area, using a starting position as its top-left corner. It's doing so for up to 4 neighboring positions, so the rectangles will partially overlap each other. That's just not going to work. If the map didn't have any obstacles, something like this, combined with a Manhattan distance check, could work (if you don't forget to look to the left and upwards too). Or better, some fancy looping that checks a diamond-shaped area.
However, you've got walls to deal with, so you'll need a different approach. The player's current position is at distance 0. Its direct neighbors are at distance 1. Their neighbors are at distance 2 - except those tiles that are at a lower distance (the tiles that have already been covered). Any neighbours of tiles at distance 2 are either at distance 3, or have already been covered. Of course, wall tiles must be skipped.
So you need to keep track of what tiles have already been covered and what neighboring tiles you still need to check, until you run out of movement points. Let's wrap that up into a reusable method:
public List<Point> GetReachableTiles(Point currentPosition, int maxDistance)
{
List<Point> coveredTiles = new List<Point> { currentPosition };
List<Point> boundaryTiles = new List<Point> { currentPosition };
for (int distance = 0; distance < maxDistance; distance++)
{
List<Point> nextBoundaryTiles = new List<Point>();
foreach (Point position in boundaryTiles)
{
foreach (Point pos in GetNeighboringPositions(position))
{
// You may also want to check against other player positions, if players can block each other:
if (!coveredTiles.Contains(pos) && !boundaryTiles.Contains(pos) && map.IsPassable(pos.X, pos.Y))
{
// We found a passable tile:
coveredTiles.Add(pos);
// And we want to check its neighbors in the next 'distance' iteration, too:
nextBoundaryTiles.Add(pos);
}
}
}
// The next 'distance' iteration should check the neighbors of the current boundary tiles:
boundaryTiles = nextBoundaryTiles;
}
return coveredTiles;
}

Categories