- Home /
How to prevent 2d objects go over each other?
Hello. I don't know if the title is clear so I'm uploading an image to make it easy to understand what I'm trying to do I'm making a checkers like game in order to learn with practice.
I have my 2D objects, moving with raycast. Basically teleporting to the point where mouse clicks. Yet I don't want them to go over each other but I want them to be have to go around.
void posOfClick()
{
Vector3 mousePos = Input.mousePosition;
mousePos.z = 10;
Vector3 screenPos = Camera.main.ScreenToWorldPoint(mousePos);
RaycastHit2D hit = Physics2D.Raycast(screenPos, Vector2.zero);
if (hit)
{
orgPos = (gameObject.transform.position);
tilePos = (hit.transform.position);
transform.Translate(tilePos.x-orgPos.x, tilePos.y-orgPos.y, 0);
}
}
This is how my teleporting system works. Thanks in advance.
Hmm, I don't really know how to do that, but try searching up a tutorial by Blackthornprod, Brackeys, or whoever for help ;\
Answer by unity_ek98vnTRplGj8Q · Feb 03, 2020 at 07:27 PM
So you don't want the piece to move at all if there is another piece in the way? There are a couple ways you can do this
1 - If your game truly is a grid, you can keep track of which tiles are occupied by pieces. Whenever you want to move a piece, you can check which tiles the piece has to travel through and if those tiles are occupied or not. If you don't already have a system that keeps track of which pieces are on which tiles and vice-versa this will be a harder solution to write but probably something that will benefit you down the road.
2 - If you don't want to do this, you could simply put the pieces on their own layer, then do a raycast from the current position to the new position before you move the piece and see if there is anything in the way. Easier solution to write, but probably not the correct way to tackle the problem if you are making a grid-based game like checkers
Thank you @unity_ek98vnTRplGj8Q I am new to these things, so could you give me an example about how to do? It doesn't matter if you write the first way or the second, I appreciate your help. I generate the game area with this code:
public GameObject tile; float width = 6; float height = 4; // Start is called before the first frame update void Start() { for (float x = -6.667f; x < width; x += 2.22f) { for (float y = -5f; y < height; y += 1.663f) { Instantiate(tile, new Vector3(x, y, 0), Quaternion.identity); } } }
Ok here is some code. This probably isn't complete (and I for sure have not tested it) but it should give a rough idea of the direction to go in. This is for the first method I mentioned.
First, a class to attach to your tile prefab
public class GameTile : $$anonymous$$onobehavior {
//This can be a useful place to put logic later deoending on your game
//There are definetely ways to do this without making a new class, but this helps keep things organized
//Attach this to your tile prefab
public int xCoord, yCoord;
public bool isOccupied = false;
public GameObject currentOccupiedPiece;
}
Next, when you create your game board
public GameObject tile;
float width = 6;
float height = 4; // Start is called before the first frame update
public static GameTile[, ] board; //2D array of tiles representing our board
protected struct TileCoords {
public int w, h;
}
void Start () {
board = new GameObject[width, height];
float x = -6.667f;
float y = -5f;
for (float i = 0; i < width;) {
for (float j = 0; j < height;) {
GameTile newTile = Instantiate (tile, new Vector3 (x, y, 0), Quaternion.identity).GetComponent<GameTile> ();
x += 2.22f;
y += 1.663f;
newTile.xCoord = i;
newTile.yCoord = j;
board[i, j] = newTile;
}
}
}
Hmm its not letting me paste the rest of the code hold on
//Now in your raycasting script
//You will need to have some map of game pieces to the tile that they are on that you keep updated
//public Dictionary<GameObject, GameTile>() pieceToTile$$anonymous$$ap = new Dictionary<GameTile, GameTile>();
//however it looks like for now this script is attached to a specific piece, so this piece should directly keep track of its current tile
public GameTile currentTile;
void posOfClick () {
Vector3 mousePos = Input.mousePosition;
mousePos.z = 10;
Vector3 screenPos = Camera.main.ScreenToWorldPoint (mousePos);
RaycastHit2D hit = Physics2D.Raycast (screenPos, Vector2.zero);
if (hit) {
orgPos = (gameObject.transform.position);
tilePos = (hit.transform.position);
GameTile targetTile = hit.gameObject.GetComponent<GameTile> ();
//Here you can change the logic to do exactly what you want
//Right now its unclear what the behavior should be for diagonal moves
//For now I will assume only vertical or horizontal moves are allowed
bool can$$anonymous$$ove = true;
if (currentTile.xCoord == targetTile.xCoord) {
//Vertical move
//Start at the lower tile
int $$anonymous$$YCoord = $$anonymous$$athf.$$anonymous$$in (currentTile.yCoord, targetTile.yCoord);
int maxYCoord = $$anonymous$$athf.$$anonymous$$ax (currentTile.yCoord, targetTile.yCoord);
for (int i = $$anonymous$$YCoord + 1; i < maxYCoord; i++) {
//Check each tile between to see if one is occupied
//I'm not sure what your script is called that creates the board, I just called it Game$$anonymous$$anager
if (Game$$anonymous$$anager.board[currentTile.xCoord, i].isOccupied) {
can$$anonymous$$ove = false;
break;
}
}
}
//Now the same logic for a horizontal move
else if (currentTile.yCoord == targetTile.yCoord) {
int $$anonymous$$XCoord = $$anonymous$$athf.$$anonymous$$in (currentTile.xCoord, targetTile.xCoord);
int maxXCoord = $$anonymous$$athf.$$anonymous$$ax (currentTile.xCoord, targetTile.xCoord);
for (int i = $$anonymous$$XCoord + 1; i < maxXCoord; i++) {
//Check each tile between to see if one is occupied
//I'm not sure what your script is called that creates the board, I just called it Game$$anonymous$$anager
if (Game$$anonymous$$anager.board[i, currentTile.yCoord].isOccupied) {
can$$anonymous$$ove = false;
break;
}
}
}
//If we didn't detect a blocking piece then lets move our piece
if(can$$anonymous$$ove){
transform.Translate(tilePos.x-orgPos.x, tilePos.y-orgPos.y, 0);
//Remember to update the game state
currentTile.isOccupied = false;
currentTile.currentOccupiedPiece = null;
targetTile.isOccupied = true;
targetTile.currentOccupiedPiece = this.gameObject;
currentTile = targetTile;
}
}