Game throws NullPointerException - java

So i am making a small game called dodge were rectangles fall from the "sky" and you have to do dodge them. As i ran the game this error message just popped up and i have no clue why. Also the screen just goes white whenever i run the program.
I cannot figure this out, Ive searched for hours in my code cant figure out why. Please Halp
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at dodge.Enemy.move(Enemy.java:22)
at dodge.Enemy.draw(Enemy.java:32)
at dodge.EnemyManager.draw(EnemyManager.java:34)
at dodge.Dodge.paint(Dodge.java:38)
at javax.swing.JComponent.paintChildren(JComponent.java:887)
at javax.swing.JComponent.paint(JComponent.java:1063)
at javax.swing.JComponent.paintChildren(JComponent.java:887)
at javax.swing.JComponent.paint(JComponent.java:1063)
at javax.swing.JLayeredPane.paint(JLayeredPane.java:585)
at javax.swing.JComponent.paintChildren(JComponent.java:887)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5226)
at
Sorry thats long. But here is my code.
Dodge.java
package dodge;
import java.awt.event.KeyListener;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.*;
import java.awt.Graphics;
import java.awt.event.*;
public class Dodge extends JPanel implements KeyListener{
private Player player;
private Stage stage;
private EnemyManager manager;
public Dodge(){
setSize(new Dimension(800, 600));
setPreferredSize(new Dimension(800, 600));
addKeyListener(this);
setFocusable(true);
stage = new Stage();
player = new Player(this, 200, 200);
manager = new EnemyManager(this, 10);
}
#Override
public void update(Graphics g){
paint(g);
}
#Override
public void paint(Graphics g){
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
stage.draw(g);
player.draw(g);
manager.draw(g);
g.dispose();
repaint();
}
#Override
public void keyReleased(KeyEvent e){
player.setXD(0);
player.setYD(0);
}
#Override
public void keyTyped(KeyEvent e){
}
public Stage getStage(){
return stage;
}
#Override
public void keyPressed(KeyEvent e){
int c = e.getKeyCode();
if(c == KeyEvent.VK_W){
}
if(c ==KeyEvent.VK_A){
player.setXD(-1);
}
if(c == KeyEvent.VK_S){
}
if(c ==KeyEvent.VK_D){
player.setXD(1);
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("Dodge the Rectangles");
frame.add(new Dodge());
frame.pack();
frame.setResizable(false);
frame.setSize(new Dimension(800, 600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
EnemyManager.java
package dodge;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
public class EnemyManager {
private int amount;
private List<Enemy> enemies = new ArrayList<Enemy>();
private Dodge instance;
public EnemyManager(Dodge instance, int a){
this.amount = a;
spawn();
this.instance = instance;
}
private void spawn(){
Random random = new Random();
int ss = enemies.size();
if(ss<amount){
for(int i = 0; i < amount - ss; i++){
enemies.add(new Enemy(instance, random.nextInt(778), 10));
}
}else if(ss>amount){
for(int i = 0; i < ss - amount; i++){
enemies.remove(i);
}
}
}
public void draw(Graphics g){
for(Enemy e : enemies) e.draw(g);
}
}
Enemy.java
package dodge;
import java.awt.*;
public class Enemy extends Entity{
private Rectangle hitbox;
private int ix, iy;
private boolean dead = false;
private Dodge instance;
public Enemy(Dodge instance, int x, int y){
super(x, y);
this.instance = instance;
hitbox = new Rectangle (x, y, 32, 32);
ix = 0;
iy = 1;
}
private void move(){
if(instance.getStage().isCollided(hitbox)){
iy = 0;
dead = true;
}
hitbox.x += ix;
hitbox.y +=iy;
}
private boolean isDead() {return dead;}
#Override
public void draw(Graphics g){
move();
g.setColor(Color.CYAN);
g.fillRect(hitbox.x, hitbox.y, hitbox.width, hitbox.height);
}
}
Entity.java
package dodge;
import java.awt.*;
public abstract class Entity {
protected int x, y, w, h;
protected boolean removed = false;
public Entity(int x, int y){
this.x = x;
this.y = y;
}
public void draw(Graphics g){
}
public int getX() {return x;}
public int getY(){return y;}
public int getW() {return w;}
public int getH(){return h;}
}
Player.java
package dodge;
import java.awt.*;
public class Player extends Entity{
private int xd, yd;
private Dodge instance;
private Rectangle hitbox;
public Player(Dodge instance, int x , int y){
super(x, y);
this.instance = instance;
w = 16; h = 16;
hitbox = new Rectangle(x, y, w, h);
}
public void draw(Graphics g){
move();
if(!instance.getStage().isCollided(hitbox)){
yd = 1;
}else yd = 0;
g.setColor(Color.ORANGE);
g.fillOval(hitbox.x, hitbox.y, hitbox.width, hitbox.height);
}
private void move(){
hitbox.x += xd;
hitbox.y +=yd;
}
public void setXD(int value){
xd = value;
}
public void setYD(int value){
yd = value;
}
}
Stage.java
package dodge;
import java.awt.*;
public class Stage {
private Rectangle platform = new Rectangle(0, 540, 800, 100);
public Stage(){
}
public boolean isCollided(Rectangle entity){
return platform.intersects(entity);
}
public void draw(Graphics g){
g.setColor(Color.WHITE);
g.fillRect(platform.x, platform.y, platform.width, platform.height);
}
}
I hope that wasnt too long. Thanks

Read the stacktrace. Either Enemy.instance or Enemy.instance.getStage() is null.

Also the screen just goes white whenever i run the program.
Don't override update() and paint(). That is old code used in AWT and is NOT the way custom painting is done in Swing. In Swing you simply override the paintComponent() method of the JPanel and don't forget to invoke super.paintComponent(). Read the section from the Swing tutorial on Custom Painting for more information and examples.
Don't invoke repaint() in a painting method. That will cause an infinite loop. The is not the way to do animation. Use a Swing Timer or a separate Thread to schedule the animation.

Related

I am not able to draw after clear my jpanel

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.border.BevelBorder;
public class Cliente extends JPanel {
private JButton adelanteButton;
private JButton undoButton;
private JPanel panel1;
private JPanel panelDibujo;
private Rectangulo rectangulo = new Rectangulo();
Originator originator;
Caretaker caretaker;
public static void main(String[] args) {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("Patron Memento");
Cliente cliente = new Cliente();
frame.setContentPane(cliente.panel1);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Memento m : caretaker.history) {
System.out.println("forcitooooooo");
dibujar(m.getState(), Color.GREEN);
}
}
public Cliente() {
caretaker = new Caretaker();
originator = new Originator();
createUIComponents();
undoButton.addActionListener(e -> {
caretaker.anterior();
panelDibujo.repaint();
});
panelDibujo.setBackground(Color.WHITE);
panelDibujo.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
panelDibujo.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
MousePressedEvent(e);
}
#Override
public void mouseReleased(MouseEvent e) {
MouseReleasedEvent(e);
}
});
}
private void createUIComponents() {
}
private void MousePressedEvent(MouseEvent e){
rectangulo.setX1(e.getX());
rectangulo.setY1(e.getY());
}
private void MouseReleasedEvent(MouseEvent e){
rectangulo.setX2(e.getX());
rectangulo.setY2(e.getY());
originator.setState(rectangulo);
dibujar(rectangulo, Color.orange);
caretaker.addMemento(originator.CreateMemento());
rectangulo = new Rectangulo();
}
public void dibujar(Rectangulo r, Color c) {
Graphics g = panelDibujo.getGraphics();
g.setColor(c);
g.drawRect(r.getX1(), r.getY1(), r.getX2() - r.getX1(), r.getY2() - r.getY1());
g.dispose();
}
}
Hello i am applying the memento pattern by using a jpanel and drawing some rectangles inside, right now my code is working fine about drawing with the mouse events, but the issue is when i try to undo. My logic so far is clear the jpanel and redo all the rectangles minus the last one.
But after clearing my jpanel is not drawing again :( can someone help me to fix it? thank you
Caretaker.java
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Stack;
public class Caretaker {
LinkedList<Memento> history;
int indice;
public Caretaker(){
history = new LinkedList<>();
indice = 0;
}
void addMemento(Memento m){
if (history.size() == indice || history.isEmpty()){
indice++;
history.add(m);
}else {
indice = history.size()-1;
history.subList(indice +1, history.size()).clear();
}
}
Memento anterior(){
if (history.size() > 0){
indice--;
return history.removeLast();
}
JOptionPane.showMessageDialog(null, "No hay mas wey!");
return null;
}
Memento siguiente(){
if (indice < history.size()+1){
indice++;
return history.get(indice+1);
}
JOptionPane.showMessageDialog(null, "No hay mas wey!");
return null;
}
public void redibujar(JPanel f){
Graphics g = f.getGraphics();
for (Memento m: history) {
g.drawRect(m.getState().getX1(), m.getState().getY1(), m.getState().getX2() - m.getState().getX1(), m.getState().getY2() - m.getState().getY1());
g.dispose();
}
}
public void clear(){
history.clear();
indice = 0;
}
}
Memento.java
public class Memento {
Rectangulo state;
/*
Constructor, unica manera de mandar/crear/guardar los datos
*/
public Memento(Rectangulo state) {
this.state = state;
}
public Memento(){
state = new Rectangulo();
}
Rectangulo getState(){
return state;
}
}
Originator.java
public class Originator {
Rectangulo state;
public void setState(Rectangulo rectangulo){
this.state = rectangulo;
}
public Memento CreateMemento(){
return new Memento(state);
}
public void setMemento(Memento m) {
setState(m.getState());
}
}
Rectangulo.java
import java.awt.*;
public class Rectangulo {
private int x1;
private int x2;
private int y1;
private int y2;
Rectangulo(){
}
public Rectangulo(int x1, int x2, int y1, int y2) {
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
}
public int getX1() {
return x1;
}
public void setX1(int x1) {
this.x1 = x1;
}
public int getX2() {
return x2;
}
public void setX2(int x2) {
this.x2 = x2;
}
public int getY1() {
return y1;
}
public void setY1(int y1) {
this.y1 = y1;
}
public int getY2() {
return y2;
}
public void setY2(int y2) {
this.y2 = y2;
}
public void draw(Graphics g){
g.setColor(Color.orange);
g.drawRect(this.getX1(), this.getY1(), this.getX2() - this.getX1(), this.getY2() - this.getY1());
}
}
Again, you're not drawing correctly. You are trying to render using a JPanel, cliente, that is never added to the GUI, and you're trying to use a Graphics object that is extracted from this unrendered component, making the Graphics object thus obtained short-lived and unstable.
Instead, do all drawing in the paintComponent method. You can use a BufferedImage and draw that in paintComponent if desired, especially if you want images with objects that show different colors, or you can use your List (here a LinkedList, but ArrayList will work) and draw in paintComponent. For instance, something simple like:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class Cliente2 extends JPanel {
private static final int BI_WIDTH = 800;
private static final int BI_HEIGHT = 650;
private Rectangle drawingRect = null;
// private java.util.List<Rectangulo> rectangulos = new ArrayList<>();
List<Rectangulo2> rectangulos = new ArrayList<>();
private JButton clearBtn = new JButton("Clear");
public Cliente2() {
setBackground(Color.WHITE);
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
clearBtn.addActionListener(e -> clear());
add(clearBtn);
}
private void clear() {
rectangulos.clear();
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(BI_WIDTH, BI_HEIGHT);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
if (drawingRect != null) {
g2.setColor(Color.LIGHT_GRAY);
g2.draw(drawingRect);
}
for (Rectangulo2 rectangulo : rectangulos) {
rectangulo.draw(g2);
}
}
private class MyMouse extends MouseAdapter {
private Point p0;
#Override
public void mousePressed(MouseEvent e) {
p0 = e.getPoint();
}
#Override
public void mouseDragged(MouseEvent e) {
drawingRect = createDrawingRect(e);
repaint();
}
private Rectangle createDrawingRect(MouseEvent e) {
Point p1 = e.getPoint();
int x = Math.min(p0.x, p1.x);
int y = Math.min(p0.y, p1.y);
int width = Math.abs(p0.x - p1.x);
int height = Math.abs(p0.y - p1.y);
return new Rectangle(x, y, width, height);
}
#Override
public void mouseReleased(MouseEvent e) {
// lets create some random colors:
float hue = (float) Math.random();
float brightness = (float) Math.random();
Color color = Color.getHSBColor(hue, 1f, brightness);
Rectangulo2 rectangulo = new Rectangulo2(color, p0, e.getPoint());
rectangulos.add(rectangulo);
drawingRect = null;
repaint();
}
}
public static void main(String[] args) {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("Patron Memento");
// Cliente cliente = new Cliente();
// frame.setContentPane(cliente.panel1);
frame.add(new Cliente2());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
class Rectangulo2 {
private static final Stroke STROKE = new BasicStroke(3f);
private Color color;
private Point p0, p1;
public Rectangulo2() {
}
public Rectangulo2(Color color, Point p0, Point p1) {
super();
this.color = color;
this.p0 = p0;
this.p1 = p1;
}
public Color getColor() {
return color;
}
public Point getP0() {
return p0;
}
public Point getP1() {
return p1;
}
public void draw(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setColor(color);
int x = Math.min(p0.x, p1.x);
int y = Math.min(p0.y, p1.y);
int width = Math.abs(p0.x - p1.x);
int height = Math.abs(p0.y - p1.y);
g2.fillRect(x, y, width, height);
g2.setStroke(STROKE);
g2.setColor(Color.BLACK);
g2.drawRect(x, y, width, height);
g2.dispose();
}
}

Why is java copying instead of creating a new Instant When i use different variables?

I made a fairly simple code and i got into an error which confused me.
So I have a class that creates two totally different variables and creating them using the new keyword
Player playerLeft = new Player(5,150);
Player playerRight = new Player( 150,150);
Player class:
import javax.swing.*;
import java.awt.*;
public class Player extends JComponent {
private int posY;
private int posX;
public Player(int x, int y) {
posX = x;
posY = y;
//repaint();
}
public float getMovementY() {
return movementY;
}
public void setMovementY(int movementY) {
this.movementY = movementY;
}
int movementY = 0;
public void paintComponent(Graphics g) {
Graphics2D _g2 = (Graphics2D) g;
Rectangle rect = new Rectangle(posX, posY, 20, 150);
_g2.fill(rect);
}
public void setLocation(int x, int y) {
posY = y;
posX = x;
repaint();
}
public void move() {
setLocation(posX, posY + movementY);
}
}
It's probably me not knowing something about Java but for me when I try to instantiate playerRight it just overwrites player left and drawsOut playerRight only.
Here is the complete code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Timer;
import java.util.TimerTask;
public class mainJFrame extends JFrame implements KeyListener {
int relativeTimeMillsec = 0;
Player playerLeft = new Player(5, 150);
Player playerRight = new Player(150, 150);
Timer timer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
relativeTimeMillsec++;
refreshTimeText(relativeTimeMillsec);
calcMovements();
}
};
//components
JLabel timeCounterLabel = new JLabel("Time: " + 0, SwingConstants.CENTER);
public mainJFrame() {
createComponents();
addKeyListener(this);
}
public void createComponents() {
this.setTitle("The title");
this.setSize(800, 600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
timer.scheduleAtFixedRate(task, 0, 10);
JButton testButton = new JButton("Label");
testButton.setSize(100, 25);
testButton.setLocation(this.getWidth() / 2 - testButton.getWidth() / 2, this.getHeight() / 2 - testButton.getHeight() / 2);
timeCounterLabel.setSize(200, 25);
timeCounterLabel.setLocation(this.getWidth() / 2 - timeCounterLabel.getWidth() / 2, 10);
//playerRight = new Player(this.getWidth()-45,this.getHeight()/2);
// this.add(testButton);
this.add(timeCounterLabel);
this.add(playerLeft);
this.add(playerRight);
}
public void paintComponent(Graphics g) {
{
super.repaint();
}
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_S) {
playerLeft.movementY = +2;
} else if (e.getKeyCode() == KeyEvent.VK_W) {
playerLeft.movementY = -2;
}
if (e.getKeyCode() == KeyEvent.VK_UP) {
playerRight.movementY = +2;
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
playerRight.movementY = -2;
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
private double calcRealRelativeTime(int _relTime) {
return relativeTimeMillsec / (double) 100;
}
private void refreshTimeText(int _relTime) {
timeCounterLabel.setText("Time: " + Math.round(calcRealRelativeTime(_relTime)));
}
private void calcMovements() {
playerLeft.move();
playerRight.move();
}
}
Understand that a JFrame's contentPane (the container that holds its components) uses BorderLayout by default, and this code:
this.add(timeCounterLabel);
this.add(playerLeft);
this.add(playerRight);
is adding all components to the same default BorderLayout.CENTER position, meaning any components added will replace components added previously.
But more importantly, yours is a common problem and stems from your having your Player class extend from a GUI component. Don't do this, as then you will have a great deal of difficulty drawing multiple Player objects and having them interact easily (as you're finding out). Instead have Player be a logical (non-component) class, and have only one class extend JPanel and do all the drawing. This class can hold Player objects, perhaps held in a collection such as an ArrayList<Player>, and then iterate through the collection within its paintComponent method.
Other issues:
Do not use java.util.Timer and java.util.TimerTask for Swing animations since these classes do not follow Swing threading rules. Use instead a javax.swing.Timer.
Learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others
If/when you do override a painting method such as paintComponent, be sure to call the super's method within your override, usually on the first line, so as not to break the painting chain. Also, use the #Override annotation before this method and any other methods that you think that you may be overriding so that the compiler catches possible errors with this.
For example (but not a complete example)
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.*;
#SuppressWarnings("serial")
public class SimpleAnimation extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 600;
private static final int TIMER_DELAY = 20;
private Player2 playerLeft = new Player2(5, 150, Color.RED);
private Player2 playerRight = new Player2(150, 150, Color.BLUE);
public SimpleAnimation() {
playerLeft.setySpeed(1);
playerRight.setySpeed(-1);
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
playerLeft.draw(g);
playerRight.draw(g);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
playerRight.move();
playerLeft.move();
repaint();
}
}
private static void createAndShowGui() {
SimpleAnimation mainPanel = new SimpleAnimation();
JFrame frame = new JFrame("SimpleAnimation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class Player2 {
private static final int RECT_WIDTH = 20;
private static final int RECT_HEIGHT = 50;
private int x;
private int y;
private int ySpeed;
private Color color;
public Player2(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setySpeed(int ySpeed) {
this.ySpeed = ySpeed;
}
public int getySpeed() {
return ySpeed;
}
public void setLocation(int x, int y) {
setX(x);
setY(y);
}
public void move() {
setLocation(x, y + ySpeed);
}
public void draw(Graphics g) {
g.setColor(color);
g.fillRect(x, y, RECT_WIDTH, RECT_HEIGHT);
}
}

JPanel size is not known on start

When I create a Board instance from my Square instance, I try to assign the size of the window to the integers x and y. I fail to do this because it seems like on start the size is 0. In the constructor in Board.java, x and y shouldn't be -50 like they end up now.
Square.java:
package Square;
import javax.swing.*;
public class Square extends JFrame {
public Square(){
add(new Board());
setSize(800, 800);
setVisible(true);
}
public static void main(String[] args){
new Square();
}
}
Board.java
package Square;
import javax.swing.*;
import java.awt.*;
public class Board extends JPanel{
int x,y;
public Board(){
x = width-50;
y = height-50;
}
public int width = (int) getSize().getWidth();
public int height = (int) getSize().getHeight();
public void paintComponent(Graphics g){
super.paintComponent(g);
g.fillRect(x,y, 100, 100);
}
}
Full Code for clarification:
Square.java
package Square;
import javax.swing.*;
public class Square extends JFrame {
public Square(){
Board board = new Board();
board.start();
add(board);
setTitle("Square");
setSize(800, 800);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args){
Square square = new Square();
square.setVisible(true);
square.setLocation(2000, 150);
}
}
Board.java
package Square;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Board extends JPanel implements ActionListener{
Timer timer;
int x, y;
int velX = 0;
int velY = 0;
public Board(){
setFocusable(true);
timer = new Timer(1, this);
addKeyListener(new TAdapter());
}
class TAdapter extends KeyAdapter{
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
switch(keyCode){
case KeyEvent.VK_ESCAPE: x = width()/2-50; y = height()/2-50; break;
case KeyEvent.VK_RIGHT: velX = 1; break;
case KeyEvent.VK_DOWN: velY = 1; break;
case KeyEvent.VK_LEFT: velX = -1; break;
case KeyEvent.VK_UP: velY = -1; break;
}
}
public void keyReleased(KeyEvent e){
velX = 0;
velY = 0;
}
}
public int width(){ return (int) getSize().getWidth();}
public int height(){ return (int) getSize().getHeight();}
public void start(){
timer.setInitialDelay(100);
timer.start();
x = width()/2-50;
y = height()/2-50;
}
#Override
public void actionPerformed(ActionEvent e) {
x += velX;
y += velY;
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(x,y, 100, 100);
}
}
Put the calculation into your paintComponent method. In general you want to avoid having code in paintComponent that will slow it down, but these calls shouldn't give too much penalty. Also, if your program is re-sizable, you'll need to be able to re-calculate the sizes of things on the go like this:
public void paintComponent(Graphics g){
super.paintComponent(g);
int x = getWidth() - 50;
int y = getHeight() - 50;
g.fillRect(x, y, 100, 100);
}
but of course in your real program, you will want to avoid "magic" numbers
Another issue: don't call setSize() on your JFrame. Instead, if you want to specify a hard size, do so in the JPanel by overriding its getPreferredSize() method. This will also then give you the suggested parameters that can be used for your calculations.
For example:
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.*;
#SuppressWarnings("serial")
public class DrawRect extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = PREF_W;
private static final int DELTA = 50;
private static final Color RECT_COLOR = Color.red;
private static final int RECT_WIDTH = 100;
private static final int TIMER_DELAY = 15;
private int rectX = PREF_W - DELTA;
private int rectY = PREF_H - DELTA;
public DrawRect() {
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(RECT_COLOR);
g.fillRect(rectX, rectY, RECT_WIDTH, RECT_WIDTH);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
rectX--;
rectY--;
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("DrawRect");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawRect());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Also, check out the key bindings animation code from this answer of mine.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
public class GamePanel extends JPanel {
private static final int ANIMATION_DELAY = 15;
private final int HEIGHT = 400;
private final int WIDTH = 600;
private Square square;
private EnumMap<Direction, Boolean> dirMap = new EnumMap<>(Direction.class);
private Map<Integer, Direction> keyToDir = new HashMap<>();
// !! private Circle circle;
private Timer animationTimer;
public GamePanel() {
for (Direction dir : Direction.values()) {
dirMap.put(dir, Boolean.FALSE);
}
keyToDir.put(KeyEvent.VK_UP, Direction.UP);
keyToDir.put(KeyEvent.VK_DOWN, Direction.DOWN);
keyToDir.put(KeyEvent.VK_LEFT, Direction.LEFT);
keyToDir.put(KeyEvent.VK_RIGHT, Direction.RIGHT);
// !! addKeyListener(new DirectionListener());
setKeyBindings();
setBackground(Color.white);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
square = new Square();
animationTimer = new Timer(ANIMATION_DELAY, new AnimationListener());
animationTimer.start();
}
private void setKeyBindings() {
int condition = WHEN_IN_FOCUSED_WINDOW;
final InputMap inputMap = getInputMap(condition);
final ActionMap actionMap = getActionMap();
boolean[] keyPressed = { true, false };
for (Integer keyCode : keyToDir.keySet()) {
Direction dir = keyToDir.get(keyCode);
for (boolean onKeyPress : keyPressed) {
boolean onKeyRelease = !onKeyPress; // to make it clear how bindings work
KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, 0,
onKeyRelease);
Object key = keyStroke.toString();
inputMap.put(keyStroke, key);
actionMap.put(key, new KeyBindingsAction(dir, onKeyPress));
}
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
square.display(g);
}
private class AnimationListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent evt) {
boolean repaint = false;
for (Direction dir : Direction.values()) {
if (dirMap.get(dir)) {
square.move(dir);
repaint = true;
}
}
if (repaint) {
repaint();
}
}
}
private class KeyBindingsAction extends AbstractAction {
private Direction dir;
boolean pressed;
public KeyBindingsAction(Direction dir, boolean pressed) {
this.dir = dir;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent evt) {
dirMap.put(dir, pressed);
}
}
private static void createAndShowGUI() {
GamePanel gamePanel = new GamePanel();
JFrame frame = new JFrame("GamePanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
gamePanel.requestFocusInWindow();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
enum Direction {
UP(0, -1), DOWN(0, 1), LEFT(-1, 0), RIGHT(1, 0);
private int incrX;
private int incrY;
private Direction(int incrX, int incrY) {
this.incrX = incrX;
this.incrY = incrY;
}
public int getIncrX() {
return incrX;
}
public int getIncrY() {
return incrY;
}
}
class Square {
private int x = 0;
private int y = 0;
private int w = 20;
private int h = w;
private int step = 1;
private Color color = Color.red;
private Color fillColor = new Color(255, 150, 150);
private Stroke stroke = new BasicStroke(3f, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND);
public void display(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(fillColor);
g2d.fillRect(x, y, w, h);
g2d.setStroke(stroke);
g2d.setColor(color);
g2d.drawRect(x, y, w, h);
g2d.dispose();
}
public void setStep(int step) {
this.step = step;
}
public void move(Direction dir) {
x += step * dir.getIncrX();
y += step * dir.getIncrY();
}
}
Hey your codestyle is horrible, but i try to help :). You can't get a size of an undrawn window. First Things first, your Constructor is wrong, you add the Board that actually create the Board Obj. Calling the Constructor of Board, which has no drawn parent yet and no x,y set. Try to initialize your variables in the Constructor. So just use width and height and fill the values in the constructor. Next, just tell your board its creation size by passing its parent size trough constructor variables.
I think you try to learn java and this is much more elegant. Furthermore, try to do all parent modification before adding some to it. So first setSize, add some Layout (Border/Flow/whatuwish) then get the frames ContentPane and add your Board component. To make things clear, you can't get e.g. the parent and parent size in Contructor because your board Obj isn't created and added yet. If you wish to getParent() and its size, create the Object add it to JFrame and than you can call getParent().getSize(). You get 0 because your JPanel isn't drawn at this time (before creation). If you wish to get the Parent Size just pass the JFrame Ref to Constructor or its size. Another Advise, don't create things in things in things, keep in mind with your code you create your JPanel as first Obj... Here is some example code:
Square:
public class Square extends JFrame {
public static void main(String[] args){
Square square = new Square();
square.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Dimension d = new Dimension(800,800);
square.setPreferredSize(d);
square.setSize(d);
//too much, every Jframe has BorderLayout enabled
square.getContentPane().setLayout(new BorderLayout());
square.getContentPane().add(new Board(square), BorderLayout.CENTER);
square.pack();
square.setVisible(true);
}
}
Board:
public class Board extends JPanel{
int x,y;
JFrame parent;
public Board(JFrame parent){
int width = parent.getPreferredSize().width;
int height = parent.getPreferredSize().height;
x = width-50;
y = height-50;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.fillRect(x,y, 100, 100);
}
}
You can take x and y values after the panel has become visible, in the next EDT cycle, by using SwingUtilities.invokeLater, for example.

Graphical object does not update as the xPosition updates

I am trying to create a planet (blue circle), and have it move when i update the x-position. Here's the main class.
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
public class Main extends Canvas implements Runnable{
public int width = 1400;
public int height = (width/16)* 9;
Dimension dim = new Dimension(width, height);
JFrame frame;
boolean running;
NewBody earth;
public Main(){
this.setPreferredSize(dim);
this.setBackground(Color.BLACK);
}
public void start(){
running = true;
Thread thread = new Thread(this, "display");
thread.start();
}
public void run(){
long startTime = System.currentTimeMillis();
double conv = Math.pow(10, 3);
while(running){
long now = System.currentTimeMillis();
if((now-startTime)/conv >= 1){
earth.incXPos();
startTime = now;
return;
}
update();
}
}
public void update(){
repaint();
}
public void stop(){
running = false;
}
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(Color.BLUE);
g2d.fillOval(earth.xPos,earth.yPos, earth.radius*2, earth.radius*2);
}
public static void main(String args[]){
Main main = new Main();
main.frame = new JFrame();
main.frame.setResizable(false);
main.frame.add(main);
main.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.frame.pack();
main.frame.setVisible(true);
main.earth = new NewBody(0, 0,0, 50);
main.start();
}
}
And here is the NewBody blueprint, from which I am creating "earth"
public class NewBody {
Main main = new Main();
public int xOrigo = 1400/2;
public int yOrigo = 800/2;
public double mass;
public double velocity;
public int xPos;
public int yPos;
public double force;
public double vectorAngle;
public double fx;
public double fy;
public double acceleration;
public int radius;
public NewBody(double mass, int xPos, int yPos, int radius){
this.mass = mass;
this.xPos = xOrigo + xPos - radius;
this.yPos = yOrigo + yPos - radius;
this.radius = radius;
}
public void incXPos(){
this.xPos++;
}
The problem is that when I run the program, the blue circle just stays in the same position, where it was initialized. It just flickers extremely fast, and nothing else happens. I am quite new to coding, and I do not seem to get any error message and therefore I do not know how to proceed. I have been stuck on this for some hours now.
Do you have any ideas?
The return; statement in your run() method causes the method short circuits exit and thus quit immediately after calling incXPos() just once. This occurs even before update() is called and so repaint() is never called.
I'd do things a bit differently though:
I'd draw in a JPanel
I'd draw in its paintComponent method.
I'd use a Swing Timer instead of a Thread to do my animation loop.
I'd be sure to call the super's paintComponent(g) inside of my override.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class SimpleAnimation extends JPanel {
private static final int PREF_W = 1400;
private static final int PREF_H = (PREF_W * 9) / 16; // do int mult **first**
private static final int TIMER_DELAY = 13;
private NewBody earth = new NewBody(0, 0, 0, 50);
public SimpleAnimation() {
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
// to allow for smooth graphics
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.BLUE);
g2d.fillOval(earth.xPos, earth.yPos, earth.radius * 2, earth.radius * 2);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
earth.incXPos();
repaint();
}
}
private static void createAndShowGui() {
SimpleAnimation mainPanel = new SimpleAnimation();
JFrame frame = new JFrame("SimpleAnimation");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class NewBody {
// !! Main main = new Main();
public int xOrigo = 1400 / 2;
public int yOrigo = 800 / 2;
public double mass;
public double velocity;
public int xPos;
public int yPos;
public double force;
public double vectorAngle;
public double fx;
public double fy;
public double acceleration;
public int radius;
public NewBody(double mass, int xPos, int yPos, int radius) {
this.mass = mass;
this.xPos = xOrigo + xPos - radius;
this.yPos = yOrigo + yPos - radius;
this.radius = radius;
}
public void incXPos() {
this.xPos++;
}
}

Yet another KeyListener/KeyBinding Issue

Ugh, I'm sorry MadProgrammer but I just couldn't get the KeyBinding to work the way I wanted it to :(. But I'll keep looking at some more tutorials till I figure it out. For now though I've stuck to a KeyListener and it works. But now I'm having an issue where p.move(); doesn't actually move the player. All other code I've put in works fine except p.move();. I probably shouldn't be asking this many questions, so if you want me to stop just say so, but the whole SO community is really nice. Again, I'll post the code.
Main class:
import javax.swing.*;
public class Game extends JFrame{
public static void main(String[] args){
new Game();
}
public Game(){
add(new Board());
setTitle("Hi mom");
setSize(555,330);
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(3);
setVisible(true);
}
}
Board Class:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Board extends JPanel implements ActionListener {
Image background;
Player p;
boolean moving;
public Board() {
setFocusable(true);
requestFocus();
addKeyListener(new KeyInputEvents());
Timer timer = new Timer(25, this);
timer.start();
ImageIcon img = new ImageIcon(getClass().getResource("images/map.png"));
background = img.getImage();
p = new Player();
}
public void paint(Graphics g) {
g.drawImage(background, 0, 0, null);
g.drawImage(p.getPlayer(), p.getX(), p.getY(), null);
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public JPanel getBoard(){
return this;
}
)
Player Class (This is probably where stuff goes wrong):
import javax.swing.*;
import java.awt.*;
public class Player{
int x = 30;
int y = 187;
Image player;
public Player(){
ImageIcon img = new ImageIcon(getClass().getResource("images/player.png"));
player = img.getImage();
}
public Image getPlayer(){
return player;
}
public void move(int x, int y){
this.x += x;
this.y += y;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
)
KeyInputEvents Class:
import java.awt.event.*;
import javax.swing.*;
public class KeyInputEvents extends KeyAdapter implements ActionListener{
int k;
boolean moving = true;
Player p = new Player();
public KeyInputEvents(){
Timer timer = new Timer(25,this);
timer.start();
}
public void keyPressed(KeyEvent e){
k = e.getKeyCode();
moving = true;
}
public void keyReleased(KeyEvent e){
moving = false;
}
public void actionPerformed(ActionEvent e) {
if(k == 'D' && moving == true){p.move(5,0);}
if(k == 'A' && moving == true){p.move(-5,0);}
}
}
The Player that is on the screen is not the same Player you are moving in your KeyInputEvents class...
In Board you create an instance of Player...
public class Board extends JPanel implements ActionListener {
Player p;
public Board() {
//...
p = new Player();
}
And in KeyInputEvents you create another one...
public class KeyInputEvents extends KeyAdapter implements ActionListener {
//....
Player p = new Player();
These two instances are in no way related...
While I'm here, you shouldn't really override paint, but instead, override paintComponent and you should definitely be calling super.paintXxx
Update with a Key Bindings example
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
public class Game extends JFrame {
public static void main(String[] args) {
new Game();
}
public Game() {
add(new Board());
setTitle("Hi mom");
setSize(555, 330);
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(3);
setVisible(true);
}
public class Board extends JPanel implements ActionListener {
Image background;
Player p;
private int xDelta, yDelta;
public Board() {
setFocusable(true);
requestFocus();
ImageIcon img = new ImageIcon(getClass().getResource("/images/map.jpg"));
background = img.getImage();
p = new Player();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Left.move");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Left.stop");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Right.move");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Right.stop");
ActionMap am = getActionMap();
am.put("Left.move", new MoveLeft(this));
am.put("Left.stop", new StopAllMovement(this));
am.put("Right.move", new MoveRight(this));
am.put("Right.stop", new StopAllMovement(this));
Timer timer = new Timer(25, this);
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
g.drawImage(background, 0, 0, null);
g.drawImage(p.getPlayer(), p.getX(), p.getY(), null);
}
protected void setMovement(int xDelta, int yDelta) {
this.xDelta = xDelta;
this.yDelta = yDelta;
}
public void actionPerformed(ActionEvent e) {
p.move(xDelta, yDelta);
repaint();
}
public JPanel getBoard() {
return this;
}
}
public class Player {
int x = 30;
int y = 187;
Image player;
public Player() {
ImageIcon img = new ImageIcon(getClass().getResource("/images/player.png"));
player = img.getImage();
}
public Image getPlayer() {
return player;
}
public void move(int x, int y) {
this.x += x;
this.y += y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
public abstract class AbstractMove extends AbstractAction {
private Board board;
private int xDelta;
private int yDelta;
public AbstractMove(Board board, int xDelta, int yDelta) {
this.board = board;
this.xDelta = xDelta;
this.yDelta = yDelta;
}
#Override
public void actionPerformed(ActionEvent e) {
board.setMovement(xDelta, yDelta);
}
}
public class MoveLeft extends AbstractMove {
public MoveLeft(Board board) {
super(board, -5, 0);
}
}
public class MoveRight extends AbstractMove {
public MoveRight(Board board) {
super(board, 5, 0);
}
}
public class StopAllMovement extends AbstractMove {
public StopAllMovement(Board board) {
super(board, 0, 0);
}
}
}

Categories