I'm making simple game in Java and I'm using Swing. I have JFrame and inside it I want to have two JPanels - one for score and so on and second below, for actual game. I read that every JPanel has its own coordinates, so point (0, 0) is on the upper-left corner of that panel.
I override method paintComponent() im my class GameView which displays the game (so it's the second JPanel from these I mentioned). But when I want to draw something in upper-left corner of gameView and set coordinates of that image to (0,0) it draws on BarView.
I read many tutorials and posts about drawing and I don't see what am I doing wrong. So my question is, how to draw something using JPanel coordinates, not JFrame ones? Here's some code:
Adding objects extending JPanel to JFrame:
GameView v = new GameView();
BarView bv = new BarView();
frame.getContentPane().add(bv);
frame.getContentPane().add(v);
frame.setVisible(true);
v.requestFocus();
v.repaint();
bv.repaint();
Drawing in JPanel:
public class GameView extends JPanel implements View, Commons{
public static final int WIDTH=WINDOW_WIDTH, HEIGHT=ARENA_HEIGHT;
private GameScene gameScene;
private TexturePaint paint;
private BufferedImage bi;
public GameView(){
addKeyListener(new CustomKeyListener());
addMouseMotionListener(new CustomMouseListener());
setSize(WINDOW_WIDTH, ARENA_HEIGHT);
setFocusable(true);
try {
bi = ImageIO.read(new File("src/res/texture.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.paint = new TexturePaint(bi, new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));
}
#Override
public void paintComponent(Graphics g1) {
Graphics2D g = (Graphics2D) g1;
g.setPaint(paint);
g.fillRect(0, 0, WINDOW_WIDTH, ARENA_HEIGHT);
for(Iterator<Drawable> it = gameScene.models.iterator(); it.hasNext();)
{
Drawable d = it.next();
d.draw(g1);
}
Toolkit.getDefaultToolkit().sync();
}
and method draw of model in gameScene usually looks like this:
public void draw(Graphics g1){
Graphics2D g = (Graphics2D) g1.create();
int cx = image.getWidth(null) / 2;
int cy = image.getHeight(null) / 2;
g.rotate(rotation, cx+x, cy+y);
g.drawImage(image, x, y, null);
}
It looks like you haven't specifed a LayoutManager for your frame, so it will default to BorderLayout.
When you subsequently call frame.getContentPane().add(component) without passing in a position constant, the position BorderLayout.CENTER will be defaulted.
The result is that your GameView and BarView components will be rendered on top of each other.
As a quick test, try specifying the component position as follows:
GameView v = new GameView();
BarView bv = new BarView();
frame.getContentPane().add(bv, BorderLayout.LINE_START);
frame.getContentPane().add(v, BorderLayout.LINE_END);
Unless your UI is really simple, you'll probably find that you need to use some other layout manager. Refer to 'How to Use Various Layout Managers' for more on this subject.
Related
I learned that if you override
protected void paintComponent(Graphics g)
You can get an image painted as the background of a class that extends javax.swing.JPanel.
In my code I have 2 instances of the same Class extending JPanel with almost exactly the same code just with a different position and background image in a second JPanel and while one gets the background the other one does not. Here is my code:
public class CardPanel extends JPanel {
private int x, y, width, height;
private BufferedImage background;
public CardPanel(int x, int y, int width, int height, BufferedImage background) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.background = background;
createCardPanel();
}
private void createCardPanel() {
setPreferredSize(new Dimension(width, height));
setMaximumSize(new Dimension(width, height));
setMinimumSize(new Dimension(width, height));
setFocusable(false);
setOpaque(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(background, x, y, null);
}
}
And how I use it:
pCardPanel = new CardPanel();
dCardPanel = new CardPanel();
Declaring the CardPanels
private void createCardPanels(String imgPath) {
BufferedImage background = ImageLoader.loadImage(imgPath);
pCardPanel = new CardPanel(0, (height - Card.CARD_HEIGHT), width, Card.CARD_HEIGHT, background.getSubimage(0, (height - Card.CARD_HEIGHT), width, Card.CARD_HEIGHT));
dCardPanel = new CardPanel(0, 0, width, Card.CARD_HEIGHT, background.getSubimage(0, 0, width, Card.CARD_HEIGHT));
this.add(pCardPanel, BorderLayout.SOUTH);
this.add(dCardPanel, BorderLayout.NORTH);
}
Method for creating and adding the CardPanels
createCardPanels("/textures/background.png");
Using the method
public void addCardImage(BufferedImage img, boolean playerCard) {
JLabel imgLabel = new JLabel();
ImageIcon icon;
icon = new ImageIcon(img);
imgLabel.setIcon(icon);
cardImages.add(imgLabel);
if (playerCard)
pCardPanel.add(imgLabel);
else
dCardPanel.add(imgLabel);
display.pack();
}
This last method is called for adding Card Images to te panel, this part works. Now to my problem:
this is how it looks. (there are some other flaws like the card position but this will be a later issue I can fix myself)
As you can see, the panel on the bottom (pCardPanel) has no background image. Any ideas why it might be this way? Thanks in advance
You can get an image painted as the background of a class that extends javax.swing.JPanel
A background generally implies that the image fills the entire panel and the size of the panel is the same as the size of the image. Therefore when you paint the image the code should be:
g.drawImage(background, 0, 0, null);
So the image is always painted at the top left of the panel.
When the panel is added to the frame, the layout manager will set the location of the panel.
just with a different position
pCardPanel = new CardPanel(0, (height - Card.CARD_HEIGHT),
I would guess the problem is the "y" value is outside the size of the panel, so you don't see the image.
That is your preferred size does not account for the fact that you are attempting to paint the image at some location other than (0, 0) in which case the preferred size should be something like:
setPreferredSize(new Dimension(x + width, y + height));
However, you don't want to do that, since each component should be independent of other components. It should not know or care that you are trying to position two panels above/below one another. It should just worry about painting its own image and let the layout manager worry about setting the location of each panel.
So what you really want to do is just paint the image at (0, 0) and let the layout manager determine the location of the panel.
You are already using the BorderLayout. So it is the job of the layout manager to set the location of the component in the "SOUTH" to some non-zero "y" value.
Currently I am trying to draw a line and a circle (which will become animated, like a wheel) onto my canvas.
I have a constructor called WheelAnimation().
Within this constructor, I have these two implementation classes, the first one is the circle:
class CircleComponent extends JComponent
{
private static final long serialVersionUID = 1L;
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
for(int i = 0; i < 200; i++)
{
// repaint();
g2.fillOval(i, 50, 50, 50);
}
}
}
final CircleComponent component2 = new CircleComponent();
panel.add(component2);
And this draws the line under the circle:
class LineComponent extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.drawLine(120, 120, 380, 120);
}
}
final LineComponent component = new LineComponent();
panel.add(component);
If I use a setLayout method such as: panel.setLayout(new GridLayout(1, 1));
I can make the two items show up (though their formatting is not good).
http://puu.sh/8fm9B/4f1dc1d0e5.png
But if I remove the setLayout method, nothing shows up onto my frame, despite the coordinates staying the same.
Could someone tell me why this is happening and give me a recommendation on how to set those layouts and make them show up?
I can make the two items show up (though their formatting is not good).
When you use a layout manager it is responsible for setting the size and location of the component added to the panel.
If you don't use a layout manager then your application code is responsible for setting the size and location of each component.
I am creating a game inside a JFrame (854 x 480). I am trying to draw a Rectangle in the upper right hand corner of the screen. like so:
int x, y;
Rectangle rect;
public Foo() {
x = 0;
y = 0;
rect = new Rectangle(x, y, 63, 27);
}
....
public void draw(Graphics g) {
g.drawRect(rect.x, rect.y, rect.width, rect.height);
}
But when I do this, the box gets drawn off the screen (x co-ords are right, but y co-ords too high :
When I change the y co-ords to 27 (the height of the rectangle), it moves down to where I want it to go:
Any idea why this is happening? Or how to fix it?
Do you override the paint(..) method of your JFrame? The coordinates seem to be in the coordinate space of the window/JFrame, where 0/0 includes the non-client area (the close box, title bar and so on).
You should create a separate component and add this to the content pane of your main frame - just a very tiny example - note that I am using paintComponent(..):
public static class MyPanel extends JPanel {
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLUE);
g2.draw(new Rectangle2D.Float(8,8, 128, 64));
}
}
Add to JFrame content pane (use default or custom LayoutManager):
public class MyFrame extends JFrame {
public MyFrame() {
...
// since JDK 1.4 you do not need to use JFrame.getContentPane().add(..)
this.add(new MyPanel());
}
}
This should do the trick. Here's the corresponding section of the Java SE tutorial.
This is because the JFrames co-ordinates are starting at the top left corner including the title bar. You need to add the height of the title bar to your y co-ordinate to make it show in the top left corner.
Draw the Rect in a JPanel.
JPanel panel = new JPanel();
this.add(panel) //Add the panel to the frame and draw from there
//Provided the class extends a JFrame
my goal is to draw some bufferedimage onto another. then all this stuff draw onto some other bufferedimage and so on. And finally draw this on top of a panel.
For now i'm trying to draw bufferedimage onto panel and nothing works. My bufferedimage looks completely white:
public class Main2 {
public static void main(String[] args) {
JFrame frame = new JFrame("asdf");
final JPanel panel = (JPanel) frame.getContentPane();
frame.setSize(500,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
somepaint(panel);
}
});
}
private static void somepaint(JPanel panel) {
BufferedImage image = new BufferedImage(200,200,BufferedImage.TYPE_INT_ARGB);
image.getGraphics().setColor(Color.red);
image.getGraphics().fillRect(0, 0, 200, 200);
Graphics2D graphics = (Graphics2D) panel.getGraphics();
graphics.setColor(Color.magenta);
graphics.fillRect(0, 0, 500, 500);
graphics.drawImage(image, null, 0, 0); // draws white square instead of red one
}
}
thanks
Re:
private static void somepaint(JPanel panel) {
BufferedImage image = new BufferedImage(200,200,BufferedImage.TYPE_INT_ARGB);
image.getGraphics().setColor(Color.red);
image.getGraphics().fillRect(0, 0, 200, 200);
Graphics2D graphics = (Graphics2D) panel.getGraphics();
This is not how you draw inside of a JPanel or JComponent.
Don't call getGraphics() on a component as the Graphics object returned will be short-lived, and anything drawn with it will not persist. Instead do your JPanel's drawing inside of its paintComponent(Graphics G) method override. You will need to create a class that extends JPanel in order to override paintComponent(...).
Most importantly, to see how to do Swing graphics correctly, don't guess. You'll want to read the Swing Graphics Tutorials first as it will require you to toss out some incorrect assumptions (I know that this is what I had to do to get it right).
You need to rectify your parameters in the drawImage() call. Change this:
graphics.drawImage(image, null, 0, 0);
to
graphics.drawImage(image, 0, 0,null);
Check the Java docs for more details.
I have been reading a lot about Double Buffering as I am working on a 2D game. I have come across many different strategies for implementation, but am unsure how Double Buffering would fit into the way I have created my game window. For example, one article I came across (http://content.gpwiki.org/index.php/Java:Tutorials:Double_Buffering) suggested having a separate method for drawing; however, I suspect this would be applicable if you were drawing shapes, instead of adding components to the window.
Here is my main GUI code (keylistener methods omitted)
public class MainWindow extends JFrame implements KeyListener{
private Dimension dim;
private CardLayout layout;
private JPanel panel;
private JLayeredPane gameLayers;
private Menu menu;
private MiniGame miniGame;
private Board board;
private Sprite sprite;
private Game game;
private Map map;
private GameState gs;
private Boolean[] keys;
public MainWindow(Game game, GameState gs, Map map, Sprite sprite){
//Call superclass.
super();
addKeyListener(this);
//Sore references to critical game components.
this.game = game;//So we can call methods when an event occurs.
this.gs = gs;
this.map = map;//Used to construct the board.
//The board needs to know the layout of the map.
this.sprite = sprite;//Used to add the sprite to one of the layers.
//Instantiate objects.
dim = new Dimension(800, 600);
layout = new CardLayout();
panel = new JPanel(layout);
menu = new Menu();
miniGame = new MiniGame();
board = new Board(map);
gameLayers = new JLayeredPane();
//Remove decoration and place window in center of screen.
setUndecorated(true);
Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
setBounds((screenDim.width /2)-(dim.width/2),
(screenDim.height/2)-(dim.height/2),
dim.width,
dim.height);
//Add the board to a layer.
gameLayers.add(board, JLayeredPane.DEFAULT_LAYER);
board.setBounds(0, 0, dim.width, dim.height);
board.setBoard();
//Add the sprite to a layer.
gameLayers.add(sprite, JLayeredPane.PALETTE_LAYER);
sprite.setBounds(0, 0, 50, 50);
//Add components to window.
panel.add(gameLayers, "gameLayers");
panel.add(miniGame, "miniGame");
panel.add(menu, "menu");
//Add the "cards" to the window.
add(panel);
//JFrame housekeeping.
pack();
setSize(dim);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
setVisible(true);
//Holds state when an event is triggered.
//Ugly hack to circumvent delay issue.
keys = new Boolean[4];
for(int i=0; i<keys.length; i++){
keys[i] = false;
}
}
}
How would you recommend I approach this? Bearing in mind that the Board is a JPanel consisting of a grid of scaled images, and the sprite will be a JComponent displaying a scaled image.
Regards,
Jack.
Override the JPanel's paintComponent() Method and paint the content into a BufferedImage image first. Once done, copy the content of the BufferedImage into the graphics context you get from paintComponent().
protected void paintComponent(Graphics g)
{
BufferedImage bufferedImage = new BufferedImage(500, 500, BufferedImage.TYPE_ARGB);
Graphics2D g2d = bufferedImage.createGraphics();
//paint using g2d ...
Graphics2D g2dComponent = (Graphics2D) g;
g2dComponent.drawImage(bufferedImage, null, 0, 0);
}
As the Java Graphics Library is not fast, either well functional, you should inform yourself about the 'Lightweight Java Game Library' ( http://lwjgl.org/ ). It uses OpenGL, a strong Graphics Library, using native code.
Lwjgl is also used for the game Minecraft.
Also, OpenGL is used for different languages, so you could learn multi-functional and high-level programming.