Sliding Tile Puzzle

I have to create a sliding tile puzzle, but it seems I am stuck. I don’t know how to make it so the tiles in the grid can swap places in order for me to arrange them correctly.

Integer AmountOfButtons = 16;
int cols = 4; 
int rows = 4;
//zeile 1-3 verändern je nach bedarf
IntList inventory;
Integer counter =0;
SimpleInventory[] slot;
SimpleButton [][] buttons;

void setup() {
  size (640, 480);
  //randomizer
  inventory = new IntList(); 
  slot = new SimpleInventory[AmountOfButtons]; //
  for (int i=0; i<slot.length; i++) {
    slot[i] = new SimpleInventory(0);
    inventory.append(i);
    inventory.shuffle();
  }

  buttons = new SimpleButton[cols][rows];
  for (int i = 0; i < cols; i++) {
    for (int j = 0; j < rows; j++)
      buttons[i][j] = new SimpleButton(i*100, j*100, 100, 100, inventory.get(counter++), #AAEE00);
  }
}


void draw() {

  for (int i = 0; i < cols; i++) {
    for (int j = 0; j< rows; j++) {
      buttons[i][j].render();
    }
  }
}

class SimpleButton {

  int posX;
  int posY;
  color baseColor;
  color hoverColor;
  Integer label;
  int buttonWidth = 50;
  int buttonHight = 50;
  PFont f = createFont("Arial", 25, true);

  SimpleButton(int posX, int posY, int buttonWidth, int buttonHight, Integer label, color c) {
    this.posX = posX;
    this.posY = posY;
    this.buttonWidth = buttonWidth;
    this.buttonHight = buttonHight;
    this.label = label;
    baseColor = c;
    hoverColor = color(red(c) + 10, green(c) + 10, blue(c) + 10);
  }

  void render() {
    if (mouseOver()) {
      fill(hoverColor);
    } else { 
      fill(baseColor);
    }
    drawShape();
    textFont(f, 20);
    textAlign(CENTER, CENTER);
    fill(0);
    text(label, posX + buttonWidth/2, posY + buttonHight/2);
  }
  void drawShape() {
    rect(posX, posY, buttonWidth, buttonHight);
  }

  boolean mouseOver() {
    if ((mouseX > posX) && (mouseX < posX + buttonWidth) &&
      (mouseY > posY) && (mouseY < posY + buttonHight)) {
      return true;
    }
    return false;
  }
}

class SimpleInventory {

  int posX;

  SimpleInventory(int posX) {
    this.posX = posX;
  }
}

can somone help me i am very new to programming

I think you need to have one space in the grid that doesn’t have a tile, then you can begin to work out how to select a tile to slide into the empty space.

I have been working on something similar recently, and once I got to the point where moving the tiles around worked correctly, and then found a way to randomize them, I realized that it’s possible to shuffle the tiles into an unsolvable condition, so you may want to research how to check for that too.

Hi Shimuni,

First you would need to move your tile. See the following code:

Integer AmountOfButtons = 16;
int cols = 4; 
int rows = 4;
//zeile 1-3 verändern je nach bedarf
IntList inventory;
Integer counter =0;
SimpleInventory[] slot;
SimpleButton [][] buttons;
int deltaX, deltaY;

void setup() {
  size (640, 480);
  //randomizer
  inventory = new IntList(); 
  slot = new SimpleInventory[AmountOfButtons]; //
  for (int i=0; i<slot.length; i++) {
    slot[i] = new SimpleInventory(0);
    inventory.append(i);
    inventory.shuffle();
  }

  buttons = new SimpleButton[cols][rows];
  for (int i = 0; i < cols; i++) {
    for (int j = 0; j < rows; j++)
      buttons[i][j] = new SimpleButton(i*100, j*100, 100, 100, inventory.get(counter++), #AAEE00);
  }
}


void draw() {
  background(20); // ***

  for (int i = 0; i < cols; i++) {
    for (int j = 0; j< rows; j++) {


      // ***
      if (buttons[i][j].isLocked()) {
        buttons[i][j].moveTo((int)mouseX - deltaX, (int)mouseY - deltaY);
      }
      // ***


      buttons[i][j].render();
    }
  }
}



// ***
void mousePressed() {
  for (int i = 0; i < cols; i++) {
    for (int j = 0; j< rows; j++) {
      if (buttons[i][j].mouseOver()) {
        deltaX = mouseX - buttons[i][j].getX();
        deltaY = mouseY - buttons[i][j].getY();
        buttons[i][j].lock();
      }
    }
  }
}

void mouseReleased() {
  for (int i = 0; i < cols; i++) {
    for (int j = 0; j< rows; j++) {
      buttons[i][j].unlock();
    }
  }
}
// ***



class SimpleButton {
  int posX;
  int posY;
  color baseColor;
  color hoverColor;
  Integer label;
  int buttonWidth = 50;
  int buttonHight = 50;
  PFont f = createFont("Arial", 25, true);

  boolean locked; // ***

  SimpleButton(int posX, int posY, int buttonWidth, int buttonHight, Integer label, color c) {
    this.posX = posX;
    this.posY = posY;
    this.buttonWidth = buttonWidth;
    this.buttonHight = buttonHight;
    this.label = label;
    baseColor = c;
    hoverColor = color(red(c) + 10, green(c) + 10, blue(c) + 10);

    locked = false; // ***
  }



  // ***
  void lock() {
    locked = true;
  }

  void unlock() {
    locked = false;
  }

  boolean isLocked() {
    return locked;
  }

  void moveTo(int x, int y) {
    posX = x;
    posY = y;
  }

  int getX() {
    return posX;
  }

  int getY() {
    return posY;
  }
  // ***





  void render() {
    if (mouseOver()) {
      fill(hoverColor);
    } else { 
      fill(baseColor);
    }
    drawShape();
    textFont(f, 20);
    textAlign(CENTER, CENTER);
    fill(0);
    text(label, posX + buttonWidth/2, posY + buttonHight/2);
  }
  
  
  void drawShape() {
    rect(posX, posY, buttonWidth, buttonHight);
  }

  boolean mouseOver() {
    if ((mouseX > posX) && (mouseX < posX + buttonWidth) &&
      (mouseY > posY) && (mouseY < posY + buttonHight)) {
      return true;
    }
    return false;
  }
}

class SimpleInventory {

  int posX;

  SimpleInventory(int posX) {
    this.posX = posX;
  }
}

All the part in between or followed by // *** are parts that were added.

I added a locked variable to your simpleButton class in order to remember if the button was clicked or not. All the added functions are pretty self explainatory.

The idea is that when you click on a button, you lock it so you know you have clicked on this tile and that you need to move it. When you release the mouse, then you just unlock the tile because you don’t want to move it anymore. That’s what the mouseClicked() and mouseReleased() functions are doing.

The deltaX and deltaY variables are there to keep track of the distance between the corner of your tile and the mouse position when clicked so the tile wont magically teleport to the mouse position but stay the same distance away.

Then in draw(), it is just a matter of moving the tile to the mouse position if the tile is locked.

Don’t forget to clean of your screen at the beginning of the draw loop with the background() function if you don’t want trippy effects :slight_smile:

For the swapping, I will leave it to you as an exercise. The idea is as followed:

  1. When you release the mouse: check in which area of the grids the mouse is. (In mouseReleased() then)
  2. Then you need to check either it is one of the following:
    • The area is the same as the one where my tile was
    • The area is just next (top, left, right or down) so it is a correct move (I assume)
    • The area is too far away: it is not a correct move
  3. Depending on the scenario, you can either
    • Draw the tile where it was previously: so no change
    • Draw the tile to the new place and draw the other tile t the previous place of the last one

how do i tell the programm what order the numbers in the array need to have inorder for it to be solved?

would you mind showing me how you managed to get the tiles moving?

I am still learning processing as well, so I can’t guarantee this method is the best, or even a good one.

I don’t know if this will help any but maybe it would be of some use.

Basically, if the mouse is pressed over a grid cell:

  • setClickedCell() stores the coordinates of the cell in the global clickedCell
  • setActiveTile() stores the tileId in the global activeTile if there is a tile in that cell
  • makeListOfTilesToMove() checks if a tile’s id is stored in activeTile
  • then it checks whether the tile is in the same row or column as the empty cell
  • if it is, then see how many tiles are between the clicked cell and the empty cell and start moving them.
Cell[][] cells;
Tile[] tiles;
int[] tileIndex;
PVector m;
PVector nCR, cellDims, tileDims, margin;
PVector clickedCell, emptyCellId;
int nTiles, activeTile;

void setup() {
  size(300, 300);
  margin = new PVector(50, 50);
  nCR = new PVector(3, 3);
  cellDims = new PVector(75, 75);
  cells = new Cell[int(nCR.x)][int(nCR.y)];
  clickedCell = new PVector(-1, -1);
  emptyCellId = new PVector(-1, -1);
  initCells();

  tileDims = new PVector(cellDims.x, cellDims.y);
  nTiles = int(nCR.x) * int(nCR.y) - 1;
  tiles = new Tile[nTiles];
  tileIndex = new int[nTiles];
  activeTile = -1;
  initTiles();
  updateEmptyCellId();
}
void draw() {
  background(127);
  m = new PVector(mouseX, mouseY);
  displayCells();
  displayTiles();
}
void mousePressed() {
  updateEmptyCellId();
  setClickedCell();
  setActiveTile();
  makeListOfTilesToMove();
  clearClickedCell();
  clearActiveTile();
}
void makeListOfTilesToMove() {
  // see if a tile has been set as active
  if (activeTile >= 0) {
    // see if the tile is in the same row as the empty cell
    if (emptyCellId.x == tiles[activeTile].coords.x) {
      // count how far from the empty cell the active tile is
      int tileCount = int(emptyCellId.y - tiles[activeTile].coords.y);
      for (int i = 0; i < abs(tileCount); i++) {
        // move each tile to the empty cell, beginning with the one already closest
        moveTile(getTileByCoords(new PVector(emptyCellId.x, emptyCellId.y - (1 * signum(tileCount)))));
      }
    }
    // see if the tile is in the same column as the empty cell
    if (emptyCellId.y == tiles[activeTile].coords.y) {
      // count how far from the empty cell the active tile is
      int tileCount = int(emptyCellId.x - tiles[activeTile].coords.x);
      for (int i = 0; i < abs(tileCount); i++) {
        // move each tile to the empty cell, beginning with the one already closest
        moveTile(getTileByCoords(new PVector(emptyCellId.x - (1 * signum(tileCount)), emptyCellId.y)));
      }
    }
    // refresh the value of emptyCell
    updateEmptyCellId();
  }
}
int signum(int i) {
  if (i > 0) return 1;
  if (i < 0) return -1;
  return 0;
}
void moveTile(int tileId) {
  changeTileLoc(tileId);
  removeTileFromCell(getCellByTileId(tileId));
  addTileToCell(tileId);
  updateEmptyCellId();
}
void removeTileFromCell(PVector cellId) {
  cells[int(cellId.x)][int(cellId.y)].hasTile = false;
  cells[int(cellId.x)][int(cellId.y)].tileId = -1;
}
void changeTileLoc(int tileToChange) {
  // change the pixel location of the tile
  tiles[tileToChange].updateLoc(cells[int(emptyCellId.x)][int(emptyCellId.y)].loc);
  // change the coordinate value of the tile
  tiles[tileToChange].updateCoords(emptyCellId);
}
void addTileToCell(int tileToAdd) {
  cells[int(emptyCellId.x)][int(emptyCellId.y)].hasTile = true;
  cells[int(emptyCellId.x)][int(emptyCellId.y)].tileId = tileToAdd;
}
PVector getCellByTileId(int tempId) {
  PVector index = new PVector(-1, -1);
  for (int c = 0; c < cells.length; c++) {
    for (int r = 0; r < cells[0].length; r++) {
      if (cells[c][r].tileId == tempId) {
        index.x = c;
        index.y = r;
      }
    }
  }
  return index;
}
int getTileByCoords(PVector tempCoords) {
  int index = -1;
  for (int n = 0; n < tiles.length; n++) {
    if (tiles[n].coords.x == tempCoords.x && tiles[n].coords.y == tempCoords.y) {
      index = n;
    }
  }
  return index;
}
void updateEmptyCellId() {
  for (int c = 0; c < cells.length; c++) {
    for (int r = 0; r < cells[0].length; r++) {
      if (cells[c][r].hasTile == false) {
        emptyCellId = cells[c][r].id;
      }
    }
  }
}
void clearClickedCell() {
  clickedCell = new PVector(-1, -1);
  for (int c = 0; c < cells.length; c++) {
    for (int r = 0; r < cells[0].length; r++) {
      cells[c][r].isActive = false;
    }
  }
}
void clearActiveTile() {
  activeTile = -1;
  for (int n = 0; n < tiles.length; n++) {
    tiles[n].isActive = false;
    tiles[n].checked = false;
  }
}
void setClickedCell() {
  for (int c = 0; c < cells.length; c++) {
    for (int r = 0; r < cells[0].length; r++) {
      cells[c][r].checkActive();
      if (cells[c][r].isActive == true) {
        clickedCell = cells[c][r].id;
      }
    }
  }
}
void setActiveTile() {
  for (int n = 0; n < tiles.length; n++) {
    tiles[n].checkActive();
    if (tiles[n].isActive == true) {
      activeTile = tiles[n].id;
    }
  }
}
void displayCells() {
  for (int c = 0; c < cells.length; c++) {
    for (int r = 0; r < cells[0].length; r++) {
      cells[c][r].display();
    }
  }
}
void displayTiles() {
  for (int n = 0; n < tiles.length; n++) {
    tiles[n].display();
  }
}
void initCells() {
  for (int c = 0; c < cells.length; c++) {
    for (int r = 0; r < cells[0].length; r++) {
      cells[c][r] = new Cell(new PVector(margin.x + c * cellDims.x, margin.y + r * cellDims.y), new PVector(c, r), cellDims);
    }
  }
}
void initTiles() {
  for (int i = 0; i < tileIndex.length; i++) {
    tileIndex[i] = i;
  }
  // randomize
  int[] tempN = new int[nTiles];
  tempN = tileIndex;
  int index, temp;
  for (int i = tempN.length - 1; i >0; i--) {
    index = int(random(i +1));
    temp = tempN[index];
    tempN[index] = tempN[i];
    tempN[i] = temp;
  }
  int[] list = new int[nTiles];
  list = subset(tempN, 0, nTiles);

  int n = 0;
  for (int r = 0; r < cells[0].length; r++) {
    for (int c = 0; c < cells.length && n < nTiles; c++) {
      tiles[list[n]] = new Tile(cells[c][r].loc, tileDims, list[n], new PVector(c, r));
      cells[c][r].hasTile = true;
      cells[c][r].tileId = list[n];
      n++;
    }
  }
}
class Tile {
  PVector loc, dims, coords;
  int id;
  boolean isActive, checked;
  Tile(PVector loc, PVector dims, int id, PVector coords) {
    this.loc = loc;
    this.dims = dims;
    this.id = id;
    this.coords = coords;
    isActive = false;
    checked = false;
  }
  void display() {
    rectMode(CENTER);
    fill(225, 225, 225, 60);
    if (isActive == true) {
      fill(225, 60, 60, 127);
    }
    if (checked) {
      fill(0, 0, 192);
    }
    strokeWeight(3);
    stroke(255);
    rect(loc.x, loc.y, dims.x, dims.y);
    String sX = str(int(coords.x));
    String sY = str(int(coords.y));
    String s = str(id);
    //String s = id + "\n" + "(" + sX + ", " + sY + ")";
    textAlign(CENTER, CENTER);
    textSize(24);
    fill(255);
    text(s, loc.x, loc.y);
  }
  void updateLoc(PVector newLoc) {
    loc = newLoc;
  }
  void updateCoords(PVector newCoords) {
    coords = newCoords;
  }
  void checkActive() {
    if (m.x>loc.x-dims.x/2 && m.y>loc.y-dims.y/2 && 
      m.x<loc.x+dims.x/2+1 && m.y<loc.y+dims.y/2+1) {
      isActive = true;
    } else {
      isActive = false;
    }
  }
}
class Cell {
  PVector loc, dims, id;
  int tileId;
  boolean isActive, hasTile;
  Cell(PVector loc, PVector id, PVector dims) {
    this.loc = loc;
    this.id = id;
    this.dims = dims;
    isActive = false;
    hasTile = false;
    tileId = -1;
  }
  void display() {
    rectMode(CENTER);
    if (hasTile) {
      fill(60);
    } else {
      fill(127, 0, 0);
    }
    strokeWeight(3);
    stroke(255);
    rect(loc.x, loc.y, dims.x, dims.y);
  }
  void checkActive() {
    if (m.x>loc.x-dims.x/2 && m.y>loc.y-dims.y/2 && 
      m.x<loc.x+dims.x/2+1 && m.y<loc.y+dims.y/2+1) {
      isActive = true;
    } else {
      isActive = false;
    }
  }
}

I did not understand your question…

Another way with Box2D. just tiles moving, nothing to do with the puzzle itself. and it may not apply if you don’t actually want the tiles sliding.

import shiffman.box2d.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.joints.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.dynamics.*;

Box2DProcessing box2d;
ArrayList boundaries;
Box[][] tiles;
Box box;
Spring spring;
Surface surface;
void setup() {
  size(321, 321);
  box2d = new Box2DProcessing(this);
  box2d.createWorld();
  box2d.setGravity(0, 0);
  addBoundaries();
  surface = new Surface(width/2, height/2);
  tiles = new Box[3][3];
  initTiles();
  bindTilesToSurface();
  spring = new Spring();
}
void draw() {
  background(255);
  box2d.step();
  surface.display();
  spring.update(mouseX, mouseY);
  displayWalls();
  displayTiles();
  //spring.display();
}
void mousePressed() {
  int n = 0;
  for (int c = 0; c < tiles.length; c++) {
    for (int r = 0; r < tiles[0].length && n < 8; r++) {
      if (tiles[c][r].contains(mouseX, mouseY)) {
        spring.bind(mouseX, mouseY, tiles[c][r]);
      }
      n++;
    }
  }
}
void mouseReleased() {
  spring.destroy();
}
void displayWalls() {
  for (int i = 0; i < boundaries.size(); i++) {
    Boundary wall = (Boundary) boundaries.get(i);
    wall.display();
  }
}
void displayTiles() {
  int n = 0;
  for (int c = 0; c < tiles.length; c++) {
    for (int r = 0; r < tiles[0].length && n < 8; r++) {
      tiles[c][r].display();
      n++;
    }
  }
}
void addBoundaries() {
  boundaries = new ArrayList();
  boundaries.add(new Boundary(width/2, height-5, width, 10, 0));
  boundaries.add(new Boundary(width/2, 5, width, 10, 0));
  boundaries.add(new Boundary(width-5, height/2, 10, height, 0));
  boundaries.add(new Boundary(5, height/2, 10, height, 0));
}
void initTiles() {
  int n = 0;
  for (int c = 0; c < tiles.length; c++) {
    for (int r = 0; r < tiles[0].length && n < 8; r++) {
      tiles[c][r] = new Box(50 + 100 * c, 50 + 100 * r);
      n++;
    }
  }
}
void bindTilesToSurface() {
  int n = 0;
  for (int c = 0; c < tiles.length; c++) {
    for (int r = 0; r < tiles[0].length && n < 8; r++) {
      tiles[c][r].bind(surface);
      n++;
    }
  }
}
class Boundary {
  float x, y, w, h;
  Body b;
  Boundary(float x, float y, float w, float h, float a) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    PolygonShape ps = new PolygonShape();
    float box2dW = box2d.scalarPixelsToWorld(w/2);
    float box2dH = box2d.scalarPixelsToWorld(h/2);
    ps.setAsBox(box2dW, box2dH);
    BodyDef bd = new BodyDef();
    bd.type = BodyType.STATIC;
    bd.angle = a;
    bd.position.set(box2d.coordPixelsToWorld(x, y));
    b = box2d.createBody(bd);
    b.createFixture(ps, 1);
  }
  void display() {
    fill(75);
    noStroke();
    rectMode(CENTER);
    float a = b.getAngle();
    pushMatrix();
    translate(x, y);
    rotate(-a);
    rect(0, 0, w, h);
    popMatrix();
  }
}
class Box {
  FrictionJoint frictionJoint;
  Body body;
  float x, y;
  float w, h;
  Box(float x, float y) {
    this.x = x;
    this.y = y;
    w = 100;
    h = 100;
    makeBody(new Vec2(x, y));
  }
  boolean contains(float x, float y) {
    Vec2 worldPoint = box2d.coordPixelsToWorld(x, y);
    Fixture f = body.getFixtureList();
    boolean inside = f.testPoint(worldPoint);
    return inside;
  }
  void makeBody(Vec2 center) {
    BodyDef bd = new BodyDef();
    bd.type = BodyType.DYNAMIC;
    bd.position.set(box2d.coordPixelsToWorld(center));
    bd.fixedRotation = true;
    body = box2d.createBody(bd);

    PolygonShape ps = new PolygonShape();
    Vec2[] vertices = new Vec2[8];
    vertices[0] = box2d.vectorPixelsToWorld(new Vec2(-50, -48));
    vertices[1] = box2d.vectorPixelsToWorld(new Vec2(-50, 48));
    vertices[2] = box2d.vectorPixelsToWorld(new Vec2(-48, 50));
    vertices[3] = box2d.vectorPixelsToWorld(new Vec2(48, 50));
    vertices[4] = box2d.vectorPixelsToWorld(new Vec2(50, 48));
    vertices[5] = box2d.vectorPixelsToWorld(new Vec2(50, -45));
    vertices[6] = box2d.vectorPixelsToWorld(new Vec2(48, -50));
    vertices[7] = box2d.vectorPixelsToWorld(new Vec2(-48, -50));
    ps.set(vertices, vertices.length);

    FixtureDef fd = new FixtureDef();
    fd.shape = ps;
    fd.density = 1;
    fd.friction = 0;
    fd.restitution = 0.0;
    body.createFixture(fd);
  }
  void display() {
    Vec2 pos = box2d.getBodyPixelCoord(body);
    float a = body.getAngle();
    pushMatrix();
    translate(pos.x, pos.y);
    rotate(-a);
    fill(255, 255, 240);
    stroke(125, 125, 110);
    strokeWeight(2);
    rectMode(CENTER);
    rect(0, 0, w, h, 5);
    popMatrix();
  }
  void killBody() {
    box2d.destroyBody(body);
  }
  void bind(Surface surface) {
    FrictionJointDef fj = new FrictionJointDef();
    fj.bodyA = body;
    fj.bodyB = surface.body;
    fj.maxForce = 1000;
    fj.maxTorque = 0;
    fj.collideConnected = false;
    frictionJoint = (FrictionJoint) box2d.world.createJoint(fj);
  }
}
class Spring {
  MouseJoint mouseJoint;
  Spring() {
    mouseJoint = null;
  }
  void update(float x, float y) {
    if (mouseJoint != null) {
      Vec2 mouseWorld = box2d.coordPixelsToWorld(x, y);
      mouseJoint.setTarget(mouseWorld);
    }
  }
  void display() {
    if (mouseJoint != null) {
      Vec2 v1 = new Vec2(0, 0);
      mouseJoint.getAnchorA(v1);
      Vec2 v2 = new Vec2(0, 0);
      mouseJoint.getAnchorB(v2);

      v1 = box2d.coordWorldToPixels(v1);
      v2 = box2d.coordWorldToPixels(v2);
      stroke(0);
      strokeWeight(1);
      line(v1.x, v1.y, v2.x, v2.y);
    }
  }
  void bind(float x, float y, Box box) {
    MouseJointDef md = new MouseJointDef();
    md.bodyA = box2d.getGroundBody();
    md.bodyB = box.body;
    Vec2 mp = box2d.coordPixelsToWorld(x, y);
    md.target.set(mp);
    md.maxForce = 1000.0 * box.body.m_mass;
    md.frequencyHz = 5.0;
    md.dampingRatio = 0.9;
    mouseJoint = (MouseJoint) box2d.world.createJoint(md);
  }
  void destroy() {
    if (mouseJoint != null) {
      box2d.world.destroyJoint(mouseJoint);
      mouseJoint = null;
    }
  }
}
class Surface {
  FrictionJoint frictionJoint;
  Body body;
  float x, y, w, h;
  Surface(float x, float y) {
    this.x = x;
    this.y = y;
    w = width;
    h = height;
    makeBody(new Vec2(x, y), w, h);
  }
  void display() {
    Vec2 pos = box2d.getBodyPixelCoord(body);
    rectMode(CENTER);
    pushMatrix();
    translate(pos.x, pos.y);
    fill(0, 127, 0);
    noStroke();
    rect(0, 0, w, h);
    popMatrix();
  }
  void makeBody(Vec2 center, float w, float h) {
    BodyDef bd = new BodyDef();
    bd.type = BodyType.STATIC;
    bd.position.set(box2d.coordPixelsToWorld(center));
    bd.fixedRotation = true;
    body = box2d.createBody(bd);

    PolygonShape ps = new PolygonShape();
    float box2dW = box2d.scalarPixelsToWorld(w/2);
    float box2dH = box2d.scalarPixelsToWorld(h/2);
    ps.setAsBox(box2dW, box2dH);

    FixtureDef fd = new FixtureDef();
    fd.shape = ps;
    body.createFixture(fd);
  }
}