Custom painting JPanel offsets the drawing - java

I'm using Netbeans (JDK 1.7) on Windows 8.1 to run an application that:
Uses the Graphics object to draw a frog in the paintComponent() of a 400x400 JPanel;
Adds the panel to a JFrame for displaying purposes;
This should be a very simple task, of course, but I'm having trouble understanding a specific behavior in this application.
Here is the problem: the very first time the window is displayed, the panel shows up at an offset (image below) when it shouldn't. I'm trying to understand why this is happening and how to solve it:
After one of the arrow keys are pressed on the keyboard the drawing gets displaced 5 pixels to the left/right/up/down. However, when this happens, the entire JPanel seems to be moved to the (0,0) coordinate of the window (which is what I expected from the beginning), thus moving the drawing a lot more than it should:
I painted a black border on the edges of the panel to make the problem more apparent.
Here is the Short, Self Contained, Correct (Compilable), Example:
KAfrog.java:
import javax.swing.SwingUtilities;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class KAfrog
{
public static void main( String args[] )
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
} // end of anonymous innerclass
); // end of invokeLater
}
private static void createAndShowGUI()
{
System.out.println("Created GUI on EDT? " + SwingUtilities.isEventDispatchThread());
Window janela = new Window("Khan Academy Frog");
janela.addWindowListener( new WindowAdapter() {
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
} // end of anonymous innerclass
); // end of addWindowListener
}
}
Window.java:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
class DrawingPanel extends JPanel
{
private int x = 50, y = 50;
public DrawingPanel() {
super();
setBorder(new LineBorder(Color.BLACK));
}
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void setX(int x) {
if (x < 0 || x >= 400)
return;
this.x = x;
repaint();
}
public void setY(int y) {
if (y < 0 || y >= 400)
return;
this.y = y;
repaint();
}
public int getX() { return this.x; }
public int getY() { return this.y; }
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("DrawingPanel::paintComponent: drawing at x:" + x + " y:" + y);
System.out.println("DrawingPanel::paintComponent: panel size " + getWidth() + "x" + getHeight());
// face
Color green = new Color(30, 204, 91);
g.setColor(green);
g.fillOval(x, y, 200, 100);
// mouth
g.setColor(Color.BLACK); // g.setColor(new Color(0, 0, 0));
g.fillOval(x+25, y+25, 150, 50);
// left eye
g.setColor(green);
g.fillOval(x+30, y-30, 45, 45); // background
g.setColor(Color.WHITE);
g.fillOval(x+35, y-25, 35, 35); // white eye sockets
g.setColor(Color.BLACK);
g.fillOval(x+48, y-15, 10, 10); // black eyes pretos
// right eye
g.setColor(green);
g.fillOval(x+110, y-30, 45, 45); // background
g.setColor(Color.WHITE);
g.fillOval(x+115, y-25, 35, 35); // white eye sockets
g.setColor(Color.BLACK);
g.fillOval(x+128, y-15, 10, 10); // black eyes
}
}
public class Window extends JFrame
{
DrawingPanel frog_panel;
public Window(String title)
{
super(title);
frog_panel = new DrawingPanel();
addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT)
frog_panel.setX(frog_panel.getX()-5);
if (e.getKeyCode() == KeyEvent.VK_RIGHT)
frog_panel.setX(frog_panel.getX()+5);
if (e.getKeyCode() == KeyEvent.VK_UP)
frog_panel.setY(frog_panel.getY()-5);
if (e.getKeyCode() == KeyEvent.VK_DOWN)
frog_panel.setY(frog_panel.getY()+5);
}
public void keyReleased(KeyEvent e) { }
public void keyTyped(KeyEvent e) { }
}
);
add(frog_panel);
pack();
setVisible(true);
System.out.println("Window::Window: size is " + getWidth() + "x" + getHeight());
}
}
Does anyone know why this happens and how to fix it?

You overrode public int getX() from javax.​swing.​JComponent.
It returns the current x coordinate of the component's origin. This method is preferable to writing component.getBounds().x, or component.getLocation().x because it doesn't cause any heap allocations.
Rename your methods public int getX() and public int getY().
This should fix the problem.

Related

How do I move from one JFrame window to another on clicking the space bar?

This is the main file where I've created the main method. I've added the JFrames here to make it easier to understand. I created a boolean variable in the second file, and whenever the spacebar is pressed, the variable updates to true. I then use the variable by creating an object in the main class and then add conditional statements to make 1 frame visible or vice versa.
import javax.swing.JFrame;
public class MainCode{
public static void main(String args[]){
JFrame f=new JFrame();
Gameplay GP = new Gameplay();
GameplayLv2 g2 = new GameplayLv2();
JFrame frame2 = new JFrame();
f.setBounds(10,10,1000,700);
f.setTitle("Breakout Ball");
f.setResizable(false);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(GP);
if(GP.flagCheck==true){
f.setVisible(false);
f.remove(GP);
frame2.setBounds(10,10,1000,700);
frame2.setTitle("Breakout Ball");
frame2.setResizable(false);
frame2.setVisible(true);
frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame2.add(g2);
}
}
}
This is the game file, level 1. The issue that I keep facing is at the bottom of this code, the keyEvent block.
import javax.swing.JPanel;
import java.awt.*;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.Timer;
import javax.swing.JFrame;
public class Gameplay extends JPanel implements KeyListener, ActionListener{
public boolean play=false; //prevents game from starting on its own...
public int score=0; //scorebar will be 0 by default...
public int totalBlocks = 30; //no.of tiles/blocks player must knock down...
public Timer speed; //speed of ball...
public int delay=5; //speed of ball....
public int playerX = 300; //starting position of the slider..
public int ballPosX = 210; //starting position of the ball..(x-axis)
public int ballPosY = 350; //starting position of the ball..(y-axis)
public int ballDirX = -1; //direction for ball to start moving in...(x axis)
public int ballDirY = -2; //direction for ball to start moving in...(y axis) //ALSO CONTROLS SPEED OF BALL ALONG Y-AXIS
public boolean flagCheck;
MapGen map;
Color ball_color = new Color(255, 252, 37); //Setting custom colors for every element using RGB values...
Color border_color = new Color(232, 20, 171);
Color border2_color = new Color(20, 232, 213);
Color slider_color = new Color(0, 240, 255);
Color bg_color = new Color(11,10,30);
Color tiles_color = new Color(255, 255, 255);
JFrame frame1 = new JFrame();
public Gameplay(){
map = new MapGen(3,10);
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
speed = new Timer(delay,this);
speed.start();
flagCheck = false;
}
public void paint(Graphics g){
g.setColor(bg_color);
g.fillRect(1, 1, 1000, 700);
//drawing the map(tiles)
map.draw((Graphics2D)g);
// adding borders so that whenever ball hits border, game ends..
g.setColor(border_color);
g.fillRect(0, 0, 10, 1000);
g.setColor(border_color);
g.fillRect(975, 0, 10, 1000);
g.setColor(border2_color);
g.fillRect(0, 0, 1000, 10);
// editing the slider/player...
g.setColor(slider_color);
g.fillRect(playerX, 620, 120, 5);
// editing the ball...
g.setColor(ball_color);
g.fillOval(ballPosX, ballPosY, 20, 20);
//scoring system
g.setColor(Color.white);
g.setFont(new Font("helvetica", Font.BOLD, 20));
g.drawString("SCORE:"+score, 800, 650);
if(ballPosY>670){ //what'll happen when the ball moves out of screen?(anything after y-axis670)? This...
play=false;
ballDirX=0;
ballDirY=0;
g.setColor(bg_color); //this BG will act as a layer on top of the previous screen, hence, covering unnecessarry elements...
g.fillRect(1, 1, 1000, 700);
g.setColor(slider_color);
g.setFont(new Font("helvetica", Font.BOLD, 25)); //scoreboard display after game ends..
g.drawString("SCORE:"+score, 440, 200);
g.setColor(slider_color);
g.setFont(new Font("helvetica", Font.BOLD, 40)); //game over text displayed after game ends..
g.drawString("GAME OVER!", 370, 300);
g.setColor(slider_color);
g.setFont(new Font("helvetica", Font.BOLD, 40)); //restart text displayed after game ends..
g.drawString("Press enter to retry", 260, 405);
}
if(score==30){ //make it 150 //what'll happen when the score is 150, ie win
play=false;
ballDirX=210;
ballDirY=350;
g.setColor(bg_color); //this BG will act as a layer on top of the previous screen, hence, covering unnecessarry elements...
g.fillRect(1, 1, 1000, 700);
g.setColor(slider_color);
g.setFont(new Font("helvetica", Font.BOLD, 25)); //scoreboard display after game ends..
g.drawString("SCORE:"+score, 420, 200);
g.setColor(slider_color);
g.setFont(new Font("helvetica", Font.BOLD, 40)); //game over text displayed after game ends..
g.drawString("YOU WIN", 400, 300);
g.setColor(slider_color);
g.setFont(new Font("helvetica", Font.BOLD, 40)); //go to next level text displayed after game ends..
g.drawString("Press space to continue to next level", 155, 405);
}
g.dispose();
}
#Override
public void actionPerformed(ActionEvent e) {
speed.start();
if(play==true){
if(new Rectangle(ballPosX, ballPosY, 20, 20).intersects(new Rectangle(playerX, 620, 120, 10)))
//creates an invisible rectangle around the slider and ball which helps with interaction control
{
ballDirY=-ballDirY;
}
A: for(int i=0; i<map.map.length; i++){ //one of the maps are MapGen map; while the other map is from #mapGen class to access the double dimensional array...
for(int j=0; j<map.map[0].length; j++){
if(map.map[i][j]>0){
int tX = j*map.tW+80;
int tY = i*map.tH+50;
int tW = map.tW;
int tH = map.tH;
Rectangle rect = new Rectangle(tX, tY, tW, tH); //creates invisible rects around tiles to detect interaction with ball...
Rectangle ballRect = new Rectangle(ballPosX, ballPosY, 20, 20);
Rectangle tileRect = rect;
if(ballRect.intersects(tileRect)){ //removes one triangle everytime interaction takes place and increases the score by 5...
map.setTileValue(0, i, j);
totalBlocks--;
score+=5;
if(ballPosX+19 <= tileRect.x || ballPosX+1 >= tileRect.x + tileRect.width){ //specifies which direction the ball must head to after interacting with the tiles...
ballDirX=-ballDirX;
}
else{
ballDirY=-ballDirY;
}
break A;
}
}
}
}
ballPosX+=ballDirX;
ballPosY+=ballDirY;
//specifies where the ball will have to shift position. In this case, on hitting either of the borders, position(x-y-coordinates) will change....
if(ballPosX<0)
ballDirX=-ballDirX;
if(ballPosY<0)
ballDirY=-ballDirY;
if(ballPosX>960)
ballDirX=-ballDirX;
}
repaint(); //will re-draw every element(slider etc)...
}
#Override
public void keyTyped(KeyEvent e) {} //unnecessarry methods that when removed produced error...so..yeah...
#Override
public void keyReleased(KeyEvent e) {}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_RIGHT)
{
if(playerX>=855) //prevents the paddle/slider from moving out of the screen
playerX=855;
else
moveRight();
}
if(e.getKeyCode()==KeyEvent.VK_LEFT)
{
if(playerX<=10) //prevents the paddle/slider from moving out of the screen
playerX=10;
else
moveLeft();
}
if(e.getKeyCode()==KeyEvent.VK_ENTER){ //What actions must take place when "ENTER" key is pressed. In this context, restarting of game...
if(!play){
play=false;
ballPosX = 210;
ballPosY = 350;
ballDirX = -1;
ballDirY = -2;
playerX = 300;
score=0;
totalBlocks=30;
map=new MapGen(3,10);
repaint();
}
}
if(e.getKeyCode()==KeyEvent.VK_SPACE){ //what actions must take place when "SPACE" key is pressed. In this context, moving to new level-to be implmented...
if(!play && score==30){
flagCheck=true;
System.out.println(flagCheck); //console stuff
}
}
}
public void moveRight(){ //helps with the movement of slider. +values move right on x-axis whereas -values move left on x-axis
play=true;
playerX+=40;
}
public void moveLeft(){
play=true;
playerX-=40;
}
}
You're running in an event driven environment, that is, something happens and then you respond to it.
You've taken a procedural approach to your design. Showing a window and some how hoping that the state of the window "block" the execution workflow and allow you to monitor some other state. This is not how (non modal) windows work.
When you call setVisible, it will return (almost) immediately, the display of the window will occur at some point in the future after that, depending on the OS and other overheads.
This means that GP.flagCheck will also fail.
Instead, stop trying to do this with windows. It's annoying (to the user). Instead, just switch the panels, for example...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MasterPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MasterPane extends JPanel {
public MasterPane() {
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "spaced");
am.put("spaced", new AbstractAction() {
private int counter = 0;
#Override
public void actionPerformed(ActionEvent e) {
JPanel next = null;
if (counter == 0) {
counter = 1;
next = new LevelTwoPane();
} else if (counter == 1) {
counter = 0;
next = new LevelOnePane();
}
if (next != null) {
removeAll();
add(next);
revalidate();
repaint();
}
}
});
setLayout(new BorderLayout());
add(new LevelOnePane());
}
}
protected abstract class AbstractGamePane extends JPanel {
private Timer timer;
protected abstract void tick();
protected void start() {
if (timer != null) {
timer.stop();
timer = null;
}
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
tick();
}
});
timer.start();
}
protected void stop() {
if (timer == null) {
return;
}
timer.stop();
timer = null;
}
#Override
public void addNotify() {
super.addNotify();
start();
}
#Override
public void removeNotify() {
super.removeNotify();
stop();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
public static class LevelOnePane extends AbstractGamePane {
protected static final String TEXT = "Level one";
private int xPos = 0;
private int xDelta = 1;
#Override
protected void tick() {
xPos += xDelta;
FontMetrics fm = getFontMetrics(getFont());
if (xPos > getWidth() - fm.stringWidth(TEXT)) {
xPos = getWidth() - fm.stringWidth(TEXT);
xDelta *= -1;
} else if (xPos < 0) {
xPos = 0;
xDelta *= -1;
}
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
FontMetrics fm = g2d.getFontMetrics();
int yPos = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g2d.drawString(TEXT, xPos, yPos);
g2d.dispose();
}
}
public class LevelTwoPane extends AbstractGamePane {
protected static final String TEXT = "Level two";
private int xPos = 0;
private int xDelta = 1;
#Override
protected void tick() {
xPos += xDelta;
FontMetrics fm = getFontMetrics(getFont());
if (xPos > getWidth() - fm.stringWidth(TEXT)) {
xPos = getWidth() - fm.stringWidth(TEXT);
xDelta *= -1;
} else if (xPos < 0) {
xPos = 0;
xDelta *= -1;
}
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
FontMetrics fm = g2d.getFontMetrics();
int yPos = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g2d.drawString(TEXT, xPos, yPos);
g2d.dispose();
}
}
}
Even this is unnecessarily heavy handed. An overall better solution would be to use a model/view/controller.
The model would carry all the information about the level that the view needed to render it and the controller would manage the interaction between the view and the model (ie the user taps a button, the view tells the controller and the controller makes decisions about what needs to be done, like updating the model or moving to the next level or what ever).
You really should take a look at How to Use Key Bindings, KeyListeners are just trouble waiting to happen.
You can also have a look at Detecting multiple keypresses in java for a more detailed example
You will note that the MainPane has a binding to Space, but I would also set up key bindings, probably in the AbstractGamePane, assuming all game levels have the same controls, to manage the game play
You should also take a look at How to Use Swing Timers for a way to manage a "game loop" better. Calling repaint within a running paint pass is asking for trouble (it will end up running your CPU hot)

How do I make a sprite move in a custom JPanel?

How do I make a sprite move in a custom JPanel?
I have looked at the similar questions and although one question is similar, it isn't addressing my problem. I have a sprite in a JPanel and I am unable to get it to move. One of the requirements I have to meet for the program is that it must begin moving when a JButton is pressed (Mouse Click). I have the code set-up in a way I believe should work, but it will spit out a long list of errors when I press the button. I'm also required to have the panel be a custom panel class.
What I need to know is this:
Methods (ha) of programming sprite movement.
Continuing to move the sprite without a trail.
Making the sprite bounce off the edges of the panel. Done (Unable to test due to no moving ball)
Here's the code I have (MainClient).
package clientPackage;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import logicPack.Logic;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ClientClass
{
Ball mSolo = new Ball();
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClientClass window = new ClientClass();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ClientClass()
{
initialize();
}
/**
* Initialize the contents of the frame.
*/
Logic Logical;
Graphics g;
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 590, 520);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
SpriteField panel = new SpriteField();
panel.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
/* int tX = e.getX();
Logical.MoveBallX();
int tY = e.getY();
Logical.MoveBallY();
panel.repaint();*/
Logical.MoveBallX();
Logical.MoveBallY();
panel.repaint();
}
});
panel.setForeground(Color.WHITE);
panel.setBackground(Color.GRAY);
panel.setBounds(64, 92, 434, 355);
frame.getContentPane().add(panel);
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
Graphics2D g2 = (Graphics2D)g;
mSolo.DrawSprite(g2 , Logical.MoveBallX(), Logical.MoveBallY());
}
});
btnStart.setBounds(64, 13, 174, 60);
frame.getContentPane().add(btnStart);
}
}
And here are my other Classes (Logic)
package logicPack;
import clientPackage.Ball;
public class Logic
{
Ball mSolo;
public int MoveBallX()
{
int NewX = mSolo.xPos + 50;
return NewX;
}
public int MoveBallY()
{
int NewY = mSolo.yPos + 50;
return NewY;
}
//Motion, force, friction and collision GO HERE ONLY
}
SpriteField
package clientPackage;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class SpriteField extends JPanel
{
Ball mSolo;
SpriteField()
{
mSolo = new Ball();
repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
mSolo.DrawSprite(g2 , mSolo.xPos , mSolo.yPos);
}
}
Ball
package clientPackage;
import java.awt.Color;
import java.awt.Graphics2D;
public class Ball
{
Ball()
{
}
public int xPos = 25;
public int yPos = 25;
int diameter = 25;
public void DrawSprite(Graphics2D g2, int xPos, int yPos)
{
g2.setColor(Color.BLACK);
g2.fillOval(xPos - diameter / 2 , yPos - diameter / 2 , diameter , diameter);
}
}
If you do not understand my Java comments, you can just ignore them.
If you need more details to help me, let me know.
EDIT 1:
Andrew, the closest article I could find used arrow keys to move a sprite. The article was "Sprite not moving in JPanel". All the other articles I found either addressed JPanels without sprites, or animating a sprite. However, I need a JButton that is MouseClicked to simply start the movement, and the ball does not change shape or color. I believe I have the collision part working, but I'm unable to test it until the ball starts moving.
EDIT 2:
LuxxMiner, Thanks for the hints. I have refined my collision portion to be a little more accurate using the getHeight and getWidth methods.
EDIT 3:
MadProgrammer, Thanks...? The problem is not the painting of the ball, I cannot get the ball to move in the first place to repaint it. And the example uses arrow keys, not a mouse click or JButton.
First, take a look at Painting in AWT and Swing and Performing Custom Painting to understand how painting works in Swing.
Let's have a look at the code...
You have a Ball class, which has it's own properties, but then your DrawSprite method passes in values which override these properties?
public class Ball {
Ball() {
}
public int xPos = 25;
public int yPos = 25;
int diameter = 25;
public void DrawSprite(Graphics2D g2, int xPos, int yPos) {
g2.setColor(Color.BLACK);
g2.fillOval(xPos - diameter / 2, yPos - diameter / 2, diameter, diameter);
}
}
What's the point of that? The Ball should paint it's own current state. You should get rid of the additional parameters
public class Ball {
Ball() {
}
public int xPos = 25;
public int yPos = 25;
int diameter = 25;
public void DrawSprite(Graphics2D g2) {
g2.setColor(Color.BLACK);
g2.fillOval(xPos - diameter / 2, yPos - diameter / 2, diameter, diameter);
}
}
ClientClass, Logic and SpriteField all have their own Ball references, none of which is shared so if Logic where to update the state of it's Ball, neither ClientClass or SpriteField would actually see those changes.
In reality, only SpriteField needs an instance of Ball, as it's basically the "ball container", it has the information need to determine if the ball moves out of bounds and wants to know when the ball should be repainted, better to isolate the functionality/responsibility for the Ball to SpriteField at this time.
You also need a means to actually move the ball. While you could use other events, I'd be nice if the ball just moved itself, to this end, you can use a Swing Timer, which won't block the Event Dispatching Thread, but which notifies the registered ActionListener within the context of the EDT, making it safe to update the UI from within.
public class SpriteField extends JPanel {
private Ball mSolo;
private Timer timer;
private int xDelta, yDelta;
public SpriteField() {
mSolo = new Ball();
do {
xDelta = (int) ((Math.random() * 8) - 4);
} while (xDelta == 0);
do {
yDelta = (int) ((Math.random() * 8) - 4);
} while (yDelta == 0);
}
public void start() {
if (timer == null || !timer.isRunning()) {
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
mSolo.xPos += xDelta;
mSolo.yPos += yDelta;
if (mSolo.xPos - (mSolo.diameter / 2) < 0) {
mSolo.xPos = mSolo.diameter / 2;
xDelta *= -1;
} else if (mSolo.xPos + (mSolo.diameter / 2) > getWidth()) {
mSolo.xPos = getWidth() - (mSolo.diameter / 2);
xDelta *= -1;
}
if (mSolo.yPos - (mSolo.diameter / 2) < 0) {
mSolo.yPos = (mSolo.diameter / 2);
yDelta *= -1;
} else if (mSolo.yPos + (mSolo.diameter / 2) > getHeight()) {
mSolo.yPos = getHeight() - (mSolo.diameter / 2);
yDelta *= -1;
}
repaint();
}
});
timer.start();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
mSolo.DrawSprite(g2);
}
}
Now, all you need to do, is when the "Start" button is clicked, call the start method
public class ClientClass {
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClientClass window = new ClientClass();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ClientClass() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
// Logic Logical;
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 590, 520);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SpriteField panel = new SpriteField();
panel.setForeground(Color.WHITE);
panel.setBackground(Color.GRAY);
frame.getContentPane().add(panel);
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
panel.start();
}
});
frame.getContentPane().add(btnStart, BorderLayout.SOUTH);
}
}

Using listeners and mouse pointer location to check if pointer is inside of a circle

I'm trying to make it so a line of text is drawn on the point of the cursor that states if the cursor is inside or outside of the drawn circle. I'm not entirely sure how to do that and I don't think that the global X and Y coordinates I've made are working properly.
Does anyone know of a way to some how trigger the string to be drawn when the cursor is in or out of the circle? I'm still sort of new to Java.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MouseCircleLocation extends JPanel {
boolean insideCircleStatus;
PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
int x = (int) b.getX();
int y = (int) b.getY();
public MouseCircleLocation() {
//the listener that checks if the mouse if moving.
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
//gets the location of the mouse.
int x = e.getX();
int y = e.getY();
//simple check to see if the mouse location is inside the circle area.
if ( (Math.pow((x - 100), 2)) + (Math.pow((y - 60), 2)) < (Math.pow((50), 2))){
insideCircleStatus = true;
}
else{
insideCircleStatus = false;
}
}
});
}
//basic frame setup.
public static void main(String[] args) {
JFrame frame = new JFrame("Mouse Location");
frame.add(new MouseCircleLocation());
frame.setSize(210, 190);
frame.setLocationRelativeTo(null); // Center the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
//draws the circle.
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (insideCircleStatus = true){
g.drawString("Point is inside the circle", x, y);
System.out.println("Point is inside the circle");
}
if (insideCircleStatus = false){
g.drawString("Point is outside the circle", x, y);
System.out.println("Point is outside the circle");
}
g.setColor(Color.BLACK);
g.drawOval(100 - 50, 60 - 50, 50 *2, 50 * 2);
}
}
One thing you're forgetting to do is call repaint(). Just because the value of insideCircleStatus changes, doesn't make it automatically repaint(), so you'll want to call repaint() inside the MouseListener
Also, you could use the Shape interface and the method contains for example
Ellipse2D.Double circle = new Ellipse2D.Double(50, 50, 200, 200);
...
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e) {
if (circle.contains(e.getPoint())) {
System.out.println("Click within Circle");
}
}
});
See full example using MouseMotionListener and the API - Also See Ellips2D
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestShapeContains extends JPanel {
private static final int D_W = 500;
private static final int D_H = 500;
Ellipse2D.Double circle = new Ellipse2D.Double(50, 50, 300, 300);
Point p = new Point();
private boolean insideCircle = false;
public TestShapeContains() {
addMouseMotionListener(new MouseMotionAdapter(){
public void mouseMoved(MouseEvent e) {
if (circle.contains(e.getPoint())) {
insideCircle = true;
} else {
insideCircle = false;
}
p = e.getPoint();
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.draw(circle);
if (insideCircle) {
g.drawString("Mouse in Circle at point " + (int)p.getX() + ", " + (int)p.getY(),
(int)p.getX(), (int)p.getY());
} else {
g.drawString("Mouse outside Circle at point " + (int)p.getX() + ", " + (int)p.getY(),
(int)p.getX(), (int)p.getY());
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new TestShapeContains());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

Graphic Only Moves 1 Pixel to the Right With KeyListener

I'm fairly new to Java programming and I'm working on my first 2D game. I followed one of Oracle's tutorials on Key Listeners in order to figure out how to make my player move with the WASD keys. The example already had a mouse listener. I decided to remove the mouse listener and add a KeyListener. I added the a KeyListener for D which moves my graphic to the right. However, my graphic only moves 1 coordinate to the right rather than updating the new position of the square, and moving it in a continuous loop whenever the D key is pressed. I'm using paint component to draw my image.
Here is my class:
import javax.swing.SwingUtilities;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseMotionAdapter;
public class PaintComponent {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
System.out.println("Created GUI on EDT? "
+ SwingUtilities.isEventDispatchThread());
JFrame f = new JFrame("Swing Paint Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
}
class MyPanel extends JPanel {
private int squareX = 50;
private int squareY = 50;
private int squareW = 20;
private int squareH = 20;
public int posX = squareX;
public int posY = squareY;
public MyPanel() {
setFocusable(true);
requestFocusInWindow();
setBorder(BorderFactory.createLineBorder(Color.black));
addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_D) {
moveSquareRight(posX);
} else {
e.consume();
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
int keyCode = e.getKeyCode();
e.consume();
}
});
/*
* addMouseListener(new MouseAdapter() { public void
* mousePressed(MouseEvent e) { moveSquare(e.getX(),e.getY()); } });
*
* addMouseMotionListener(new MouseAdapter() { public void
* mouseDragged(MouseEvent e) { moveSquare(e.getX(),e.getY()); } });
*/
}
private void moveSquareRight(int x) {
int OFFSET = 1;
if (squareX == x) {
repaint(squareX, squareY, squareW + OFFSET, squareH + OFFSET);
squareX = x + 1;
repaint(squareX, squareY, squareW + OFFSET, squareH + OFFSET);
}
}
public Dimension getPreferredSize() {
return new Dimension(250, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("This is my custom Panel!", 10, 20);
g.setColor(Color.RED);
g.fillRect(squareX, squareY, squareW, squareH);
g.setColor(Color.BLACK);
g.drawRect(squareX, squareY, squareW, squareH);
}
}
I'm thinking of turning posX into an array and adding a for loop to it,
No you don't need a loop.
moveSquareRight(posX);
There is no need to pass posX as a parameter since it is a class variable every method has access to it.
Although posX doesn't change and it therefore stays in the same spot.
So update posX.
if (squareX == x) {
repaint(squareX, squareY, squareW + OFFSET, squareH + OFFSET);
squareX = x + 1;
repaint(squareX, squareY, squareW + OFFSET, squareH + OFFSET);
}
You are making the above code too complicated. Try something like:
posX++;
repaint();
You don't want to specify the rectangle to repaint() because the old painting will not be cleared before the rectangle is painted in its new position.
You should also check out Motion Using the Keyboard for an approach the moves a component.

java graphics update

hey im trying to make a simple ascii game in java but i cant get the graphics to update on key presses
im trying to make it so the guy is controllable with the arrow keys
heres the code
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.Timer;
public class shooter extends JFrame{
int x = 100, y = 100, dx, dy;
Timer time;
/**
*
*/
private static final long serialVersionUID = 1L;
public void move(){
x = x + dx;
y = y + dx;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public shooter() {
setTitle("title of gip");
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
//keep at bottom
setVisible(true);
//dont code here >:(
}
///////////GRAPHICS ////////////////////
public void paint(Graphics g){
g.clearRect(0, 0, getWidth(), getHeight());
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.white);
g.drawString("☺", getX(), getY());
}
public void paintComponent(Graphics g){
}
//////////////end of graphics//////////////
public void keyPressed(KeyEvent e){
int pressed = e.getKeyCode();
if (pressed == KeyEvent.VK_LEFT){
dx = -1;
}
if (pressed == KeyEvent.VK_RIGHT){
dx = 1;
}
}
public void keyReleased(KeyEvent e){
int rel = e.getKeyCode();
if (rel == KeyEvent.VK_LEFT){
dx = 0;
}
if (rel == KeyEvent.VK_RIGHT){
dx = 0;
}
}
public void actionPerformed(ActionEvent e) {
move();
repaint();
}
public static void main(String[] args){
new shooter();
}
}
please help
thanks
You haven't added any action listener to your frame. The key pressed and action performed methods are called when these events happen, but only from objects with action listeners or Key listeners attached.
Check out http://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html.
In short though you need the following, public class TryItOut extends JFrame implements ActionListener,KeyListener{ and the in your constructor addActionListener(this); and addKeyListener(this);
Do that and then try from there.
#dann.dev has well found the issue. You are not hooking up your key strokes and events to the main frame.
Also, I just want to comment on the way you are drawing your scene. You should draw in a JPanel instead of a JFrame, this will make your life easier if you are willing to add buttons and menus to the main frame.
Override and draw in paintComponent not paint.
class DrawingPanel{
// ....
#Override
public void paintComponent(Graphics g){
g.clearRect(0, 0, getWidth(), getHeight());
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.white);
g.drawString("☺", getX(), getY());
}
// ....
}
Once done, set this panel as your frame's content pane.
frame.setContentPane(new DrawingPanel());
1) for listening key events from keyboard would be better to implement KeyBindings, example here
2) ClassName would be public class Shooter extends JFrame {
3) paint method for Swing would be public void paintComponent(Graphics g){
4) main method would be
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Shooter();
}
});
}
EDIT:
5) by #camickr correct noticed me -> CustomPainting should be done on a JComponent or JPanel, or you can use method paint to the GlassPane or RootPane

Categories

Resources