- Home /
Foreach Board Game Movement Bug
To make it short I am really exhausted from trying to fix it, I am trying to create my own movement system. I have some white sprites as tiles for my player to move on with a distance of 3 between them and my player which is the black sprite. In the bottom right there is a controller to move the player, but you can only move in a direction if a tile lies there. But my code doesn't work and only lets me go up at one specific tile, the one that lies at y = 0. Here is my code and a screenshot, I hope you can help me, I will go to sleep now .-.
![{
Vector3 moveX = new Vector3(3.0f,0,0);
Vector3 moveY = new Vector3(0,3.0f,0);
GameObject[] tiles;
public void MoveUp()
{
GameObject.Find("Player1").transform.position += moveY;
CheckTiles();
}
public void MoveDown()
{
GameObject.Find("Player1").transform.position -= moveY;
CheckTiles();
}
public void MoveLeft()
{
GameObject.Find("Player1").transform.position -= moveX;
CheckTiles();
}
public void MoveRight()
{
GameObject.Find("Player1").transform.position += moveX;
CheckTiles();
}
void Start()
{
tiles = GameObject.FindGameObjectsWithTag("tile");
CheckTiles();
}
public void CheckTiles()
{
foreach (GameObject go in tiles)
{
if ((go.transform.position.y <= (GameObject.Find("Player1").transform.position.y + 4)) && (go.transform.position.y >= (GameObject.Find("Player1").transform.position.y + 2)))
{
GameObject.Find("MoveUp").GetComponent<Button>().interactable = true;
}
else
{
GameObject.Find("MoveUp").GetComponent<Button>().interactable = false;
}
}][1]
[1]: /storage/temp/143839-foreachbug.png
Answer by SirPaddow · Aug 01, 2019 at 12:40 AM
The problem is that you assign the value inside the foreach, meaning that you are in fact assigning true or false depending only on the last item of the foreach. What you want in fact is "If it's true for at least one tile, I want it the button to be interactable".
Something like this should work better:
public void CheckTiles()
{
bool enableMoveUp = false;
foreach (GameObject go in tiles)
{
if ((go.transform.position.y <= (GameObject.Find("Player1").transform.position.y + 4)) && (go.transform.position.y >= (GameObject.Find("Player1").transform.position.y + 2)))
{
enableMoveUp = true;
}
}
GameObject.Find("MoveUp").GetComponent<Button>().interactable = enableMoveUp;
}
Unrelated advice:
you should really try to avoid this "Find" function all the time. Can't you have a Player attribute in your class, as you did for the tiles?
it would probably be more efficient and more intuitive to keep a table for your board, and compare indexes rather than world positions.
Sounds logical to me, I will try it out later but it should work, thanks. And yes I will also add a Player attribute and I am not really a Unity pro, I know what a table is and all but how exactly would it work with my board game?
Okay never$$anonymous$$d, I got how you meant it and I think I will actually do it. Thanks for you help :D
I didn't test it, but here is how I would imagine the code, assu$$anonymous$$g the bottom left tile (empty or not) of your grid is positionned at [0, 0] :
private const float tilingDistance = 3;
private Transform player;
private Transform[,] tiles;
private Button moveUpButton;
private Button moveRightButton;
private Button moveLeftButton;
private Button moveDownButton;
private Vector2Int playerCoordinates;
public void Start()
{
BuildTileGrid();
player = GameObject.Find("Player1").transform;
playerCoordinates = new Vector2Int((int)(player.position.x / tilingDistance), (int)(player.position.y / tilingDistance));
moveUpButton = GameObject.Find("$$anonymous$$oveUp").GetComponent<Button>();
moveRightButton = GameObject.Find("$$anonymous$$oveRight").GetComponent<Button>();
moveLeftButton = GameObject.Find("$$anonymous$$oveLeft").GetComponent<Button>();
moveDownButton = GameObject.Find("$$anonymous$$oveDown").GetComponent<Button>();
}
private void BuildTileGrid()
{
GameObject[] tiles = GameObject.FindGameObjectsWithTag("tile");
float maxTileX = 0;
float maxTileY = 0;
foreach (GameObject tile in tiles)
{
if (tile.transform.position.x > maxTileX) maxTileX = tile.transform.position.x;
if (tile.transform.position.y > maxTileY) maxTileY = tile.transform.position.y;
}
this.tiles = new Transform[(int)(maxTileX / tilingDistance) + 1, (int)(maxTileY / tilingDistance) + 1];
foreach (GameObject tile in tiles)
{
this.tiles[(int)(tile.transform.position.x / tilingDistance), (int)(tile.transform.position.y / tilingDistance)] = tile.transform;
}
}
public void $$anonymous$$oveUp() { $$anonymous$$ove(0, 1); }
public void $$anonymous$$oveDown() { $$anonymous$$ove(0, -1); }
public void $$anonymous$$oveLeft() { $$anonymous$$ove(-1, 0); }
public void $$anonymous$$oveRight() { $$anonymous$$ove(1, 0); }
private void $$anonymous$$ove(int diffX, int diffY)
{
playerCoordinates += new Vector2Int(diffX, diffY);
player.position = tiles[playerCoordinates.x, playerCoordinates.y].position;
CheckTiles();
}
public void CheckTiles()
{
moveUpButton.interactable = playerCoordinates.y < tiles.GetLength(1) - 1 && tiles[playerCoordinates.x, playerCoordinates.y + 1] != null;
moveDownButton.interactable = playerCoordinates.y > 0 && tiles[playerCoordinates.x, playerCoordinates.y - 1] != null;
moveRightButton.interactable = playerCoordinates.x < tiles.GetLength(0) - 1 && tiles[playerCoordinates.x + 1, playerCoordinates.y] != null;
moveLeftButton.interactable = playerCoordinates.y > 0 && tiles[playerCoordinates.x - 1, playerCoordinates.y] != null;
}
The main difference is the BuildTileGrid function. In it, ins$$anonymous$$d of only keeping the list of tiles, I insert the tiles in a table. Where there is no tile, the table cell remains empty. Then, in the CheckTiles, all I need to do is to check around the position of the player, if the table cell is empty or not.
I hope it will help :)
Your answer
Follow this Question
Related Questions
Using Mathf.SmoothDamp() in a foreach loop 1 Answer
Not able to make a foreach loop 2 Answers
Access all variables of type within a C# script 2 Answers