-1

So i got this class that inherited from JPanel and a Thread that instantiates a Image object and making it move horizontally. The problem is the Image is just drawn using the Graphics class and the drawImage() method but stays still and not moving.

MyJPanel.java

import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class MyJPanel extends JPanel implements ActionListener,Runnable
{
    private static final long serialVersionUID = 1L;

    private Timer timer;
    private Image backgroundImage;
    private Image player;
    private int playerX, playerY;
    private int projectileX,projectileY;
    private Image projectileImage;
    private ArrayList<Image> projectiles = new ArrayList<Image>();

    boolean flag = false;

    public MyJPanel(Image backgroundImage, Image player,Image projectileImage)
    {
        this.backgroundImage = backgroundImage;
        this.player = player;
        this.projectileImage = projectileImage;
        this.setLayout(null);

        timer = new Timer(50, this);
        timer.start();

        this.addKeyListener(new KeyAdapter() // Listens for a keyboard event
        {
            public void keyPressed(KeyEvent e) 
            {
                if (e.getKeyCode() == KeyEvent.VK_SPACE) // If pressing space - shoot
                {
                    ProjectileThread projectileThread = new ProjectileThread(playerX,playerY);
                    projectileThread.start(); // Create a Thread and call run() method
                }
                repaint();
            }
        });

        // Mouse listener
        this.addMouseMotionListener(new MouseMotionListener()
        {   
            @Override
            public void mouseMoved(MouseEvent e)
            {
                playerX = e.getX();
                playerY = e.getY();
            }

            @Override
            public void mouseDragged(MouseEvent e)
            {

            }
        });
        hideMouseCursor();

        this.setFocusable(true);
        this.setVisible(true);
    } // End of JPanel constructor

    public void paintComponent(Graphics graphics)
    {
        super.paintComponent(graphics);

        graphics.drawImage(backgroundImage,0,0,this.getWidth(),this.getHeight(),null); // Draw the background
        graphics.drawImage(player,playerX,playerY,null); // Draw the player
        graphics.drawImage(projectileImage,projectileX,projectileY,null);
    }

    public void moveProjectile()
    {
        while (projectileX < this.getWidth())
        {
            this.projectileX += 2;
        }
    }

    public void  hideMouseCursor() // Hides the mouse cursor
    {
        //Transparent 16 x 16 pixel cursor image.
        BufferedImage cursorbackgroundImgage = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);

        // Create a new blank cursor.
        Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(
                cursorbackgroundImgage, new Point(0, 0), "Blank Cursor");

        // Set the blank cursor to the JPanel.
        this.setCursor(blankCursor);
    }

    @Override
    public void actionPerformed(ActionEvent actionEvent) // Without the method and the repaint() the mouse listener will not work 
    {
        repaint();
    }

    public class ProjectileThread extends Thread
    {
        Graphics graphics;

        public ProjectileThread(int playerX,int playerY)
        {
            projectileX = playerX + player.getWidth(null);
            projectileY = playerY + player.getHeight(null) / 2;
        }
        @Override
        public void run()
        {
            graphics.drawImage(projectileImage,projectileX,projectileY,null);
            while (projectileX < getWidth())
            {
                projectileX += 2;
            }
            try 
            {
                Thread.sleep(500);
            } 
            catch (InterruptedException e) 
            {
                e.printStackTrace();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args)
    {
        JFrame frame = new JFrame("A Game by me");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setExtendedState(JFrame.MAXIMIZED_BOTH); // Making the frame take a full screen

        ImageIcon backgroundImageIcon = new ImageIcon("space_background_2.jpg");
        Image backgroundImgage = backgroundImageIcon.getImage();
        ImageIcon playerImageIcon = new ImageIcon("spaceship_1.png");
        Image playerImage = playerImageIcon.getImage();
        ImageIcon projectileIcon = new ImageIcon("spaceship_projectile_1.png");
        Image projectileImage = projectileIcon.getImage();  

        frame.add(new MyJPanel(backgroundImgage,playerImage,projectileImage));
        frame.setVisible(true);
    }

    @Override
    public void run() 
    {

    }
} // End of MyJPanel

there are some variables and Objects that i don't use so don't mind them please.

The CS:

Thread:

public class ProjectileThread extends Thread
    {
        Graphics graphics;

        public ProjectileThread(int playerX,int playerY)
        {
            projectileX = playerX + player.getWidth(null);
            projectileY = playerY + player.getHeight(null) / 2;
        }
        @Override
        public void run()
        {
            graphics.drawImage(projectileImage,projectileX,projectileY,null);
            while (projectileX < getWidth())
            {
                projectileX += 2;
            }
            try 
            {
                Thread.sleep(500);
            } 
            catch (InterruptedException e) 
            {
                e.printStackTrace();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
}

Starting the Thread each space bar click:

 this.addKeyListener(new KeyAdapter() // Listens for a keyboard event
        {
            public void keyPressed(KeyEvent e) 
            {
                if (e.getKeyCode() == KeyEvent.VK_SPACE) // If pressing space - shoot
                {
                    ProjectileThread projectileThread = new ProjectileThread(playerX,playerY);
                    projectileThread.start(); // Create a Thread and call run() method
                }
                repaint();
            }
        });

I have been to this problem for quiet a lot and i don't seem to recall why it is not moving if i clearly say this.projectileX += 2;?

Any suggestions would be very appreciated. Thanks.

God
  • 1,238
  • 2
  • 18
  • 45
  • 3
    Your code looks to be a set up for throwing lots of NullPointerExceptions. Consider searching this site for similar Swing animation questions to see how this should be done. I would never use a Graphics field as you're doing (for the risk of NPE being thrown and as this results in unstable drawings) and instead would draw within paintComponent as the tutorials and the examples to be found here will tell and show you. Also don't start a game loop each time the key listener is called. Instead use some type of game loop that runs and whose state is... – Hovercraft Full Of Eels Feb 20 '16 at 15:37
  • 2
    changed by key press, preferably using key bindings and not a keylistener. – Hovercraft Full Of Eels Feb 20 '16 at 15:37
  • 2
    Also, never extend Thread, but consider implementing Runnable instead, although in your situation neither is needed since you'll use a Swing Timer for your game loop. The timer should do much more than call `repaint()`. it should help move sprites that need moving as well. – Hovercraft Full Of Eels Feb 20 '16 at 15:40
  • @HovercraftFullOfEels Where do i put exactly my game loop? – God Feb 20 '16 at 17:43
  • @HovercraftFullOfEels Read about key bindings. I still want to stick with the `keyListener`. – God Feb 20 '16 at 17:48
  • @HovercraftFullOfEels And i can't see any question on the site that deals with my problem.. – God Feb 20 '16 at 17:55
  • 2
    `"Read about key bindings. I still want to stick with the keyListener"` -- then you will be going against Swing official recommendations and will forever be fighting focus issues. But it's your party. – Hovercraft Full Of Eels Feb 20 '16 at 18:54
  • 2
    `" And i can't see any question on the site that deals with my problem."` -- I find this hard to believe. Here are some similar posts answered by me, and there are many other similar questions to be found that are answered by others as well: [example 1](http://stackoverflow.com/a/8961998/522444), [example 2](http://stackoverflow.com/a/6887354/522444), [example 3](http://stackoverflow.com/a/12992048/522444), [example 4](http://stackoverflow.com/a/30226382/522444), [example 5](http://stackoverflow.com/a/27771379/522444). – Hovercraft Full Of Eels Feb 20 '16 at 19:04
  • 1
    The movement code should be part of the "main" or "game" loop, not another thread – MadProgrammer Feb 20 '16 at 20:35
  • 1
    Where does `ProjectileThread` get it's `Graphics` reference from? This is a very dangrous way to do painting – MadProgrammer Feb 20 '16 at 20:36
  • @HovercraftFullOfEels Ok thanks man. i will go over it. – God Feb 21 '16 at 06:31

1 Answers1

0

The answer:

Simply i needed to create a thread and add it to the list of threds(projectiles) with the x,y values I wanted each time i press the space bar. Then in the paintComponent() method i moved with foreach loop on the threads list and draw them. Also i added Thread.sleep(10) in the while loop that moves the projectile so it will not move too fast.

public class MyJPanel extends JPanel implements ActionListener
{
    private static final long serialVersionUID = 1L;

    private Timer timer;
    private Image backgroundImage;
    private Image player;
    private int playerX, playerY;
    private int projectileX,projectileY;
    private Image projectileImage;
    private ArrayList<ProjectileThread> projectiles = new ArrayList<ProjectileThread>();

    static boolean gameLoop = true;

    public MyJPanel(Image backgroundImage, Image player,Image projectileImage)
    {
        this.backgroundImage = backgroundImage;
        this.player = player;
        this.projectileImage = projectileImage;
        this.setLayout(null);

        timer = new Timer(50, this);
        timer.start();

        this.addKeyListener(new KeyAdapter() // Listens for a keyboard event
        {
            public void keyPressed(KeyEvent e) 
            {
                if (e.getKeyCode() == KeyEvent.VK_SPACE) // If pressing space - shoot
                {
                    ProjectileThread projectileThread = new ProjectileThread(playerX,playerY);
                    projectileThread.start(); // Create a Thread and call run() method
                    projectiles.add(projectileThread);
                    repaint();
                }
            }
        });

        // Mouse listener
        this.addMouseMotionListener(new MouseMotionListener()
        {   
            @Override
            public void mouseMoved(MouseEvent e)
            {
                playerX = e.getX();
                playerY = e.getY();
            }

            @Override
            public void mouseDragged(MouseEvent e)
            {

            }
        });
        hideMouseCursor();

        this.setFocusable(true);
        this.setVisible(true);
    } // End of JPanel constructor

    public int getX()
    {
        return this.projectileX;
    }

    public int getY()
    {
        return this.projectileX;
    }

    public void paintComponent(Graphics graphics)
    {
        super.paintComponent(graphics);

        graphics.drawImage(backgroundImage,0,0,this.getWidth(),this.getHeight(),null); // Draw the background
        graphics.drawImage(player,playerX,playerY,null); // Draw the player
        for (ProjectileThread projectileThread : projectiles)
        {
            graphics.drawImage(projectileImage,projectileThread.getX(),projectileThread.getY(),null);
        }
    }

    public void  hideMouseCursor() // Hides the mouse cursor
    {
        //Transparent 16 x 16 pixel cursor image.
        BufferedImage cursorbackgroundImgage = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);

        // Create a new blank cursor.
        Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(
                cursorbackgroundImgage, new Point(0, 0), "Blank Cursor");

        // Set the blank cursor to the JPanel.
        this.setCursor(blankCursor);
    }

    @Override
    public void actionPerformed(ActionEvent actionEvent) // Without the method and the repaint() the mouse listener will not work 
    {
        repaint();
    }

    public class ProjectileThread extends Thread
    {   
        private int projectileX;
        private int projectileY;

        public ProjectileThread(int playerX,int playerY)
        {
            this.projectileX = playerX + player.getWidth(null);
            this.projectileY = playerY + player.getHeight(null) / 2 - 28;
        }
        @Override
        public void run()
        {
            try 
            {
                while (projectileX < backgroundImage.getWidth(null) - projectileImage.getWidth(null))
                {
                    projectileX += 2;
                    Thread.sleep(10);
                    repaint(); // Goes to paintComponent() method
                }
                projectiles.remove(this); // Remove the thread from the list
            } 
            catch (InterruptedException e) 
            {
                e.printStackTrace();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }

        public int getX()
        {
            return this.projectileX;
        }

        public int getY()
        {
            return this.projectileY;
        }
    }

    public static void main(String[] args)
    {
        JFrame frame = new JFrame("A Game by me");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setExtendedState(JFrame.MAXIMIZED_BOTH); // Making the frame take a full screen

        ImageIcon backgroundImageIcon = new ImageIcon("space_background_2.jpg");
        Image backgroundImgage = backgroundImageIcon.getImage();
        ImageIcon playerImageIcon = new ImageIcon("spaceship_1.png");
        Image playerImage = playerImageIcon.getImage();
        ImageIcon projectileIcon = new ImageIcon("spaceship_projectile_1.png");
        Image projectileImage = projectileIcon.getImage();  

        frame.add(new MyJPanel(backgroundImgage,playerImage,projectileImage));
        frame.setVisible(true);

        while (gameLoop)
        {

        }
    }
} // End of MyJPanel
God
  • 1,238
  • 2
  • 18
  • 45
  • Don't just code dump. Explain what the solution is. – user1803551 Feb 21 '16 at 18:52
  • @user1803551 Took care of it. – God Feb 22 '16 at 05:14
  • 1
    Alright. I didn't get into the question and your answer, but 2 observations. (1) Use key bindings instead of key listeners. (2) Don't call `sleep` while in the EDT, you will make the whole GUI non-responsive. – user1803551 Feb 22 '16 at 14:48
  • @user1803551 Whats EDT? – God Feb 22 '16 at 14:49
  • 1
    The Event Dispatch Thread. It's the thread the GUI runs on. If you put it to sleep then the user can't issue any commands. See [the tutorial](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html). Also, [key bindings](http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html). – user1803551 Feb 22 '16 at 15:12
  • @user1803551 Ok thank you man. – God Feb 22 '16 at 15:42
  • And again, don't extend Thread but implement Runnable. Also you seem to have two game type loops and only need one. – Hovercraft Full Of Eels Feb 22 '16 at 16:35