简体   繁体   中英

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:

  1. Methods (ha) of programming sprite movement.
  2. Continuing to move the sprite without a trail.
  3. 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);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM