Why do the pixels appear to be offset in my JFrame? [duplicate] - java

I'm creating a simple Break Out style game. The main game extends JFrame and I'm adding a JPanel to the frame.
When I was using paint() to draw the game graphics the items sat within the window as expected (i.e by their x, y coordinates).
I've updated the code to use BufferStrategy as I was getting flickering. Since the, the graphics that are rendered are offset by 22px.
This means the Bricks are off the top of the screen!
The code is as follows:
package BreakOut;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
public class Game extends JPanel implements KeyListener{
GameStateManager gsm = new GameStateManager();
BufferStrategy strategy;
public Game() {
//add menu state to GameStateManager
gsm.add(new MenuState(gsm));
createFrame();
while(true)
{
gsm.update();
//repaint();
render();
try{
Thread.sleep(10);
}
catch(InterruptedException e)
{
}
}
}
public void createFrame()
{
JFrame frame = new JFrame("Mini Tennis");
frame.setLayout(new BorderLayout());
this.setPreferredSize(new Dimension(400,400));
frame.add(this);
frame.pack();
frame.setBackground(Color.BLACK);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addKeyListener(this);
this.setFocusable(true);
frame.createBufferStrategy(2);
strategy = frame.getBufferStrategy();
frame.setVisible(true);
System.out.println(frame.getInsets());
}
public void render()
{
Graphics g = strategy.getDrawGraphics();
super.paint(g);
gsm.render(g);
g.dispose();
strategy.show();
}
public void keyPressed(KeyEvent k) {
switch(gsm.getState())
{
case MAIN_MENU:
if(k.getKeyCode()==KeyEvent.VK_ENTER)
{
//add the PlayState to the Stack and update enum value
gsm.add(new PlayState(gsm, this));
}
break;
case PLAYING:
if(k.getKeyCode()==KeyEvent.VK_P)
{
//add the PlayState to the Stack and update enum value
gsm.add(new PauseState(gsm));
}
break;
case PAUSE:
if(k.getKeyCode()==KeyEvent.VK_P)
{
gsm.pop();
}
break;
case GAME_OVER:
if(k.getKeyCode()==KeyEvent.VK_ENTER)
{
gsm.pop();
}
break;
}
//send input to GameStateManager
gsm.keyPressed(k.getKeyCode());
}
public void keyReleased(KeyEvent k) {
gsm.keyReleased(k.getKeyCode());
}
public void keyTyped(KeyEvent k) {
gsm.keyTyped(k.getKeyCode());
}
public static void main(String[] args) {
new Game();
}
}
When I output System.out.println(frame.getInsets()); I get
java.awt.Insets[top=22,left=0,bottom=0,right=0]
I'm obviously doing something wrong but can't figure out why adding BufferStrategy would create the JPanel to be offset by 22px
Any help would be appreciated :)

Frames have borders and decorations, which is included within the bounds of the frame (they don't get added to the outside), from the looks of things you're using MacOS and the 22pixels to the top is the window title.
Best solution is, don't use the frame as the render surface, instead, use the Game class. This will mean it will need to extend from java.awt.Canvas instead of javax.swing.JPanel and you will need to create the BufferStrategy from it
If you override the Canvas's getPreferredSize method, you can use pack on the frame it will pack the window around this, so the physical frame will be larger then the content, but the content will be the size you would prefer
You will also want to move you main/game loop to separate thread, as this is current in the risk of blocking the Event Dispatching Thread, which could cause you no end of issues (like never getting a key event)

Related

Can't get paint function to update in simple pong game

I'm a relatively inexperienced Java programmer when it comes to graphics. I'm trying to make a simple pong game to learn more about how graphics are done in Java.
In the program, the left paddle (the only paddle thus far implemented) should move up 5 pixels when 'W' is pressed on the keyboard. According to the console, it is recognizing that the key is being pressed, and it is updating a variable that represents the y-coordinate for the left paddle accordingly. However, the paddle's location isn't actually being updated on the screen.
Help?
Game class:
import javax.swing.JFrame;
public class Game {
static int WIDTH = 500;
static int HEIGHT = 500;
public static void main(String[] args) {
window();
}
public static void window() {
JFrame frame = new JFrame();
frame.setSize(WIDTH, HEIGHT);
frame.setLocationRelativeTo(null);
frame.setAlwaysOnTop(true);
frame.setVisible(true);
frame.setResizable(false);
frame.add(new Panel());
frame.addKeyListener(new Panel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Panel class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
public class Panel extends JPanel implements KeyListener {
int leftPaddlePos = 100;
public void paint(Graphics g) {
g.clearRect(0, 0, Game.WIDTH, Game.HEIGHT);
g.setColor(Color.black);
g.fillRect(0, 0, Game.WIDTH, Game.HEIGHT);
g.setColor(Color.white);
g.fillRect(75, leftPaddlePos, 15, 100);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_W) {
System.out.println("W pressed");
leftPaddlePos = leftPaddlePos + 5;
System.out.println(leftPaddlePos);
}
}
#Override
public void keyReleased(KeyEvent e) {
}
}
public class Panel extends JPanel implements KeyListener {
Don't call your class Panel. There is an AWT class with that name so it is confusing. Your class name should be more descriptive. Maybe something like PongPanel.
public void paint(Graphics g) {
Don't override paint(...);
public void paintComponent(Graphics g) {
Custom painting in Swing is done by overriding paintComponent(...).
g.clearRect(0, 0, Game.WIDTH, Game.HEIGHT);
g.setColor(Color.black);
g.fillRect(0, 0, Game.WIDTH, Game.HEIGHT);
Don't use above code to paint the background of the panel. Instead, you just use:
super.paintComponent(g);
So in the constructor of your class you can invoke setBackground(...) to set the desired background color.
However, the paddle's location isn't actually being updated on the screen.
leftPaddlePos = leftPaddlePos + 5;
You update the position, but you haven't told the component that a property of the class has changed, so it doesn't know it needs to paint itself again.
You should not change the property of the class in the listener. Instead you should have a method like adjustLeftPaddlePosition(int value)
The method would be:
public void adjustLeftPaddlePostion(int value)
{
leftPaddlePos = leftPaddlePos + value;
repaint();
}
Then in the KeyListener you would use:
adjustLeftPaddlePosition(5);
Also, you should not be using a KeyListener. Instead you should be using "Key Bindings" Check out Motion Using the Keyboard for more information and working examples.

if-statement inside of paintComponent makes it not paint

I'm trying to write a solitaire game.
I would like to call repaint() when the game starts and paint the full deck
once only the first time repaint is called, but when I add the if-statement it
no longer paints.
Here is the code with the if-statement:
private void paintInitialDeck(Graphics g, Card card){
card.paintCard(g);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (initialDrawing) {
Card card;
for (int i = 0; i < deck.size(); i++) {
card = deck.get(i);
card.setY((int) (50 + 0.2 * i));
card.setX((int) (250 + 0.2 * i));
paintInitialDeck(g, card);
}
initialDrawing = false;
}
}
It doesn't work, but if I remove the if statement and the initialDrawing = false
then it works.
Problem is, I need the if-statement.
Anyone can help me understand this better?
ps.: initialDrawing is set to true to begin with. Also, it's really the initialDrawing = false
that makes the whole thing not paint.
What's happening is that with initial instantiation of that class with the paintComponent method, the paintComponent method is called once. So the paint will occur once, but after that, not again because the initiaDrawing is set to false after the first call to paintComponent in the initial instantiation. So a call to repaint() within the program would not work, as initialDrawing will be false after the first start up paint process.
I'm not sure if you see the paint occurring when you first start the program, but this is why, it won't allow you to paint again after the program has started.
A possible fix might be to initialize initialDrawing to false so it doesn't initially paint. Then whatever component calls the repaint() method, in the action, first set the inititialDrawing to true, then call repaint(). Disable that component so another call to repaint() cannot be made. (or something of this logical nature).
Here's an example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class DisablePaint extends JPanel {
ImagePanel panel;
JButton button;
boolean intialDraw = false;
public DisablePaint() {
button = new JButton("Paint Once");
setLayout(new BorderLayout());
panel = new ImagePanel();
add(panel, BorderLayout.CENTER);
add(button, BorderLayout.SOUTH);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
intialDraw = true;
panel.repaint();
button.setEnabled(false);
}
});
}
private class ImagePanel extends JPanel {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (intialDraw) {
g.setColor(Color.BLUE);
g.fillRect(50, 50, 200, 200);
initialDraw = false;
}
}
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame();
frame.add(new DisablePaint());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
You can see that initialDraw is first set to false, so it won't draw on initial start of program. I then make a call to repaint() in the actionPerformed, but first setting the intialDrawing to true. Then disable the button, not allowing a another initial painting
With the if statement your cards will only ever be drawn on the first call to paintComponent(). After that, the method will only invoke super.paintComponent() which erases any content that had previously been drawn on the component.
Since you don't have a lot of control over when paintComponent() is invoked it might be best to avoid including any behaviour related to it in your application's logic as you've done with the if-statement.
Here's an arbitrary block of code. Try compiling this example and see how many times "Repainted" is printed.
import javax.swing.*;
import java.awt.*;
public class Test {
public static void main(String args[]) {
JFrame frame = new JFrame();
frame.add(new JPanel() {
public void paintComponent(Graphics g)
{
super.paintComponents(g);
System.out.println("Repainted");
}
});
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(200, 200);
frame.setVisible(true);
}
}
The straight-forward solution would be
if (initialDrawing) {
Card card;
for (int i = 0; i < deck.size(); i++) {
card = deck.get(i);
card.setY((int) (50 + 0.2 * i));
card.setX((int) (250 + 0.2 * i));
}
initialDrawing = false;
}
for (Card card : deck) {
card.paintCard(g);
}
This however suggests, that the initialisation should be done elsewhere, maybe in the constructor. In general painting code should do just painting and may be called several times.

Making an image show up in Java

I've been struggling to get an image to show up for some time now. Ive read a few different things, and all of them seem to have different ways of showing images. Can someone tell me what I'm doing wrong? I'm trying to make a program that uses 2 classes to make a picture show up in a frame. I guess what I don't understand still is what a Graphics object is, what a Graphics2D object is and how its different, and what method from what class do I call in order to make an image show up. Here is my code:
public class Smiley {
private BufferedImage smileyFace;
private Graphics2D renderWindow;
private Dimension smileyPosition;
private File smileyFile;
public Smiley() {
try{
smileyFile = new File("C:\\Users\\MyName\\Desktop\\smiley.png");
smileyFace = ImageIO.read(smileyFile);
}
catch (Exception e){
System.out.println("There was an error finding or reading the file \" smiley.png.\"");
}
MainScreen.graphicPane.drawImage(smileyFace,50,50, null);
}
and the second class:
public class MainScreen extends JFrame{
public static MainScreen ms;
public static Graphics2D graphicPane;
public static void main (String[] args){
MainScreen ms = new MainScreen();
Smiley newSmiley = new Smiley();
}
public MainScreen(){
super("Main Screen Window");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
this.setSize(500,800);
this.getContentPane().setBackground(Color.black);
graphicPane = (Graphics2D) this.getContentPane().getGraphics();
}
}
the program compiles with no errors and nothing is reported back to me about not finding the file.
Your going to need some sore of paint method. For that you will require a Component to paint on. You need to learn a GUI framework, like Swing. There are clear compoents you can paint on like a JPanel. With that panel you need to override its paintComponent method.
The Graphcics object is what the component uses to actually paint the graphic onto the component.
The Graphics2D object just extends the capabilities of the Graphics object.
You should take a look at the Swing tuorial and the **Graphics toturial
To get your program running though you would do something like this
public class DrawPanel extends JPanel {
BufferedImage smileyFace;
public DrawPanel() {
try{
smileyFile = new File("C:\\Users\\MyName\\Desktop\\smiley.png");
smileyFace = ImageIO.read(smileyFile);
}
catch (Exception e){
System.out.println("There was an error finding or reading the file \" smiley.png.\"");
}
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(smileyFace,50,50, this);
}
#Override
public Dimension getPreferredSize(){
return new Dimension(500, 500);
}
}
Then you can instantiate that panel in another class, adding it to a JFrame to run it
public class Main {
public static void main(String[] args) {
SwingUtiliites.invokeLater(new Runnable(){
public void run() {
JFrame frame = new JFrame();
frame.add(new DrawPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
});
}
}
You are calling this in your constructor for your Smiley class.
MainScreen.graphicPane.drawImage(smileyFace,50,50, null);
If you are going to paint the image yourself you need to override paintComponent() in a component that gets added to your main screen.
Or just add the image to a JLabel that you added to the main screen.
You draw image in wrong way.
For using drawImage() you need to use that in paintComponent() method of JComponent(for example JPanel), examine next code:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.beans.Transient;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Example extends JFrame {
public Example() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Smiley());
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]) {
new Example();
}
class Smiley extends JPanel{
private BufferedImage smileyFace;
Smiley(){
try {
File smileyFile = new File("C:\\Users\\MyName\\Desktop\\smiley.png");
smileyFace = ImageIO.read(smileyFile);
} catch (Exception e) {
System.out
.println("There was an error finding or reading the file \" smiley.png.\"");
}
}
#Override
#Transient
public Dimension getPreferredSize() {
return new Dimension(smileyFace.getWidth(),smileyFace.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(smileyFace, 0,0, this);
}
}
}
or you can add your image to JLabel and that do all for you, change Smile class like next:
class Smiley extends JPanel{
Smiley(){
ImageIcon icon = new ImageIcon("C:\\Users\\MyName\\Desktop\\smiley.png");
JLabel l = new JLabel(icon);
add(l);
}
}
ALso read more about customPaintings.

Listener that has events for when a frame is snapped to the left or right of screen

I know WindowStateListener will tell me if a component is maximized vertically, horizontally, or both. At least, it says it will. I do not know how to maximize a window exclusively horizontally or exclusively vertically outside of snapping the window to the left edge or right edge of my screen. Is there another listener for this behavior? Or will I need to make something myself that tracks mouse location in relation to the screen? I made an example program that displays what I've described.
SSCCE
import java.awt.Dimension;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
SnapFrame frame = new SnapFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(new Dimension(500,500));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class SnapFrame extends JFrame implements WindowStateListener{
public SnapFrame(){
super();
addWindowStateListener(this);
}
#Override
public void windowStateChanged(WindowEvent arg0) {
//will print a statement for all state change events except snapping to left/right edges of screen
System.out.println("state changed");
}
}
If I wanted to have things triggered by this behavior, and there are no other listeners that have events for this, would a decent idea be to get my screen size and watch my mouse location for when it comes near the edge of my screen?
This source detects when the frame is snapped to the top or bottom of screen, using a ComponentListener. I don't know how to configure my Windows to snap to the left or right.
import java.awt.*;
import java.awt.event.*;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
SnapFrame frame = new SnapFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(new Dimension(500, 500));
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
class SnapFrame extends JFrame implements ComponentListener {
Dimension screenSize;
public SnapFrame() {
super();
screenSize = Toolkit.getDefaultToolkit().getScreenSize();
addComponentListener(this);
}
#Override
public void componentResized(ComponentEvent e) {
Rectangle r = getBounds();
if (r.getMinY()==0 || r.getMaxY()==(int)screenSize.getHeight()) {
System.out.print("Well snap!");
}
}
#Override
public void componentMoved(ComponentEvent ignore) {}
#Override
public void componentShown(ComponentEvent ignore) {}
#Override
public void componentHidden(ComponentEvent ignore) {}
}
Other tips
Don't extend frame or other top level containers. Instead create & use an instance of one.
Swing GUIs should be created and updated on the EDT. See Concurrency in Swing for more details.

g.drawLine doing nothing

This is my code so far with some print lines just to make sure it was actually even going into the method. For some reason NOTHING is being drawn on the canvas, I have a program similar to this as far as the drawing goes and it works fine. What is wrong with this one?
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Scanner;
import javax.swing.*;
public class gameOfLife implements ActionListener {
private int height;
private int width;
private Graphics g;
private JPanel panel;
private JFrame frame;
int[][] board= new int[40][40];
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
gameOfLife gui = new gameOfLife();
}
public gameOfLife() {
int height=400;
int width=400;
frame= new JFrame("Keegan's Game Of Life");
frame.setSize(new Dimension(height,width));
frame.setLayout(new BorderLayout());
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE );
g=frame.getGraphics();
drawBoard();
}
public void drawBoard() {
g.setColor(Color.BLUE);
g.drawLine(0, 0, 50, 50);
g.fillOval(50,50,10,10);
System.out.println("Done Drawing");
g.drawString("IT WORKED!", 100, 100);
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
}
Let's start with g=frame.getGraphics();
This is a very bad idea and not how custom painting is performed. getGraphics may return null and is generally only a snap shot of the last paint cycle. Anything painted to the Graphics context via this method will be destroyed on the next repaint cycle.
You should never maintain a reference to any Graphics context, they are transient and may not be the same object between paint cycles
Instead, create yourself a custom component (something like JPanel) and override it's paintComponent method
Check out Performing Custom Painting for more details
Updated
You can check out this simple example for an idea...
You can override paint(Graphics g) in your canvas, otherwise the drawing will disappear once the canvas is invalidated (e.g. moved or covered by another windows).
It might be easier to let your class extends JFrame and override the paint methods, otherwise you can use anonymous class e.g.
frame = new JFrame("Keegan's Game Of Life") { //override paint here }
However, if your application aims to create animation for Game Of Life, you should not be doing this in a JFrame, consider using JPanel or Canvas

Categories