(Java) Rotate around Center while Preserving Orientation and Directional References

1.3k views Asked by At

I am designing a top-down game that has a "camera" used to pan around the playing area. The camera can be rotated and translated; translation is typical for most top-down games.

To implement this I am using a class I created called "Center." Groups of objects are drawn around these centers (for instance, "Car" might extend Center, and Car has Parts "wheel" and "hood." In whatever position and orientation Car is drawn, so are wheel and hood).


Things I want:

(1) Centers always rotate around the center of the screen. EG, a center five pixels to the left of the center of the screen that is rotated 180 degrees will end up five pixels to the right of the center of the screen.

(2) Centers maintain their orientation with respect to the center of the screen. So a Car "facing" the center of the screen continues to "face" the center as it is rotated, and vice versa.

(3) Translates up, down, left, and right always move the "camera" up, down, left and right. For instance, pushing the "up" key should move the Car up, regardless of whether the car is facing up or down. Rotating the car 180 degrees (or at all) should not rotate the direction of translation (for instance, making the "up" key translate down).


I can get one, sometimes two of these at a time, but haven't been able to get all three. Here is the main chunk of code I've played with, called before the draw function:

protected void setCanvas(Graphics2D g){
    at = g.getTransform();                        //Used to restore the original
    Point screenCenter = Grid.FRAME_CENTER;
                  //Grid is the abstract class maintaining all
                  //properties of the frame and game grid
    g.translate(xloc,yloc);
    g.translate(screenCenter.x+xloc,screenCenter.y+yloc);
    g.rotate(rotation);
    g.scale(scale, scale);
    g.translate(-screenCenter.x-xloc, -screenCenter.y-yloc);
}

I know the transforms occur in "opposite" order so the order above should be correct, but I have tried every permutation possible because, you know, why not.

I have also tried using my rotate function to alter the position at the same time:

public void addRotation(int rot){
    double pi = Math.PI;

    rotation = rotation + ((double) (rot))*pi/180;

    if(rotation > 2*pi){
        rotation = rotation - 2*pi;
    }else if(rotation < 0){
        rotation = rotation + 2*pi;
    }
    double hypoteneuse = Math.sqrt((double) (xloc*xloc+yloc*yloc));
    xloc = Math.cos(rotation)*hypoteneuse;
    yloc = Math.sin(rotation)*hypoteneuse;
}

To no avail. Note that the input is in degrees and Center stores the current rotation in radians, which are required by the Math and transform functions. All variables stored by Center are doubles -- using ints led to some (occasionally hilarious) rounding errors over a lot of rotations.

For the record, here are my translate and rotate functions:

public void translate(int dx, int dy){
    xloc = xloc + dx;
    yloc = yloc + dy;
}

public void addRotation(int rot){
    rotation = rotation + ((double) (rot))*Math.PI/180;
}

Obviously xloc and `yloc are the x and y location of the object on the screen, and the translate function always works properly when there is no rotation.


On another note (I can probably figure this out if I can figure out the above, but may as well), it doesn't want to preserve the shape of the triangle -- after rotating, the x and y distance from the center of the screen are always the same.

Halp?

1

There are 1 answers

2
Vincent Ramdhanie On BEST ANSWER

Looks like you need to separate the position and direction of the objects from the screen/camera. I would suggest keeping a position and rotation of the screen/camera and then each object can have its own position and rotation.

Then at drawing time you can first transform the screen according to the screen/camera values and then draw each object, finally inverting the transform of the screen.

You can use an AffineTransform object to keep track of the screen.

  AffineTransform at = new AffineTransform();
  at.translate(screenCenter.x, screenCenter.y);

Every time the user moves or rotates you add it to the transform. E.g.

  at.rotate(ANGLE_CHOSEN_BY_USER);

Then when you are ready to draw you first:

  g.tranform(at);
  //draw you object
  g.transform(at.createInverse());

Each object can just be drawn at whatever rotation or translation it requires without worry about the camera.