I try to make my own simple game with pairs of images that player try to guess position of same ones and I have no clue how to have 2 same bufferedimages that are animated put on frame in random position that not extends frame size and what need to be done to images to dont put them on same x and y that previous ones. Can collision system do the work and remove the image put on similiar x and y or is there better solutions.
What I got for now is full one image animated that can be covered by the
covering image.
What I did to achieve this.
public Rozmiar(Game game, Textures tex) {
this.game = game;
this.tex = tex;
for (int x = 128; x < (Game.WIDTH * Game.SCALE - 128); x += 128) {
for (int y = 96; y < (Game.HEIGHT * Game.SCALE - 96); y += 128) {
addOdkryte(new Odkryte(x, y, tex));
}
}
}
public void addOdkryte(Odkryte block) {
os.add(block);
}
package com.game.src.main;
import java.awt.Graphics;
import java.util.LinkedList;
public class Odkryte {
private double x;
private double y;
private Textures tex;
Animation anim;
Animation anim1;
private int counter;
public Odkryte(double x, double y, Textures tex) {
this.x = x;
this.y = y;
this.tex = tex;
anim = new Animation(3, tex.odkimg1px128[0], tex.odkimg1px128[1], tex.odkimg1px128[2], tex.odkimg1px128[3],
tex.odkimg1px128[4], tex.odkimg1px128[5], tex.odkimg1px128[6], tex.odkimg1px128[7], tex.odkimg1px128[8],
tex.odkimg1px128[9]);
}
public void tick() {
anim.runAnimation();
}
public void render(Graphics g) {
anim.drawAnimation(g, x, y, 0);
}
}
Add the pairs of images to an ArrayList
Use the Collections.shuffle(...) method on the ArrayList to randomize the images
Iterate through the ArrayList and add each image to the frame.
Related
I am currently working on a 3 cushion billiards game project. I have added two balls on the table so far. I am trying to move one of the balls but I am having a hard time doing that. Should I use a timer? If so then could you tell me an effective way to use the timer on my code so I can move my balls?
Your help would be much appreciated.
Thanks in advance.
Farhan Hasan
I have tried to create a move function for the class balls. But I am not sure what I should put inside the function, I have added the xSpeed and ySpeed. The xLocation and the yLocation changes depending on the xSpeed and ySpeed.
public class Balls
{
private Color ballFillColor;
private Color ballBorderColor;
private int ballX = 0;
private int ballY = 0;
private int xSpeed = 5;
private int ySpeed = 0;
private int ballWidth = 0;
private int ballHeight = 0;
Timer t;
public boolean fillBall = false;
private static Balls ballArray[]; //Required for drawMultipleBalls
Balls(){ //Constructor
ballBorderColor = Color.black;
}
Balls(int ballX, int ballY, int ballWidth, int ballHeight, Color ballBorderColor, JFrame window){ //Constructor
// X , Y , Width, Height, Border Colour, container
this.setBallBorderColor(ballBorderColor);
this.setBallWidth(ballWidth);
this.setBallHeight(ballHeight);
this.setBallX(ballX);
this.setBallY(ballY);
this.drawBall(window);
}
//Here is the move function. I am not really sure what to do here.
public void move()
{
if(this.ballX < 1000 - this.ballWidth)
{
this.ballX += this.xSpeed;
}
try
{
Thread.sleep(1);
}
catch(Exception e)
{
}
}
//GET AND SET FUNCTIONS HERE
//HERE ARE THE FUNCTIONS WHICH ARE RESPONSIBLE FOR DRAWING MY BALLS IN JFRAME
public void drawBall(JFrame frame)
{
frame.getContentPane().add(new MyComponent());
}
public void drawMultipleBalls(JFrame frame, Balls[] balls)
{
ballArray = balls;
frame.getContentPane().add(new MyComponent2());
}
private class MyComponent extends JComponent{
public void paintComponent(Graphics g){
if (fillBall) //Fill first, and then draw outline.
{
g.setColor(ballFillColor);
g.fillOval(getBallX(),getBallY(), getBallHeight(),getBallWidth());
}
g.setColor(getBallBorderColor());
g.drawOval(getBallX(),getBallY(), getBallHeight(),getBallWidth());
}
}
private class MyComponent2 extends JComponent{
public void paintComponent(Graphics g){
for (int i = 0; i < ballArray.length; i++)
{
if (ballArray[i].fillBall) //Fill first, and then draw outline.
{
g.setColor(ballArray[i].ballFillColor);
g.fillOval(ballArray[i].getBallX(),ballArray[i].getBallY(), ballArray[i].getBallHeight(),ballArray[i].getBallWidth());
}
g.setColor(ballArray[i].getBallBorderColor());
g.drawOval(ballArray[i].getBallX(),ballArray[i].getBallY(), ballArray[i].getBallHeight(),ballArray[i].getBallWidth());
}
}
}
Hopefully, I can have two movable balls for the game, the should bounce back as the hit the edge of the screen and they should be able to slow down over time. For that, I am thinking to use a damper (I will multiply the xSpeed and ySpeed with a number less than 1, eventually it will slow down the ball)
Here is a simple example I came up with to show a ball moving and bouncing off the edges.
The direction changes based on the boundary. Left and top edges just check for 0. Bottom and right edges need to include the diameter of the ball.
The x and y increments are independent. And these amounts in conjunction with the timer can change the movement. Notice however, that to have objects bounce off of each other (as in a pool game) is more complicated due to angle of trajectories, etc. And the distances bounced will vary and slow with time based on frictional values. Everything else is documented in the Java API.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MovementDemo extends JPanel implements ActionListener {
JFrame frame = new JFrame("Movement Demo");
int size = 500;
int x = 50;
int y = 200;
int diameter = 50;
int yinc = 2;
int xinc = 2;
int xdirection = 1;
int ydirection = 1;
public MovementDemo() {
setPreferredSize(new Dimension(size, size));
frame.add(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new MovementDemo().start());
}
public void start() {
Timer timer = new Timer(100, this);
timer.setDelay(5);
timer.start();
}
public void actionPerformed(ActionEvent ae) {
if (x < 0) {
xdirection = 1;
}
else if (x > size - diameter) {
xdirection = -1;
}
if (y < 0) {
ydirection = 1;
}
else if (y > size - diameter) {
ydirection = -1;
}
x = x + xdirection * xinc;
y = y + ydirection * yinc;
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLUE);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.fillOval(x, y, diameter, diameter);
}
}
It seems in general there are a few things you need to figure out:
has the ball collided with another ball
has the ball collided with a wall
otherwise just figure out what is the ball's new position based on its velocity
Below is some sample code that stubs some of this out. You can first compare the current ball's position to all others (not including the current ball of course). If there are any equal positions, process a collision with a ball. If the ball is at the window border i.e it hit a wall, process a collision with a wall. Otherwise just calculate its new position based on its current velocity.
The process collision part is just to apply physics mechanics to whatever degree of complexity you require. One general suggested change would be to update the velocity of the balls then apply it to the position after. The specific calculations for velocity changes you could apply as needed and as you can imagine it can get pretty involved which is why I suggest using a separate method and possibly a sub class for velocity instead of managing each part of the velocity vector in the ball itself. I used the wall as an object because of this. The composition, weights, velocities etc of the object's colliding can affect the resulting collision, but how complex you want that processing to be is up to you.
Sorry I'm no physics expert but I hope this sends you in the right direction in terms of code! Also this might help with the specific calculations you might want to use:
https://www.khanacademy.org/science/physics/one-dimensional-motion/displacement-velocity-time/v/calculating-average-velocity-or-speed
public void move()
{
// check if balls are on same position not including this ball
for(Ball b: ballArray){
if (this.position == b.position && this != b){
processCollision(this, b, null);
} else{
// if the ball hasn't collided with anything process its movement based on speed
// this assumes a 1000 x 1000 window for keeping objects inside it
if(this.ballX < 1000 - this.ballWidth && this.ballY < 1000 - this.ballHeight){
this.ballX += this.xSpeed;
this.ballY += this.ySpeed;
}else {
processCollision(this, null, new Wall());
}
}
}
try
{
Thread.sleep(1);
}
catch(Exception e)
{
}
}
public void processCollision(Ball b1, Ball b2, Wall w){
// if ball hasn't collided with a wall, process a ball - ball collision
if(w == null){
// apply physics mechanics according the complexity desired for ball collisions
b1.xSpeed -= b2.xSpeed;
b1.ySpeed -= b2.ySpeed;
// ball 2 would end up slowing down
b2.xSpeed -= b1.xSpeed;
b2.ySpeed -= b1.ySpeed;
}
// if ball hasn't collided with a ball, process a ball - wall collision
if(b2 == null){
// apply physics mechanics for hitting a wall
// e.g as below: just send ball in opposite direction
b1.xSpeed = b1.xSpeed * -1;
b1.ySpeed = b1.ySpeed * -1;
}
// either way, process ball's new position based on its new speed
b1.ballX += b1.xSpeed;
b1.ballY += b1.ySpeed;
b2.ballX += b2.xSpeed;
b2.ballY += b2.ySpeed;
}
So I can load a 2D map using tiles using a text file, which is great and all. However, one issue I have met with this method is that I can't add objects/actors to my map since the file is a 2D grid. (The game is similar to games like zelda and pokemon.) I've tried creating an object layer so I can overlap images, but it doesn't seem to work for me. To give an example of what I want, have objects such as trees to be solid and on top of the background grass.
I am also looking for better methods to creating these tile based maps if you want to pitch some ideas to me.
**Note: I am about beginner/intermediate at Java.
Here is my constructor for the GameState class that calls the Map.
public GameState(Game game) {
super(game);
player = new Player(game, 0, 0, 64, 64);
map = new Map(game, "res/saves/save1.txt");
}
Here is the Map class (which works) that also calls the object (2nd) layer.
private int width, height;
public static int spawnX, spawnY;
private int[][] mapTiles;
MapObjects mapObjects;
Game game;
public Map(Game game, String path) {
this.game = game;
mapObjects = new MapObjects(game, "res/saves/save1_obj.txt", width, height);
loadMap(path);
}
private void loadMap(String path) {
String file = Utils.loadFileAsString(path);
//Token is which number it is out of the total
String[] tokens = file.split("\\s+");
//Sets what is what
width = Utils.parseInt(tokens[0]);
height = Utils.parseInt(tokens[1]);
spawnX = Utils.parseInt(tokens[2]);
spawnY = Utils.parseInt(tokens[3]);
mapTiles = new int[width][height];
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
// (x+y*width) : calculates the nth token (+4) : The 4 prior tokens before the graph
mapTiles[x][y] = Utils.parseInt(tokens[(x + y *width) + 4]);
}
}
}
public void render(Graphics g) {
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
//Only renders what is seen.
getMapTile(x, y).render(g, (int)(x*Tile.TILE_WIDTH-game.getCamera().getxOffset()), (int)(y*Tile.TILE_HEIGHT-game.getCamera().getyOffset()));
}
}
}
public void tick() {
}
//Gets the specific tile at specific coordinates.
private Tile getMapTile(int x, int y) {
Tile t = Tile.tiles[mapTiles[x][y]];
if(t == null) {
return Tile.grassTile;
}
return t;
}
And lastly, the object layer that doesn't work. It does not give an error, just the overlapping objects aren't visible. I've made sure to load the object layer before the Map layer, but that doesn't seem to be the issue.
private int width, height;
private int[][] objTiles;
Game game;
public MapObjects(Game game, String path, int width, int height) {
this.game = game;
loadObjects(path, width, height);
}
public void loadObjects(String path, int width, int height) {
this.width = width;
this.height = height;
String file = Utils.loadFileAsString(path);
String[] tokens = file.split("\\s+");
objTiles = new int[width][height];
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
objTiles[x][y] = Utils.parseInt(tokens[(x + y *width)]);
}
}
}
public void render(Graphics g) {
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
//Only renders what is seen.
getObjTile(x, y).render(g, (int)(x*Tile.TILE_WIDTH-game.getCamera().getxOffset()), (int)(y*Tile.TILE_HEIGHT-game.getCamera().getyOffset()));
}
}
}
public void tick() {
}
//Gets the specific object tile at specific coordinates.
private Tile getObjTile(int x, int y) {
Tile t = Tile.tiles[objTiles[x][y]];
if(t == null) {
return Tile.nothingTile;
}
return t;
}
We may need a bit more info from you.
Do you use a different "container/component" to draw map tiles than you do for your objects? Because if you render objects first then they will disappear as soon as you render the map. You should draw the map first, and then do objects like so:
public Map(Game game, String path) {
this.game = game;
//swapped the order of the lines below so the map loads first:
loadMap(path);
mapObjects = new MapObjects(game, "res/saves/save1_obj.txt", width, height);
}
From what you have said then this does not work either, however if you use the same component to draw your map and objects then one will always override the other, and something will always be missing. To fix this you need to crease two separate panes, one for the map, and a transparent one that sits on top of the map that you can use to render your objects.
See this illustration as an example:
You basically need to add a new transparent "content plane" similar to the way that glass pane shown above.
I am attempting to draw a checkerboard pattern in java using nested for loops, but I am having trouble doing it with two different colors. I know this question has been asked before, but it hasn't been asked with two different colors on the board that are not just using a background color. I plan on using the individual squares as an array to hold checker positions, so I do need each individual square made. Would it be better to drop the ice of a nested for loop to create each square, or should i stick with that shortcut? And if I were to stick with it, how would the nested loop be formatted (one for each color)?
When creating checker tiles, I would pass in an int for the x coordinate, and y coordinate such as:
import java.awt.Color;
import java.awt.Graphics;
public class CheckerTile {
public static final int WIDTH = 100; //width of each tile
public static final int HEIGHT = 100; //height of each tile, most likely same as width so its a square
public static int currentId = 0; //variable to reference unique id for each tile
private int id; //current id of tile
private int x; //x coordinate
private int y; //y coordinate
private int width; //width of tile
private int height; //height of tile
//Default constructor to take x and y coordinate
public CheckerTile( int x, int y ) {
this.id = currentId++;
this.x = x;
this.y = y;
width = WIDTH;
height = HEIGHT;
}
public int getId()
{
return id;
}
//draws the tile on the panel.
public void draw(Graphics g)
{
//if the checkerTile's id is divisible by 2, draw it red, otherwise draw it black.
g.setColor( id % 2 == 0 ? Color.RED : Color.black);
g.fillRect(x, y, width, height);
}
}
That way we have a way to draw the tile on the board. Now, when creating each object, we increment a currentId variable so that we can color each one individually using the modulus operator later.
I am assuming you are using Swing so I decided to add a draw(Graphics g) method so when repainting in java it would use that Graphics object. If you are using a different library, then you will have to do some research in to how to draw it on the board.
Now in your JPanel, it would look something like this:
//Creates the JPanel, which needs to be added to JFrame object in main
import java.awt.BorderLayout;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class CheckerBoard extends JPanel {
CheckerTile[][] checkerTiles; //2-dimension array of checkerTiles
public CheckerBoard() {
super();
this.setSize(800,800);
checkerTiles = new CheckerTile[9][9];
//This creates the checkerTiles.
for(int i = 0; i < 9; i++)
{
for( int j = 0; j < 9; j++)
{
checkerTiles[i][j] = new CheckerTile( j * CheckerTile.WIDTH, i * CheckerTile.HEIGHT );
}
}
this.setVisible(true);
//Repaint right away to show results.
repaint();
}
//We need to override this to paint the tiles on the board.
#Override
public void paintComponent(Graphics g)
{
for(int i = 0; i < checkerTiles.length; i++)
{
for(int j = 0; j < checkerTiles[i].length; j++)
{
//call the draw method on each tile.
checkerTiles[i][j].draw(g);
}
}
}
//A demo of adding the panel to a frame and showing the tiles.
public static void main(String[] args)
{
//Create the JFrame and add the CheckerBoard we made to it.
JFrame frame = new JFrame();
frame.setSize(800,800);
frame.setLayout(new BorderLayout());
frame.add(new CheckerBoard(), BorderLayout.CENTER);
frame.setVisible(true);
}
}
I am developing a game, and the principle of the game is a bit like a rhythm game- you need to press a button/click the screen right on the incoming red blocks which are all lined up in a black line(sorry I can't post pics due to low reputation) . The thing is, I want the game to be like a mini game where the red blocks will spawn randomly, sometimes as a 'long note' and sometimes as 'short note', but I can't think of a way to do this efficiently. So far I have created two block variables with random number of gaps between each other, but I can't shake of the fear that this is quite unnecessary. Here's the code for the blocks-
package com.jollygent.game;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Random;
public class Hitbox {
private int x,y;
BufferedImage hitbox;
Random random;
BufferedImage hitbox2;
private int gap;
public Hitbox(int x,int y,GamePanel game) {
this.x = x;
this.y = y;
SpriteSheet ss = new SpriteSheet(game.getSheet());
hitbox = ss.getImage(1,2,64,64);
hitbox2 = ss.getImage(1,2,64,64);
random = new Random();
gap = random.nextInt(128) + 64;
}
public void update() {
if(x < -GamePanel.WIDTH) {
x = GamePanel.WIDTH;
gap = random.nextInt(128) + 64;
}
x-= 6;
}
public void render(Graphics g) {
g.drawImage(hitbox,x,y,null);
g.drawImage(hitbox2,x + gap,y,null);
}
}
how should I approach this?
I have the following code which does (the first part of) what I want drawing a chessboard with some pieces on it.
Image pieceImage = getImage(currentPiece);
int pieceHeight = pieceImage.getHeight(null);
double scale = (double)side/(double)pieceHeight;
AffineTransform transform = new AffineTransform();
transform.setToTranslation(xPos, yPos);
transform.scale(scale, scale);
realGraphics.drawImage(pieceImage, transform, this);
that is, it gets a chess piece's image and the image's height, it translates the drawing of that image to the square the piece is on and scales the image to the size of the square.
Llet's say I want to rotate the black pieces 180 degrees. Somewhere I expect to have something like:
transform.rotate(Math.toRadians(180) /* ?, ? */);
But I can't figure out what to put in as X and Y. If I put nothing, the image is nicely rotated around the 0,0 point of its chessboard square, putting the piece upside down in the square to the northeast of where it is supposed to be. I've guessed at various other combinations of x,y, with no luck yet.
I am already using translation to put the piece in the right square, the rotation transform wants another x,y around which to rotate things, but I don't know how to tell the transform to rotate the piece around one x,y and write the image to a different x,y. Can someone help me with the rotation parameters, or point me to something that explains how these things work? I've found examples of things that don't explain how they work, and so far I haven't figured out how to alter them to my situation...
Major edit: addition of working code. Sorry, I don't know how to post images, please substitute your own.
When I run the following I get a 2x2 chess board with a rook at the top left and a knight at the bottom right.
If I go into SmallChessboardComponent and take the comment delims off the first rotation transform statement, I get the rook in its original place upside down and the knight does not appear. If I instead take the comment delims off the second transform statement, neither piece appears at all.
I am looking for a way to turn the pieces upside down on the square on which they would appear anyway. I want to draw each piece onto the board; I don't want code that flips the board.
main program:
package main;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import directredraw.SmallChessboardComponent;
public class SmallChessboardMain
{
private static void dbg (String message) { System.out.println(message); }
public static void main(String[] args)
{
//Create the top-level container and add contents to it.
final JFrame frame = new JFrame("Small Chessboard");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// create the chessboard itself and set it in the component
SmallChessboard chessboard = new SmallChessboard();
// create the GUI component that will contain the chessboard
SmallChessboardComponent chessboardComponent = new SmallChessboardComponent();
chessboardComponent.setBoard (chessboard);
frame.getContentPane().add(chessboardComponent, BorderLayout.CENTER);
// pack and display all this
frame.pack();
frame.setVisible(true);
}
}
chessboard class:
package main;
public class SmallChessboard
{
Piece [][] squares = new Piece[2][2];
public SmallChessboard()
{
squares[0][0] = new Piece(Piece.WHITECOLOR, Piece.ROOK);
squares[1][1] = new Piece(Piece.WHITECOLOR, Piece.KNIGHT);
}
/**
* get the piece at the given rank and file; null if
* no piece exists there.
*/
public Piece getPiece(int rank, int file)
{
if (0 > rank || rank > 2 || 0 > file || file > 2) { return null; }
else { return squares[rank][file]; }
}
}
chessboard component class:
package directredraw;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import javax.swing.JPanel;
import main.Piece;
import main.PieceImages;
import main.SmallChessboard;
public class SmallChessboardComponent extends JPanel
{
private static final long serialVersionUID = 1L;
Color whiteSquareColor = Color.yellow;
Color blackSquareColor = Color.blue;
private static void dbg (String msg) { System.out.println(msg); }
private SmallChessboard chessboard = null;
// currently playing with rotating images; this affine transform
// should help
AffineTransform rotationTransform = null;
private final int DEFAULT_PREFERRED_SIDE = 400;
int wholeSide = DEFAULT_PREFERRED_SIDE;
int side = DEFAULT_PREFERRED_SIDE / 8;
public void setBoard (SmallChessboard givenBoard)
{ chessboard = givenBoard;
}
/**
* set either or both colors for this chessboard; if either of
* the arguments are null, they do not change the existing color
* setting.
*/
public void setColors (Color darkSquare, Color lightSquare)
{
if (darkSquare != null) { blackSquareColor = darkSquare; }
if (lightSquare != null) { whiteSquareColor = lightSquare; }
}
/**
* return the preferred size for this component.s
*/
public Dimension getPreferredSize()
{ return new Dimension(wholeSide, wholeSide);
}
/*
* return the image object for the given piece
*/
private Image getImage(Piece piece)
{ return PieceImages.getPieceImage(this, piece);
}
public void paintComponent (Graphics graphics)
{
Graphics2D realGraphics = (Graphics2D) graphics;
// the image container might have been stretched.
// calculate the largest square held by the current container,
// and then 1/2 of that size for an individual square.
int wholeWidth = this.getWidth();
int wholeHeight = this.getHeight();
wholeSide = (wholeWidth / 2) * 2;
if (wholeHeight < wholeWidth) { wholeSide = (wholeHeight / 2) * 2; }
side = wholeSide / 2;
Rectangle clip = realGraphics.getClipBounds();
boolean firstColumnWhite = false;
// for each file on the board:
// set whether top square is white
// set background color according to white/black square
//
for (int fileIndex=0; fileIndex<8; fileIndex++)
{ boolean currentColorWhite = firstColumnWhite;
firstColumnWhite = !firstColumnWhite;
// draw the board and all the pieces
int rankIndex = 2;
for (rankIndex=2; rankIndex>=0; rankIndex--)
{
currentColorWhite = !currentColorWhite;
// x and y position of the top left corner of the square we're drawing,
// and rect becomes the dimensions and position of the square itself.
int xPos = fileIndex * side;
int yPos = rankIndex * side;
Rectangle rect = new Rectangle(xPos, yPos, side, side);
// if this square intersects the clipping rectangle we're drawing,
// then we'll draw the square and the piece on the square.
if (rect.intersects(clip))
{
// this puts down the correct color of square
if (currentColorWhite) { realGraphics.setColor(whiteSquareColor); }
else { realGraphics.setColor(blackSquareColor); }
realGraphics.fillRect(xPos, yPos, side, side);
// if there is a piece on this square and it isn't selected at the
// moment, then draw it.
Piece currentPiece = chessboard.getPiece(rankIndex, fileIndex);
if (currentPiece != null)
{
Image pieceImage = getImage(currentPiece);
int pieceHeight = pieceImage.getHeight(null);
double scalePiece = (double)side/(double)pieceHeight;
AffineTransform transform = new AffineTransform();
// transform.setToRotation(Math.toRadians(180));
transform.setToRotation(Math.toRadians(180), side/2, side/2);
transform.scale(scalePiece, scalePiece);
transform.translate(xPos/scalePiece, yPos/scalePiece);
// if (currentPiece.isBlack())
// {
// transform.translate(xPos + (side+2), yPos + (side+2));
// transform.rotate(Math.toRadians(180) /*, ,*/ );
// }
// else
// {
// transform.translate(xPos, yPos);
// }
realGraphics.drawImage(pieceImage, transform, this);
}
}
}
}
}
}
Piece.java
package main;
public class Piece
{
// piece types; the sum of the piece type and the
// color gives a number unique to both type and color,
// which is used for things like image indices.
public static final int PAWN = 0;
public static final int KNIGHT = 1;
public static final int BISHOP = 2;
public static final int ROOK = 3;
public static final int QUEEN = 4;
public static final int KING = 5;
// one of these is the color of the current piece
public static final int NOCOLOR = -1;
// the sum of the piece type and the
// color gives a number unique to both type and color,
// which is used for things like image indices.
public static final int BLACKCOLOR = 0;
public static final int WHITECOLOR = 6;
int color = NOCOLOR;
int imageIndex;
public Piece(int color, int pieceType)
{
// dbg -- all pieces are white rooks for now...
this.color = color;
imageIndex = color + pieceType;
}
/**
* return the integer associated with this piece's color;
*/
int getPieceColor()
{ return color;
}
/**
* return true if the piece is black
*/
public boolean isBlack()
{
return (color == BLACKCOLOR);
}
/**
* set the color associated with this piece; constants
* found in this class.
*/
public void setPieceColor(int givenColor)
{ color = givenColor;
}
/**
* return the integer designated for the image used for this piece.
*/
int getImageIndex()
{ return imageIndex;
}
}
and PieceImages.java
package main;
import java.awt.Component;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.net.URL;
public class PieceImages
{ static Image images[] = null;
private static void dbg (String msg) { System.out.println(msg); }
public static Image getPieceImage (Component target, Piece piece)
{
if (images == null)
try
{
MediaTracker tracker = new MediaTracker(target);
images = new Image[12];
images[Piece.BLACKCOLOR + Piece.PAWN] = getImage(tracker, "bPawn.gif");
images[Piece.BLACKCOLOR + Piece.KNIGHT] = getImage(tracker, "bKnight.gif");
images[Piece.BLACKCOLOR + Piece.BISHOP] = getImage(tracker, "bBishop.gif");
images[Piece.BLACKCOLOR + Piece.ROOK] = getImage(tracker, "bRook.gif");
images[Piece.BLACKCOLOR + Piece.QUEEN] = getImage(tracker, "bQueen.gif");
images[Piece.BLACKCOLOR + Piece.KING] = getImage(tracker, "bKing.gif");
images[Piece.WHITECOLOR + Piece.PAWN] = getImage(tracker, "wPawn.gif");
images[Piece.WHITECOLOR + Piece.KNIGHT] = getImage(tracker, "wKnight.gif");
images[Piece.WHITECOLOR + Piece.BISHOP] = getImage(tracker, "wBishop.gif");
images[Piece.WHITECOLOR + Piece.ROOK] = getImage(tracker, "wRook.gif");
images[Piece.WHITECOLOR + Piece.QUEEN] = getImage(tracker, "wQueen.gif");
images[Piece.WHITECOLOR + Piece.KING] = getImage(tracker, "wKing.gif");
if (!tracker.waitForAll(10000))
{ System.out.println("ERROR: not all piece main.images loaded");
}
dbg("piece images loaded");
}
catch (Exception xcp)
{ System.out.println("Error loading images");
xcp.printStackTrace();
}
return images[piece.getImageIndex()];
}
private static Image getImage(MediaTracker tracker, String file)
{
URL url = PieceImages.class.getResource("images/" + file);
Image image = Toolkit.getDefaultToolkit().getImage(url);
tracker.addImage(image, 1);
return image;
}
}
Okay, this is a little slight of hand. The example code will only work for 90 degree increments (it was only designed this way), to do smaller increments you to use some trig to calculate the image width and height (there's a answer somewhere for that to ;))
public class ImagePane extends JPanel {
private BufferedImage masterImage;
private BufferedImage renderedImage;
public ImagePane(BufferedImage image) {
masterImage = image;
applyRotation(0);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(renderedImage.getWidth(), renderedImage.getHeight());
}
#Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
protected int getVirtualAngle(int angle) {
float fRotations = (float) angle / 360f;
int rotations = (int) (fRotations - (fRotations / 1000));
int virtual = angle - (rotations * 360);
if (virtual < 0) {
virtual = 360 + virtual;
}
return virtual;
}
public void applyRotation(int angle) {
// This will only work for angles of 90 degrees...
// Normalize the angle to make sure it's only between 0-360 degrees
int virtualAngle = getVirtualAngle(angle);
Dimension size = new Dimension(masterImage.getWidth(), masterImage.getHeight());
int masterWidth = masterImage.getWidth();
int masterHeight = masterImage.getHeight();
double x = 0; //masterWidth / 2.0;
double y = 0; //masterHeight / 2.0;
switch (virtualAngle) {
case 0:
break;
case 180:
break;
case 90:
case 270:
size = new Dimension(masterImage.getHeight(), masterImage.getWidth());
x = (masterHeight - masterWidth) / 2.0;
y = (masterWidth - masterHeight) / 2.0;
break;
}
renderedImage = new BufferedImage(size.width, size.height, masterImage.getTransparency());
Graphics2D g2d = renderedImage.createGraphics();
AffineTransform at = AffineTransform.getTranslateInstance(x, y);
at.rotate(Math.toRadians(virtualAngle), masterWidth / 2.0, masterHeight / 2.0);
g2d.drawImage(masterImage, at, null);
g2d.dispose();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - renderedImage.getWidth()) / 2;
int y = (height - renderedImage.getHeight()) / 2;
g2d.drawImage(renderedImage, x, y, this);
}
}
Now, you could simply "flip" the image vertically, if that works better for you
public class FlipPane extends JPanel {
private BufferedImage masterImage;
private BufferedImage renderedImage;
public FlipPane(BufferedImage image) {
masterImage = image;
flipMaster();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(renderedImage.getWidth(), renderedImage.getHeight());
}
#Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
protected void flipMaster() {
renderedImage = new BufferedImage(masterImage.getWidth(), masterImage.getHeight(), masterImage.getTransparency());
Graphics2D g2d = renderedImage.createGraphics();
g2d.setTransform(AffineTransform.getScaleInstance(1, -1));
g2d.drawImage(masterImage, 0, -masterImage.getHeight(), this);
g2d.dispose();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - renderedImage.getWidth()) / 2;
int y = (height - renderedImage.getHeight()) / 2;
g2d.drawImage(renderedImage, x, y, this);
}
}
This basically results in:
Original | 180 degree rotation | Vertical inversion...
Now, if you change the flipMaster method to read:
g2d.setTransform(AffineTransform.getScaleInstance(-1, -1));
g2d.drawImage(masterImage, -masterImage.getWidth(), -masterImage.getHeight(), this);
You'll get the same effect as the 180 rotation ;)
Try performing the rotation before translating it into the correct position. Simply reorder the transformations so that first you scale, then you rotate (around the center point of the image), and then you translate:
transform.scale(scale, scale);
transform.rotate(Math.PI, pieceWidth / 2, pieceHeight /2);
transform.translation(xPos, yPos);
By the way, the black pieces on a chess board usually aren't rotated. :)
Update
In what way does it not work? The solution I provided also also differs from your code in that scaling is performed before translating. You can try the rotating, translating, and then scaling.
I strongly suggest that you modify your code so that you can perform the translation last. If you do this, everything will become a lot less complicated. Once you have done so, you only have to scale once to automatically take care of the rotation.
transform.scale(scale, scale); // or transform.scale(scale, -scale); to rotate
transform.translate(xPos, yPos);