Exercise1609: Write a program that draws line segments using the arrow keys. The line starts from the center of the frame and draws towards east,north, west, or south when the right-arrow key, up-arrow key, left-arrow key, or down-arrow key is pressed. In short, draw a maze. See comments below for a description of my question.
import java.awt.*;
import java.awt.event.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Exercise1609 extends JFrame {
private KeyboardPanel panel = new KeyboardPanel();
public Exercise1609() {
add(panel);
panel.setFocusable(true);
}
public static void main(String[] args) {
Exercise1609 frame = new Exercise1609();
frame.setTitle("Tegn med piltaster");
frame.setSize(600, 300);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
//The panel that listens for key and responds by drawing
public static class KeyboardPanel extends JPanel {
private int x,y,previousX,previousY;
private boolean firstTime = true;
public KeyboardPanel() {
/**
* why must x and y be initialized inside paintComponent?
* if I want to start drawing from the middle of the panel?
* If I remove the if-block inside paintComponent and instead
* place the initialization here, as shown with the two lines below:
* x = previousX = getWidth() / 2;
* y = previousY = getHeight() / 2;
* ...then the program will not start to draw from the middle,
* but upper left corner of the screen
*/
addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
previousY = y;
previousX = x;
switch (e.getKeyCode()) {
case KeyEvent.VK_DOWN:
y++;
break;
case KeyEvent.VK_UP:
y--;
break;
case KeyEvent.VK_RIGHT:
x++;
break;
case KeyEvent.VK_LEFT:
x--;
break;
}
repaint();
}
});
}//end constructor
#Override
protected void paintComponent(Graphics g) {
super.paintComponents(g);
if(firstTime) {
//Why can't x and y be initialized outiside paintComponent?
//Why can't they be initialized inside the constructor of this class?
x = previousX = getWidth() / 2;
y = previousY = getHeight() / 2;
firstTime = false;
}
g.drawLine(previousX, previousY, x, y);
System.out.println(x + " " + y);
}
}
}
The last line System.out.println(x + " " + y); outputs 0,0 if I try to initialize x and y anywhere else
but paintComponent(). When initialized inside paintcomponent() the output is 292,131...which is what I want.
getWidth() and getHeight() are not set correctly until the UI elements have gone through a layout pass. This is guaranteed to happen before paintComponent() is called but probably not at other points where you have tried to call them.
See: getWidth() and getHeight() are 0 after calling setPreferredSize()
If you need to be notified when the width and height of the component is set/changed, check out ComponentListener: http://docs.oracle.com/javase/7/docs/api/java/awt/event/ComponentListener.html
Related
I have a problem that I haven't been able to completely understand and thus I am struggling to fix it.
Basically I am busy writing a small game engine for Java Swing, and one of the key components of this engine is the ability to separate design resolution from screen resolution. Meaning if I design a game on a resolution of 400 (w) x 300 (h), and I position an object at the center of the design resolution, then a user can specify the actual resolution they want to play the game at for example 800 (w) x 600 (h) and the object will still be placed correctly at the center of the screen in the current resolution.
This is where I am having trouble, when the design resolution and the current resolution are the same i.e. design resolution 400 x 300 and current resolution is 400 x 300, the object seems to be placed correctly at the center of the screen on start up and the bullet correctly at the center of the player regardless of the players position when moved:
However when the design resolution and current screen resolution are not the same i.e. design resolution 400 x 300 and current resolution is 800 x 600 the object is no longer correctly placed at center of the screen and neither is the bullet centered for the player:
I have a method to generate the center spawn point for all visible objects (the red reference dot, the sprite/player and the bullet) this method is a simple convenience method to help generate a center based coordinate for a Sprite within a container or another Sprite:
public static Point2D getCenterSpawnPoint(int parentWidth, int parentHeight, int childWidth, int childHeight, double childXOffset, double childYOffset) {
double spawnX = ((parentWidth - childWidth) / 2) + childXOffset;
double spawnY = ((parentHeight - childHeight) / 2) + childYOffset;
return new Point2D.Double((int) spawnX, (int) spawnY);
}
The Sprite and bullet render using screen coordinates:
public int getScreenX() {
//return (int) (imageScaler.getWidthScaleFactor() * this.getX());
return (int) ((double) this.getX() / DESIGN_SCREEN_SIZE.width * CURRENT_SCREEN_SIZE.width);
}
public int getScreenY() {
//return (int) (imageScaler.getHeightScaleFactor() * this.getY());
return (int) ((double) this.getY() / DESIGN_SCREEN_SIZE.height * CURRENT_SCREEN_SIZE.height);
}
I am unsure of where I am going wrong, but essentially what Id want to see is the same behavior in my first GIF regardless of the current screen size the game is in, the red reference dot seems to position correctly and it is simply drawn to the JPanel and bypasses the getScreen... calls:
// lets draw a centered dot based on the panels dimensions for a reference
int dotSize = 10;
g2d.setColor(Color.red);
Point2D centeredReferencePoint = getCenterSpawnPoint(getWidth(), getHeight(), dotSize, dotSize, 0, 0);
g2d.fillOval((int) centeredReferencePoint.getX(), (int) centeredReferencePoint.getY(), dotSize, dotSize);
Here is the minaml reproducible example:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.*;
public class ResolutionIndependentLocationIssue {
/**
* uncommenting this and commenting the line below will result in the bullet
* spawning correctly at the center of the sprite/player
*/
private static final Dimension CURRENT_SCREEN_SIZE = new Dimension(800, 600);
//private static final Dimension CURRENT_SCREEN_SIZE = new Dimension(400, 300);
private static final Dimension DESIGN_SCREEN_SIZE = new Dimension(400, 300);
private Scene scene;
private Sprite player;
public ResolutionIndependentLocationIssue() {
try {
createAndShowUI();
} catch (IOException ex) {
Logger.getLogger(ResolutionIndependentLocationIssue.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(ResolutionIndependentLocationIssue::new);
}
private void createAndShowUI() throws MalformedURLException, IOException {
JFrame frame = new JFrame("Resolution Issue");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BufferedImage bulletImage = resize(ImageIO.read(new URL("https://i.stack.imgur.com/JlSEL.png")), 20, 20);
BufferedImage playerImage = resize(ImageIO.read(new URL("https://icons.iconarchive.com/icons/icons8/windows-8/512/Programming-Java-Duke-Logo-icon.png")), 100, 100);
player = new Sprite(playerImage);
player.setBulletImage(bulletImage);
System.out.println();
// center player according to our design resolution
Point2D spawnPoint = getCenterSpawnPoint(DESIGN_SCREEN_SIZE.width, DESIGN_SCREEN_SIZE.height, playerImage.getWidth(), playerImage.getHeight(), 0, 0);
player.setPosition((int) spawnPoint.getX(), (int) spawnPoint.getY());
System.out.println("ResolutionScalingIssue#createAndShowUI() - Player spawn point (always expressed in design resolution co-ordinates): X: " + spawnPoint.getX() + " Y: " + spawnPoint.getY());
System.out.println("ResolutionScalingIssue#createAndShowUI() - Player Design Resolution X: " + player.getX() + " Y: " + player.getY());
System.out.println("ResolutionScalingIssue#createAndShowUI() - Player Screen X: " + player.getScreenX() + " Screen Y: " + player.getScreenY());
System.out.println("ResolutionScalingIssue#createAndShowUI() - Player Width: " + playerImage.getWidth() + " Height: " + playerImage.getHeight());
System.out.println();
this.scene = new Scene();
this.scene.add(player);
this.addKeyBindings();
frame.add(this.scene);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Thread gameLoop = new Thread(() -> {
while (true) {
this.scene.update();
this.scene.repaint();
try {
Thread.sleep(15);
} catch (InterruptedException ex) {
}
}
});
gameLoop.start();
}
private void addKeyBindings() {
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "A pressed");
this.scene.getActionMap().put("A pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.LEFT = true;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "A released");
this.scene.getActionMap().put("A released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.LEFT = false;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "D pressed");
this.scene.getActionMap().put("D pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.RIGHT = true;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "D released");
this.scene.getActionMap().put("D released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.RIGHT = false;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "W pressed");
this.scene.getActionMap().put("W pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.UP = true;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "W released");
this.scene.getActionMap().put("W released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.UP = false;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "S pressed");
this.scene.getActionMap().put("S pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.DOWN = true;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "S released");
this.scene.getActionMap().put("S released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.DOWN = false;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "Space pressed");
this.scene.getActionMap().put("Space pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.shoot();
}
});
}
public static BufferedImage resize(BufferedImage image, int width, int height) {
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TRANSLUCENT);
Graphics2D g2d = (Graphics2D) bi.createGraphics();
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2d.drawImage(image, 0, 0, width, height, null);
g2d.dispose();
return bi;
}
/**
* Used to calculate the center based spawning point, to ensure calculations
* are the same for the player spawning on the screen and bullet spawning
* from the player
*
* #return
*/
public static Point2D getCenterSpawnPoint(int parentWidth, int parentHeight, int childWidth, int childHeight, double childXOffset, double childYOffset) {
double spawnX = ((parentWidth - childWidth) / 2) + childXOffset;
double spawnY = ((parentHeight - childHeight) / 2) + childYOffset;
return new Point2D.Double((int) spawnX, (int) spawnY);
}
public class Scene extends JPanel {
private final ArrayList<Sprite> sprites;
public Scene() {
this.sprites = new ArrayList<>();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
sprites.forEach((sprite) -> {
sprite.render(g2d);
});
// lets draw a centered dot based on the panels dimensions for a reference
int dotSize = 10;
g2d.setColor(Color.red);
Point2D centeredReferencePoint = getCenterSpawnPoint(getWidth(), getHeight(), dotSize, dotSize, 0, 0);
g2d.fillOval((int) centeredReferencePoint.getX(), (int) centeredReferencePoint.getY(), dotSize, dotSize);
}
#Override
public Dimension getPreferredSize() {
return CURRENT_SCREEN_SIZE;
}
#Override
public boolean getIgnoreRepaint() {
return true;
}
public void add(Sprite sprite) {
sprite.setScence(this);
this.sprites.add(sprite);
}
private void update() {
sprites.forEach((sprite) -> {
sprite.update();
});
}
}
public class Sprite {
protected int x;
protected int y;
protected int speed = 5;
protected final BufferedImage image;
public boolean UP, DOWN, LEFT, RIGHT;
private boolean isFlippedX = false;
private Scene scene;
private BufferedImage bulletImage;
public Sprite(BufferedImage image) {
this.image = image;
}
public void render(Graphics2D g2d) {
// sprite is drawn based on the position of the current screen relative to our design screen size
g2d.setColor(Color.red);
g2d.drawRect(this.getScreenX(), this.getScreenY(), this.getWidth(), this.getHeight());
if (this.isFlippedX) {
// flip horizontally
g2d.drawImage(this.image, this.getScreenX() + this.image.getWidth(), this.getScreenY(), -this.getWidth(), this.getHeight(), null);
} else {
g2d.drawImage(this.image, this.getScreenX(), this.getScreenY(), null);
}
}
public void update() {
if (LEFT) {
setFlippedX(true);
this.x -= this.speed;
}
if (RIGHT) {
setFlippedX(false);
this.x += this.speed;
}
if (UP) {
this.y -= this.speed;
}
if (DOWN) {
this.y += this.speed;
}
}
public void setFlippedX(boolean isFlippedX) {
this.isFlippedX = isFlippedX;
}
/**
*
* #return The current screen x co-ordindate of the sprite relative to
* the design resolution
*/
public int getScreenX() {
//return (int) (imageScaler.getWidthScaleFactor() * this.getX());
return (int) ((double) this.getX() / DESIGN_SCREEN_SIZE.width * CURRENT_SCREEN_SIZE.width);
}
/**
*
* #return The current screen y co-ordindate of the sprite relative to
* the design resolution
*/
public int getScreenY() {
//return (int) (imageScaler.getHeightScaleFactor() * this.getY());
return (int) ((double) this.getY() / DESIGN_SCREEN_SIZE.height * CURRENT_SCREEN_SIZE.height);
}
/**
*
* #return The design resolution x co-ordindate
*/
public int getX() {
return this.x;
}
/**
*
* #return The design resolution y co-ordindate
*/
public int getY() {
return this.y;
}
public int getWidth() {
return this.image.getWidth();
}
public int getHeight() {
return this.image.getHeight();
}
public void setPosition(int x, int y) {
this.x = x;
this.y = y;
}
public void setBulletImage(BufferedImage bulletImage) {
this.bulletImage = bulletImage;
}
public void shoot() {
System.out.println("Sprite#shoot() - Player Design Resolution X: " + this.getX() + " Y: " + this.getY());
System.out.println("Sprite#shoot() - Player Width: " + this.getWidth() + " Height: " + this.getHeight());
/**
* center the bullet according to the players design x and y
* co-ordinates, this is necessary as x and y should the design
* co-ordinates and render method will call getScreenX and
* getScreenY to calculate the current screen resolution
* co-ordinates
*
*/
Point2D spawnPoint = getCenterSpawnPoint(this.getWidth(), this.getHeight(), bulletImage.getWidth(), bulletImage.getHeight(), this.getX(), this.getY());
Bullet bullet = new Bullet((int) spawnPoint.getX(), (int) spawnPoint.getY(), this.bulletImage);
System.out.println("Sprite#shoot() - Bullet spawn point (always expressed in design resolution co-ordinates): X: " + spawnPoint.getX() + " Y: " + spawnPoint.getY());
System.out.println("Sprite#shoot() - Bullet spawn: X: " + bullet.getX() + " Y: " + bullet.getY());
System.out.println("Sprite#shoot() - Bullet spawn: Screen X: " + bullet.getScreenX() + " Screen Y: " + bullet.getScreenY());
System.out.println();
//bullet.LEFT = this.isFlippedX;
//bullet.RIGHT = !this.isFlippedX;
this.scene.add(bullet);
}
public void setScence(Scene scene) {
this.scene = scene;
}
}
public class Bullet extends Sprite {
public Bullet(int x, int y, BufferedImage image) {
super(image);
this.x = x;
this.y = y;
this.speed = 10;
}
}
}
Any help would be greatly appreciated!
UPDATE:
When using the solution by #akuzminykh all seems to work fine, however, now when I set the players position to something like player.setPosition(0,0), expecting it to appear in the top left corner, I get this instead:
which makes sense as I assume we are now positioning via the coordinate being at the center of the sprite, but how would I fix his so both setPosition for the top left corner and center would work, I think I might need to fix the getCenterSpawnPoint?
In your methods getScreenX and getScreenY you are ignoring that getX and getY include the width and height of the sprite. E.g. getX doesn't give you the center position of the sprite in the x-axis, but the position minus half of the sprite's width. When you scale this like you do in getScreenX, then you also scale the offset in x for the sprite. To solve this, simply add the offset initially, do the scaling and subtract the offset finally.
/**
*
* #return The current screen x co-ordindate of the sprite relative to
* the design resolution
*/
public int getScreenX() {
//return (int) (imageScaler.getWidthScaleFactor() * this.getX());
//return (int) ((double) this.getX() / DESIGN_SCREEN_SIZE.width * CURRENT_SCREEN_SIZE.width);
double halfWidth = this.getWidth() / 2.0;
double xCenterDesign = this.getX() + halfWidth;
double xCenterCurrent = xCenterDesign / DESIGN_SCREEN_SIZE.width * CURRENT_SCREEN_SIZE.width;
return (int) (xCenterCurrent - halfWidth);
}
/**
*
* #return The current screen y co-ordindate of the sprite relative to
* the design resolution
*/
public int getScreenY() {
//return (int) (imageScaler.getHeightScaleFactor() * this.getY());
//return (int) ((double) this.getY() / DESIGN_SCREEN_SIZE.height * CURRENT_SCREEN_SIZE.height);
double halfHeight = this.getHeight() / 2.0;
double yCenterDesign = this.getY() + halfHeight;
double yCenterCurrent = yCenterDesign / DESIGN_SCREEN_SIZE.height * CURRENT_SCREEN_SIZE.height;
return (int) (yCenterCurrent - halfHeight);
}
Or more mathematically:
If we take your example with 400x300 in "design" resolution, 800x600 being the "current" resolution and the sprite being 100x100 big: The position of the sprite is (150, 100), which makes sense: (400 / 2 - 100 / 2, 300 / 2 - 100 / 2). Now the formula you've used to bring it in "current" resolution (only for x because I'm lazy): 150 / 400 * 800 = 300. Hm, but half of 800 is 400 and the position should be 400 - 100 / 2? Exactly, the offset 100 / 2 for the sprite got scaled as well, from 50 to 100, which results in .. 400 - 100 = 300.
Therefore, add the offset back initially, so you scale the center. Then it's: (150 + 50) / 400 * 800 = 400. Don't forget to finally subtract the offset: 400 - 50 = 350. Now you have the correct position in the x-axis.
Re: UPDATE:
When you want to put the sprite in the top left corner, you might expect player.setPosition(0, 0) to do the trick. This is not the case. The way you've written it, the coordinates given by getX and getY include the width and height of the sprite, remember? Methods like getScreenX and getScreenY, with my fix, consider that and are used to render the sprite at the correct position. That means the coordinates (0, 0) describe the position of the center to be at (0 + 50, 0 + 50), where 50 is just 100 / 2, the width and height of the sprite divided by two.
To place the sprite in the top left corner, you need to consider the sprite's width and height when setting its position using the method setPosition: In our example, where the sprite is 100x100 big, you need to pass (0 - 100 / 2, 0 - 100 / 2), so the call looks like this: player.setPosition(-50, -50). You can of course make it dynamic by using playerImage.getWidth() and so on and so on.
Suggestion:
I suggest you to let x and y of Sprite to be relative to the center of the corresponding sprite. This will make some changes to the code necessary but it will also simplify other things and make them more intuitive. E.g. the problem with player.setPosition(0, 0) won't exist, it will actually put the sprite at the top left corner, exactly what you'd intuitively expect. This will also simplify getScreenX and getScreenY. Consider the offsets caused by the sprite's width and height just in the render method. This should be enough.
This question already has answers here:
Something seems wrong with the layout, JButton showing unexpected behaviour at resize of the window
(4 answers)
Closed 6 years ago.
I'm trying to make this Bouncing Ball program have the ball bounce around in the window, but no matter what I do, it bounces once and then just goes indefinitely out of the screen. What do I do to make it stay in the screen?
/*
* File: BouncingBall.java
* This program graphically simulates a bouncing ball.
*
*
*/
import java.awt.event.*;
import java.awt.Graphics;
import java.awt.Color;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.Timer;
public class BouncingBall
{
public static void main()
{
JFrame frame = new JFrame( "Bouncing Ball" );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
BallPanel bp = new BallPanel();
frame.add( bp );
frame.setSize( 600, 300 ); // set frame size
frame.setVisible( true ); // display frame
} // end main
}
// class BallPanel
class BallPanel extends JPanel implements ActionListener
{
private int delay = 15;
protected Timer timer;
private int x = 0; // x position
private int y = 0; // y position
private int diameter = 20; // ball diameter
private int velX = 2; // velocity offset for x
private int velY = 2; // velocity offset for y
public BallPanel()
{
timer = new Timer(delay, this);
timer.start(); // start the timer
}
public void actionPerformed(ActionEvent e) // This method runs when timer done
{
repaint(); // repaint panel to make ball in different place
}
public void paintComponent( Graphics g ) // determine ball position
{
super.paintComponent( g ); // call superclass's paintComponent
g.setColor(Color.black); // set ball to black
if (y > getHeight()) // make ball bounce off floor
{
velY = -velY;
}
x += velX; // add velocity offset for new position
y += velY; // add velocity offset for new position
g.fillOval(x, y, diameter, diameter);
}
}
You only detect that the ball has hit the floor. You need to also check if the ball has hit the ceiling;
if (y < 0) // make ball bounce off ceiling
{
velY = 2;
}
Similarly you will need to check for it hitting the left and right sides...
You only check the Y-Coordinate and only that if is below 0.
if(y <= 0 || y >= 300 || x <= 0 || x >= 600)
Replace that with your if-statement and it should work.
I'm trying to make a hex board with hex images (720x835 GIF) on a scroll-able JPanel. I've overridden the paintComponent method to draw the tiles at different specific locations and used a timer to call repaint at each tick.
When repaint() is called, doDrawing is called. When doDrawing is called, choseTile is called to draw the tiles with drawImage.
For some reason, the tiles are not being drawn and I'm left with an empty black panel. Why are my images not being drawn? Is it because the images are too large? The panel is too large?
public class MapPanel extends JPanel {
// Images for the tiles
Image tile1;
Image tile2;
//etc
// measurements for the tiles
int tileX = 720;
int tileY = 835;
int dimensionX = 14760;
int dimensionY = 14613;
//Use this to keep track of which tiles goes where on a 20x20 board
public int[][] hexer;
/**
* Create the panel.
*/
public MapPanel(int[][] hexMap) {
hexer = hexMap;
setPreferredSize(new Dimension(dimensionX, dimensionY));
setBackground(Color.black);
setFocusable(true);
loadImages();
Timer timer = new Timer(140, animatorTask);
timer.start();
}
//getting the images for the tiles
private void loadImages() {
// Setting the images for the tiles
ImageIcon iid1 = new ImageIcon("/Images/tiles/tile1.gif");
tile1 = iid1.getImage();
ImageIcon iid2 = new ImageIcon("/Images/tiles/tile2.gif");
tile2 = iid2.getImage();
//etc
}
// Drawing tiles
private void choseTile(Graphics g, int x, int y, int id) {
switch (id) {
case 1:
g.drawImage(tile1, x, y, this);
break;
case 2:
g.drawImage(tile2, x, y, this);
break;
//etc
}
}
// repainting stuff
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
private void doDrawing(Graphics g) {
int actualX;
int actualY;
//set the painting coordinates and image ID then call the method to draw
for (int x = 0; x < 20; x++) {
for (int y = 0; y < 20; y++) {
if ((y + 1) % 2 == 0) {
actualX = x * tileX + 720;
} else {
actualX = x * tileX + 360;
}
if((x + 1) % 2 == 0){
actualY = (y/2) * 1253 + 418;
}else{
actualY = (y+1)/2 * 1253 + 1044;
}
if(hexer[x][y] != 0)
choseTile(g, actualX, actualY, hexer[x][y]);
}
}
}
private ActionListener animatorTask = new ActionListener() {
public void actionPerformed(ActionEvent e) {
repaint();
}
};
}
Edit: I've already checked to make sure the images aren't null.
Following Andrew Thompson's suggestion; I used ImageIO. I was able to figure out that the way I was accessing the image files was faulty thanks to thrown errors by ImageIO.
im trying to put restrictions on movement if my chicken reaches the corner here is my code for my move method
public void move1(){
if(((x<=350) && (y>=350)) || ((x>=450) && (y>=350))){
if(dy<0){
dy=0;
}
}
if(((x>=350) && (y<=350)) || ((x<=450) && (y<=350))){
if(dx<0 || dx>0){
dx=0;
}
}
if(((y<=250) && (x<=350)) || ((y<=250) && (x>=450))){
if(dy>0){
dy=0;
}
}
if (x >= 750){
x = 750;
}
if (y >= 575){
y = 575;
}
x += dx;y += dy;
}
i used a key adapter for the movement of my chicken but its from a different class
public void keyPressed(KeyEvent e) {
chick.keyPressed(e);
}
the animation is perfectly fine but my chicken wont respond the keyevent
I would strongly recommend you to have a look at KeyBindings, they are more accustomed to such situations, which are more concerned about focus related issues. This post regarding Motion Using the Keyboard, might will surely interest you, on the topic concern :-)
EDIT :
Here a small program from help :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class KeyBindingExample
{
private DrawingBoard contentPane;
/*
* There variables will simply
* decide how much the square
* will move with click key press,
* in this case I have set this to
* 1 (inside the constructor).
* brakes will simply tell whether
* the square will move or not in
* a given direction.
*/
private int speed;
private int brakes;
public KeyBindingExample() {
speed = 5;
brakes = 0;
}
private void displayGUI()
{
JFrame frame = new JFrame("Swing Worker Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
contentPane = new DrawingBoard(10, 10, Color.BLUE.darker());
addBindingsToBoard();
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private void addBindingsToBoard() {
/*
* Since, when UP Arrow is pressed, that means, the square
* can move vertically upwards, hence, the square will move
* along Y-Axis that too in the negative direction of the
* same, though along X-Axis the square will move nowhere,
* hence, we passing 0 and -1, since we want to add the
* current location (say square is at present at 50, 50),
* now after UP Arrow key event, square will be at (50, 49);
*/
putBindingsFor(contentPane, KeyStroke.getKeyStroke("UP"),
"UP Arrow Key", brakes, -speed);
/*
* When RIGHT Arrow is pressed, the square is suppose to
* move horizontally, along the X-Axis, in the positive
* direction towards the RIGHT. Hence +1 change along X-Axis
* and no change along Y-Axis, i.e. from (50, 49), the square
* will now move to (51, 49), that's why we passing (+1, 0)
*/
putBindingsFor(contentPane, KeyStroke.getKeyStroke("RIGHT"),
"RIGHT Arrow Key", speed, brakes);
/*
* When DOWN Arrow is pressed, the square is suppose to
* move vertically, along the Y-Axis, in the positive
* direction towards the BOTTOM. Hence no change along X-Axis
* and +1 change along Y-Axis, i.e. from (51, 49), the square
* will now move to (51, 50), that's why we passing (0, +1)
*/
putBindingsFor(contentPane, KeyStroke.getKeyStroke("DOWN"),
"DOWN Arrow Key", brakes, +speed);
/*
* When LEFT Arrow is pressed, the square is suppose to
* move horizontally, along the X-Axis, in the negative
* direction towards the LEFT side. Hence -1 change along X-Axis
* and no change along Y-Axis, i.e. from (51, 50), the square
* will now move to (50, 50), that's why we passing (-1, 0).
* The square will atlast come to it's initial position.
*/
putBindingsFor(contentPane, KeyStroke.getKeyStroke("LEFT"),
"LEFT Arrow Key", -speed, brakes);
}
private void putBindingsFor(JComponent comp,
KeyStroke keyStroke, String value, final int moveX, final int moveY) {
comp.getInputMap().put(keyStroke, value);
comp.getActionMap().put(value, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
contentPane.setValues(moveX, moveY);
}
});
}
public static void main(String[] args)
{
Runnable runnable = new Runnable()
{
#Override
public void run()
{
new KeyBindingExample().displayGUI();
}
};
EventQueue.invokeLater(runnable);
}
}
class DrawingBoard extends JPanel {
private int x;
private int y;
private int width;
private int height;
private Color rectColor;
public DrawingBoard(int x, int y, Color rColor) {
setOpaque(true);
this.x = x;
this.y = y;
rectColor = rColor;
width = height = 10;
}
public void setValues(int deltaX, int deltaY) {
System.out.format("Firstly X : %d\tY : %d%n", x, y);
repaint(x, y, width, height);
/*
* Whatever values are passed from above, i.e.
* say on Left ARROW, we passing (-1, 0),
* therefore deltaX will be -1 and deltaY will
* be 0. Now whatever the current value of X is
* we are simply adding deltaX to that value
* and the same goes for deltaY as well.
* Now since the value for x and y is updated
* after these two statements below, we checking
* that whether these two updated values lies
* within the bounds of our board or not.
* If they are, then we simply calling repaint,
* to draw the square at this new location, else
* we simply bring back the previous values of
* x and y to their previous state.
*/
x += deltaX;
y += deltaY;
if ((x + width) <= getWidth() && (y + height) <= getHeight()
&& x >= 0 && y >= 0) {
System.out.format("Later X : %d\tY : %d%n", x, y);
repaint(x, y, width, height);
}
else {
x -= deltaX;
y -= deltaY;
System.out.format("Later X : %d\tY : %d%n", x, y);
repaint(x, y, width, height);
}
}
/*
* Make this a customary habbit of overridding
* this method whenever you have to override
* any JComponent, instead of calling
* setPreferredSize(...).
*/
#Override
public Dimension getPreferredSize() {
return (new Dimension(100, 100));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(rectColor);
g.fillRect(x, y, width, height);
}
}
I have two rectangles, r1 which is moving, and r2 which is a stationary tile. I need to know what side of r2 (or r1) is hit to determine which direction r1 should go. The collision detection works with the shape.intersects(otherShape), unfortunately this means that r1 will "Sink" a few pixels inside of r2. This is very problematic when dealing with the corners of the rectangles since this will cause two sides of each rectangle to intersect, thus making figuring out what side was hit by checking what side intersects which to be ineffective.
What we know:
Positions and Center positions of r1 and r2
Velocities and speeds of r1 and r2
So given that information, i need an algorithm that is able to get the side of collision.
A square is defined by four corners, the upper-left, upper-right, lower-left, lower-right. If your updates/collisiondetections are fast enough then the following will work. This takes into consideration the event that only one corner collides (if your rectangle rotates or moves in 2D).
import java.awt.Color;
import java.awt.Graphics;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class TestMovingRect extends JComponent {
JFrame frame;
Rectangle2D r1;
Rectangle2D r2;
int speedX, speedY;
int width = 20;
int height = 20;
public static void main(String[] args) {
new TestMovingRect();
}
public TestMovingRect() {
r1 = new Rectangle2D.Double(0, 0, width, height);
r2 = new Rectangle2D.Double(200, 0, width, height);
speedX = 1;
frame = new JFrame();
frame.setSize(500, 500);
frame.add(this);
frame.setVisible(true);
loop();
}
public void tick(){
System.out.println("Old r1: " + r1.getX() + "," + r1.getY());
double x = r1.getX();
double y = r1.getY();
r1.setRect(x + speedX, y + speedY, 10, 10);
System.out.println("New r1: " + r1.getX() + "," + r1.getY());
}
private void loop() {
while (true){
tick();
sleep();
checkIntersect();
frame.validate();
frame.repaint();
}
}
private void checkIntersect() {
Point2D upperLeft = new Point2D.Double(r1.getX(), r1.getY());
Point2D upperRight = new Point2D.Double(r1.getX() + r1.getWidth(),
r1.getY());
Point2D lowerLeft = new Point2D.Double(r1.getX(), r1.getY()
+ r1.getHeight());
Point2D lowerRight = new Point2D.Double(r1.getX() + r1.getWidth(),
r1.getY() + r1.getHeight());
if (r2.contains(upperRight)){
System.out.println("UpperRight hit");
//Do stuff
}
if (r2.contains(lowerRight)) {
System.out.println("lowerRight hit");
// Do stuff
}
if (r2.contains(lowerLeft)) {
System.out.println("LowerLeft hit");
// Do stuff
}
if (r2.contains(upperLeft)) {
System.out.println("UpperLeft hit");
// Do stuff
}
}
private void sleep() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void paintComponent(Graphics g) {
// TODO Auto-generated method stub
super.paintComponents(g);
g.setColor(Color.blue);
g.fillRect((int) r1.getX(), (int) r1.getY(), (int) r1.getWidth(),
(int) r1.getHeight());
g.setColor(Color.red);
g.fillRect((int) r2.getX(), (int) r2.getY(), (int) r2.getWidth(),
(int) r2.getHeight());
}
}
Another (far simpler) way is to interpolate the velocity of the rectangle. If it is moving to the right and it intersects then you know it must have hit the right side. But then if your rectangle is rotating or moving in 2D you will have added complexity and the above code would do just as fine.
Edit: To your comment
if (r2.contains(upperRight)){
if (speedX * speedX > speedY * speedY) {
System.out.println("I hit on the right side");
}
else if (speedY * speedY > speedX * speedX) {
System.out.println("I hit on the topside");
}
else if (speedX * speedX == speedY * speedY) {
System.out
.println("I hit on a 45 degree diagonal, side is ambigious, please decide for me");
}
}