java - Component.setBounds 调用 Component.repaint?

标签 java multithreading swing

所以我正在制作一个游戏,并且我有 EnemyAI以及 player它们都扩展 JPanel 。世界上有一个null布局,所以我使用 setBounds(); “移动”(我实际上只是移动世界图像)entities ( playerAI )周围并正确放置它们。但是当我添加(看起来像是我测试了可能的最小数字)5 时,它会调用 repaint()完全靠自己。这会导致玩家视觉上在原地行走。添加的实体越多,间隔就越快(即 5 个实体调用 repaint() 比 500 个实体调用慢很多)。

注:window下面的类中只是一个 JFrame .

主类:

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();
            }
        }
    }
}

玩家:

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 类别:

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类:

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);
}

我知道我只是向你们扔了很多代码。我试图不这样做,但回顾它,我试图提供一个运行的例子。如果我遗漏了任何代码,请告诉我,我会添加它。

无论如何,我需要知道的是如何制作setBounds()停止调用repaint() (除了使用少于 5 个 entities )。另外,我删除了 EnemyAI.start()添加entities时它确实阻止了它。所以我有理由相信问题出在AI内类run()方法。这几乎只是调用 setBounds() .

最佳答案

这是正常行为,也是您无法修改 paintComponent 内部状态的原因。我们无法控制重绘何时发生:系统有时会自行执行重绘。

以下是我的意思的一个示例,但您不应该这样做:

public class PlayerPane extends JPanel{
    ...

    public void paintComponent(Graphics g){
        ...

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

您需要仔细检查所有代码,查找在 paintComponent 中修改过此类变量的每个位置,并将其移至其他位置。

顺便说一句,您还应该移动 ImageIO.read 调用,以便它们不在 paintComponent 内部。程序启动时将图像加载到静态变量或类似的变量中。

作为一般提示,您应该仅通过绘画来研究动画,而不是尝试为组件设置动画。从长远来看,这会给你的游戏带来巨大的好处。


总结一下:

  • 保持paintComponent无状态。
  • 将具有游戏/动画状态的图像封装在非 UI 对象中。
  • paintComponent 中绘制这些图像。

这是一个最小的示例,通过动画形状从窗口落下来演示这一点:

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 = {
            "/image/wCF8S.png",
            "/image/5v2TX.png",
            "/image/F0JHK.png",
            "/image/4EVv1.png",
            "/image/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);
                }
            }
        }
    }
}

(图片来自 here 。)

动画和绘画的其他有用示例:

关于java - Component.setBounds 调用 Component.repaint?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30175533/

相关文章:

java - 使用正则表达式设置 boolean 值?

linux - 多个线程定期更新全局变量,而第三个线程等待读取

java - 鼠标点击坐标不起作用

java - 为什么我的输出每次都是null?

java - 在java中将图像图标添加到组合框

java - Libgdx "pack",不工作,我可能做错了什么?

java - 如何在Java中存储唯一的字符串对?

java - 限定符在 Spring 不起作用

C# BackgroundWorker 和调用

c++ - 如何避免在 C++11 服务器程序中为多个客户端使用多线程