Component.setBounds invoking Component.repaint?

2020-02-02 02:46发布

So I'm making a game and I have the EnemyAI as well as the player and they both extend JPanel. The world has a null layout, so I'm using setBounds(); to "move" (I actually just move the world image) the entities (player and AI) around and position them properly. But when I add (what seems like, I tested for the smallest number possible) 5, it invokes repaint() completely on it's own. This causes the player to visually be walking in place. The more entities I add, the faster the interval gets (i.e. 5 entities invokes repaint() a lot slower than 500 does).

NOTE: window in the below class is just a JFrame.

Main Class:

public class Game(){
public static boolean fighting = false;
public static void startGame(){
    WorldPanel game = new WorldPanel();
    game.setPreferredSize(new Dimension(window.getWidth(), window.getHeight()));
    PlayerPane player = new PlayerPane(32,32, "Player 1");
    game.addKeyListener(new KeyListener(){

        public void keyPressed(KeyEvent arg0) {
            if(fighting == false){
                move(player, game, arg0.isShiftDown(), arg0.getKeyCode());
            } else {
                System.out.println("Fighting = " + fighting);
            }
        }
        @Override
        public void keyReleased(KeyEvent arg0) {    
            gameTimer.stop();
            player.setIndex(0);
            game.repaint();
        }
        @Override
        public void keyTyped(KeyEvent arg0) {
        }
    });
    window.add(game);
    game.setLayout(null);
    game.requestFocus();
    setImages(player, PLAYER_DOWN);
    player.setDrawY(349);
    player.setDrawX(493);
    player.updateBounds();
    game.add(player);
    entities.add(player);
    addEntities(game);
    redoWindow();
}
public static void updateEntityBounds(){
    PlayerPane player = null;
    EnemyAI enemy = null;
    for(int i = 0; i < entities.size(); i++){
        if(i == 0){
            player = (PlayerPane) entities.get(i);
        } else {
            enemy = (EnemyAI) entities.get(i);
            if(enemy.getBounds().intersects(player.getBounds())){
                startFight(i);
            }
        }
        if(player != null){
            player.updateBounds();
        }
        if(enemy != null){
            enemy.updateBounds();
        }
    }
}
public static void addEntities(WorldPanel game){
    EnemyAI enemies[] = new EnemyAI[5];
    for(int i = 0; i < enemies.length; i++){
        if(i%6 == 0){
            try{
                enemies[i] = new EnemyAI(32,32, ImageIO.read(new File("H:\\Java\\Game\\src\\res\\SlimeLv3Still.png")));
                enemies[i].start();
            }catch(IOException e){
                e.printStackTrace();
            }
        }else if (i % 2 == 0){
            try{
                enemies[i] = new EnemyAI(32,32, ImageIO.read(new File("H:\\Java\\Game\\src\\res\\SlimeLv2Still.png")));
                enemies[i].setEnX(enemies[i].getRandomX());
                enemies[i].setEnY(enemies[i].getRandomY());
                enemies[i].start();
            }catch(IOException e){
                e.printStackTrace();
            }
        } else {
            try{
                enemies[i] = new EnemyAI(32,32, ImageIO.read(new File("H:\\Java\\Game\\src\\res\\SlimeLv1Still.png")));
                enemies[i].setEnX(enemies[i].getRandomX());
                enemies[i].setEnY(enemies[i].getRandomY());
                enemies[i].start();
            }catch(IOException e){
                e.printStackTrace();
            }
        }
        game.add(enemies[i]);
        entities.add(enemies[i]);
    }
}
    public static void move(PlayerPane player, WorldPanel game, boolean shiftDown, int keyPressed){
     gameTimer = new Timer(50, new ActionListener(){
        public void actionPerformed(ActionEvent e){
            updateEntityBounds();
            redoWindow();
            gameTimer.stop();
        }
    });
    if(gameTimer.isRepeats()){
        gameTimer.setRepeats(false);
    }
        if(shiftDown){
            if(keyPressed == KeyEvent.VK_W && ((game.getImageY() == 0 && player.getDrawY() > 10) || player.getDrawY() >= 349)){
                player.setDrawY(player.getDrawY() - 2);
                setImages(player, PLAYER_UP);
                gameTimer.start();
            } else
            if(keyPressed == KeyEvent.VK_S && ((game.getImageY() == -3868 && player.getDrawY() < 681) || player.getDrawY() <= 349)){
                player.setDrawY(player.getDrawY() + 2);
                setImages(player, PLAYER_DOWN);
                gameTimer.start();
            } else
            if(keyPressed == KeyEvent.VK_A && ((game.getImageX() == 0 && player.getDrawX() > 10) || player.getDrawX() > 493 )){
                player.setDrawX(player.getDrawX() - 2);
                setImages(player, PLAYER_LEFT);
                gameTimer.start();
            } else
            if(keyPressed == KeyEvent.VK_D && ((game.getImageX() == -5126 && player.getDrawX() < player.getHeight() - 10) || player.getDrawX() < 493 )){
                player.setDrawX(player.getDrawX() + 2);
                setImages(player, PLAYER_RIGHT);
                gameTimer.start();
            } else
            if(keyPressed == KeyEvent.VK_W && game.getImageY() < 0){
                if(game.getImageY() == -1){
                    game.setImageY(game.getImageY() + 1);
                } else {
                    game.setImageY(game.getImageY() + 2);
                }
                for(int i = 1; i < entities.size(); i++){
                    EnemyAI enemy = (EnemyAI)entities.get(i);
                    enemy.setEnY(enemy.getEnY() + 2);
                }
                setImages(player, PLAYER_UP);
                gameTimer.start();
            } else
            if(keyPressed == KeyEvent.VK_A && game.getImageX() < 0){
                if(game.getImageX() == -1){
                    game.setImageX(game.getImageX() + 1);
                } else {
                    game.setImageX(game.getImageX() + 2);
                }
                for(int i = 1; i < entities.size(); i++){
                    EnemyAI enemy = (EnemyAI)entities.get(i);
                    enemy.setEnX(enemy.getEnX() + 2);
                }
                setImages(player, PLAYER_LEFT);
                gameTimer.start();
            } else
            if(keyPressed == KeyEvent.VK_S && game.getImageY() > -3868){
                if(game.getImageY() == -3867){
                    game.setImageY(game.getImageY() - 1);
                } else {
                    game.setImageY(game.getImageY() - 2);
                }
                for(int i = 1; i < entities.size(); i++){
                    EnemyAI enemy = (EnemyAI)entities.get(i);
                    enemy.setEnY(enemy.getEnY() - 2);
                }
                setImages(player, PLAYER_DOWN);
                gameTimer.start();
            } else
            if(keyPressed == KeyEvent.VK_D && game.getImageX() > -5126){
                if(game.getImageX() == -5125){
                    game.setImageX(game.getImageX() - 1);
                } else {
                    game.setImageX(game.getImageX() - 2);
                }
                for(int i = 1; i < entities.size(); i++){
                    EnemyAI enemy = (EnemyAI)entities.get(i);
                    enemy.setEnX(enemy.getEnX() - 2);
                }
                setImages(player, PLAYER_RIGHT);
                gameTimer.start();

            }
        } else {
            if(keyPressed == KeyEvent.VK_W && ((game.getImageY() == 0 && player.getDrawY() > 10) || player.getDrawY() > 349)){
                player.setDrawY(player.getDrawY() - 1);
                setImages(player, PLAYER_UP);
                gameTimer.start();
            } else
            if(keyPressed == KeyEvent.VK_S && ((game.getImageY() == -3868 && player.getDrawY() < 681) || player.getDrawY() < 349)){
                player.setDrawY(player.getDrawY() + 1);
                setImages(player, PLAYER_DOWN);
                gameTimer.start();
            } else
            if(keyPressed == KeyEvent.VK_A && ((game.getImageX() == 0 && player.getDrawX() > 10) || player.getDrawX() > 493 )){
                player.setDrawX(player.getDrawX() - 1);
                setImages(player, PLAYER_LEFT);
                gameTimer.start();
            } else
            if(keyPressed == KeyEvent.VK_D && ((game.getImageX() == -5126 && player.getDrawX() < player.getHeight() - 10) || player.getDrawX() < 493 )){
                player.setDrawX(player.getDrawX() + 1);
                setImages(player, PLAYER_RIGHT);
                gameTimer.start();
            } else
                if(keyPressed == KeyEvent.VK_W && game.getImageY() < 0){
                game.setImageY(game.getImageY() + 1);
                setImages(player, PLAYER_UP);
                for(int i = 1; i < entities.size(); i++){
                    EnemyAI enemy = (EnemyAI)entities.get(i);
                    enemy.setEnY(enemy.getEnY() + 1);
                }
                gameTimer.start();
            } else
            if(keyPressed == KeyEvent.VK_A && game.getImageX() < 0){
                game.setImageX(game.getImageX() + 1);
                setImages(player, PLAYER_LEFT);
                for(int i = 1; i < entities.size(); i++){
                    EnemyAI enemy = (EnemyAI)entities.get(i);
                    enemy.setEnX(enemy.getEnX() + 1);
                }
                gameTimer.start();

            } else
            if(keyPressed == KeyEvent.VK_S && game.getImageY() > -3868){
                game.setImageY(game.getImageY() - 1);
                setImages(player, PLAYER_DOWN);
                for(int i = 1; i < entities.size(); i++){
                    EnemyAI enemy = (EnemyAI)entities.get(i);
                    enemy.setEnY(enemy.getEnY() - 1);
                }
                gameTimer.start();                              
            } else
            if(keyPressed == KeyEvent.VK_D && game.getImageX() > -5126){
                game.setImageX(game.getImageX() - 1);
                setImages(player, PLAYER_RIGHT);
                for(int i = 1; i < entities.size(); i++){
                    EnemyAI enemy = (EnemyAI)entities.get(i);
                    enemy.setEnX(enemy.getEnX() - 1);
                }
                gameTimer.start();
            }
        }
    }
}

Player:

public class PlayerPane extends JPanel{
    private static final long serialVersionUID = 8946273935579723365L;
    private ImageIcon images[] = new ImageIcon[9];
    public static final int PLAYER_FRAMES = 9;
    private String name;
    private int index;
    private int imageX, imageY;
    private int HP = 100;



    public PlayerPane(int width, int height, String playerName) {
            setPreferredSize(new Dimension(width, height));
            imageX = imageY = 0;
            name = playerName;
        //      Border border = BorderFactory.createBevelBorder(BevelBorder.RAISED);
        //      PlayerPane.this.setBorder(border);
        }
        public String getName(){
            return name;
        }
        public void paintComponent(Graphics g){
            try {
                g.drawImage(ImageIO.read(new File("H:\\Java\\Game\\src\\res\\TransparentImg.png")),0,0,getWidth(),getHeight(), null);
            } catch (IOException e) {
                e.printStackTrace();
            }
                g.drawImage(images[index].getImage(), 0,0, null);

            if(index == images.length-1){
                index = 0;
            } else {
                index++;
            }
        }
        public void setIndex(int index){
            this.index = index;
        }
        public void stop(){
            index = 0;
        }
        public void addImage(ImageIcon image, int x){
            images[x] = image;
        }
        public void setDrawX(int x){
            imageX = x;
        }
        public void setDrawY(int y){
            imageY = y;
        }
        public int getDrawY(){
            return imageY;
        }
        public int getDrawX(){
            return imageX;
        }
        public void updateBounds(){
            setBounds(imageX, imageY, 32,32);
        }
        public int getHP(){
                return HP;
            }
            public void setHP(int hp){
                HP = hp;
            }
        }

EnemyAI class:

public class EnemyAI extends AI{
    private static final long serialVersionUID = -2417438750134536982L;

    private Rectangle rect;
    private BufferedImage backgroundImg;
    private int HP = 1;
    private int damage = 15;

    public EnemyAI(int width, int height, BufferedImage backgroundImg){
        super();
        rect = new Rectangle(width, height);
        this.backgroundImg = backgroundImg;
    }
    @Override
    public void paintComponent(Graphics g) {
        try{
            g.drawImage(ImageIO.read(new File("H:\\Java\\Game\\src\\res\\TransparentImg.png")), 0,0,null);
        }catch(IOException e){
            e.printStackTrace();
        }

        g.drawImage(backgroundImg, 0, 0, null);
    }
    public boolean intersects(Rectangle r){
        return rect.intersects(r);
    }
    public int getRandomX(){
        Random ran = new Random();
        int rand = ran.nextInt(6144);
        return rand;
    }
    public int getRandomY(){
        Random ran = new Random();
        int rand = ran.nextInt(4608);
        return rand;
    }
    public int getHP(){
        return HP;
    }
    public void setHP(int hp){
        HP = hp;
    }
    public int getDamage(){
        return damage;
    }
}

AI Class that the above class extends:

public abstract class AI extends JPanel implements Runnable{
    private static final long serialVersionUID = 283692586329054555L;

    private boolean running = false;
    private Thread moveThread;
    private int x = 0, y = 0;

    public AI(){
        moveThread = new Thread(this);
    }
    public void start(){
        running = true;
        moveThread.start();
    }
    public void stop(){
        running = false;
    }
    public boolean isRunning(){
        return running;
    }

    public void run(){
        while(running){
            if(Game.fighting == false){
                Random direction = new Random();
                int dir = direction.nextInt(4);
                switch(dir){
                case 1: 
                    if(this.getX() < Game.getWindowWidth()){
                    this.setEnX(this.getX() + 1);
                    }
                    break;
                case 2:
                    if(this.getX() > 0){
                        this.setEnX(this.getX() - 1);
                    }
                    break;
                case 3:
                    if(this.getX() < Game.getWindowHeight()){
                        this.setEnY(this.getY() + 1);
                    }
                    break;
                case 4:
                    if(this.getX() < 0){
                        this.setEnY(this.getY() - 1);
                    }
                    break;
                }
                updateBounds();
                try{
                    Thread.sleep(200);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }
    public void setEnX(int x){
        this.x = x;
    }
    public void setEnY(int y){
        this.y = y;
    }
    public int getEnX(){
        return x;
    }
    public int getEnY(){
        return y;
    }
    public void updateBounds(){
        setBounds(x, y, 32,32);
    }
    public abstract void paintComponent(Graphics g);
}

I understand that I just threw a lot of code at you guys. I tried not to, but looking back at it I'm trying to provide an example that runs. If I missed any code, tell me and I'll add it.

Anyways, what I need to know is how to make setBounds() stop invoking repaint() (Other than just using less than 5 entities). Also, I've removed EnemyAI.start() when adding entities and it did stop it. So I have reason to believe that the problem is within the AI class run() method. Which pretty much just calls setBounds().

1条回答
放荡不羁爱自由
2楼-- · 2020-02-02 03:12

This is a normal behavior and it's why you can't modify state inside paintComponent. We don't have control over when repaints happen: the system does them on its own sometimes.

Here's an example of what I mean, which you shouldn't be doing:

public class PlayerPane extends JPanel{
    ...

    public void paintComponent(Graphics g){
        ...

        // modifying index
        if(index == images.length-1){
            index = 0;
        } else {
            index++;
        }
    }
}

You need to go through all of your code looking for every place you've modified a variable like this inside paintComponent, and move it out to somewhere else.

As a side note, you should also move your ImageIO.read calls so they are not inside paintComponent. Load your images once when the program starts, in to static variables or something like that.

And as a general tip, you should look in to animation with just painting instead of trying to animate components. It will do you huge favors in the long run for a game.


So in summary:

  • Keep paintComponent stateless.
  • Wrap images with game/animation state in non-UI objects.
  • Paint these images in paintComponent.

Here's a minimal example which demonstrates this by animating shapes falling down the window:

falling shapes

import java.net.*;
import javax.swing.*;
import javax.imageio.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.Graphics;
import java.util.List;
import java.util.ArrayList;
import java.util.Random;

class FallingShapes implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new FallingShapes());
    }

    @Override
    public void run() {
        List<Entity> entities = new ArrayList<Entity>();

        int w = 0;
        int h = 0;

        for (BufferedImage img : Resources.images) {
            entities.add(new Entity(img));

            w += img.getWidth();
            h += img.getHeight();
        }

        PaintPanel p = new PaintPanel(entities);
        p.setPreferredSize(new Dimension(w, (2 * h)));

        JFrame f = new JFrame();

        f.setContentPane(p);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setResizable(false);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);

        new Animator((1000 / 60), p, entities).start();
    }

    static class Animator implements ActionListener {
        int period;
        JPanel context;
        int height;
        List<Entity> entities;

        Animator(int period, JPanel context, List<Entity> entities) {
            this.context  = context;
            this.height   = context.getHeight();
            this.period   = period;
            this.entities = entities;
        }

        @Override
        public void actionPerformed(ActionEvent a) {
            for (Entity e : entities) {
                double dist =
                    (period / 1000.0) * (height * e.rate);

                e.y += dist;
                e.y %= height;
            }

            context.repaint();
        }

        void start() {
            Random r = new Random();
            int    x = 0;
            for (Entity e : entities) {
                e.x    = x;
                e.y    = r.nextInt(height);
                e.rate = (0.25 + (0.75 * r.nextDouble()));

                x += e.width;
            }

            new Timer(period, this).start();
        }
    }

    static class Entity {
        BufferedImage img;

        double x, y, rate;
        int width, height;

        Entity(BufferedImage img) {
            this.img    = img;
            this.width  = img.getWidth();
            this.height = img.getHeight();
        }

        void paint(Graphics g, JPanel context) {
            int x = (int) Math.round(this.x);
            int y = (int) Math.round(this.y);
            g.drawImage(img, x, y, null);

            int cHeight = context.getHeight();
            if ((y + height) > cHeight) {
                g.drawImage(img, x, y - cHeight, null);
            }
        }
    }

    static class PaintPanel extends JPanel {
        List<Entity> entities;

        PaintPanel(List<Entity> entities) {
            this.entities = entities;

            setBackground(Color.white);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            for (Entity e : entities) {
                e.paint(g, this);
            }
        }
    }

    static class Resources {
        static final String[] paths = {
            "http://i.stack.imgur.com/wCF8S.png",
            "http://i.stack.imgur.com/5v2TX.png",
            "http://i.stack.imgur.com/F0JHK.png",
            "http://i.stack.imgur.com/4EVv1.png",
            "http://i.stack.imgur.com/xj49g.png",
        };

        static final List<BufferedImage> images =
            new ArrayList<BufferedImage>();
        static {
            for (String path : paths) {
                try {
                    images.add(ImageIO.read(new URL(path)));
                } catch (Exception e) {
                    throw new AssertionError(e);
                }
            }
        }
    }
}

(Images from here.)

Other useful examples of animation and painting:

查看更多
登录 后发表回答