JDialogue 的组件在 while 循环结束之前不会显示

问题描述 投票:0回答:1

我目前正在使用内置 GUI 构建 RPG 游戏,并尝试在 JDialogue 上显示简单的战斗场景。我的部分代码如下:

public void setupFight(Character c) {
        fightPopup.setLocationRelativeTo(this);
        fightPopup.setVisible(true);
        mcBar.setMinimum(0);
        mcBar.setMaximum(mc.getAtkspd());
        enemyBar.setMinimum(0);
        enemyBar.setMaximum(c.getAtkspd());
        enemyDm.setVisible(false);
        enemyDmType.setVisible(false);
        mcDm.setVisible(false);
        mcDm.setVisible(false);
        mcFight.setVisible(true);
        enemyFight.setVisible(true);
        fight(c);
    }
    
    public void fight(Character c) {
        mc.setAtkbr(mc.getAtkspd());
        c.setAtkbr(c.getAtkspd());
        int[] damage1 = new int[2];
        int[] damage2 = new int[2];
        System.out.println(mc.getAtk());
        
        while (mc.getHP() > 0 && c.getHP() > 0) {
            try {
                Thread.sleep(10);
            }
            catch (InterruptedException ex) {
                Logger.getLogger(Character.class.getName()).log(Level.SEVERE, null, ex);
            }
            
            mc.setAtkbr(mc.getAtkbr() - 10);
            c.setAtkbr(c.getAtkbr() - 10);
            mcBar.setValue(mcBar.getMaximum() - mc.getAtkbr());
            enemyBar.setValue(enemyBar.getMaximum() - c.getAtkbr());
            enemyDmType.setText("hola");
            
            if (mc.getAtkbr() == 0 && c.getAtkbr() == 0) {
                damage1 = mc.attack(c);
                damage2 = c.attack(mc);
                mc.setHP(mc.getHP() - damage1[1]);
                c.setHP(c.getHP() - damage2[1]);
                mc.setAtkbr(mc.getAtkspd());
                c.setAtkbr(c.getAtkspd());
            }
            else if (mc.getAtkbr() == 0) {
                damage1 = mc.attack(c);
                mc.setHP(mc.getHP() - damage1[1]);
                mc.setAtkbr(mc.getAtkspd());
            }
            else if (c.getAtkbr() == 0) {
                damage2 = c.attack(mc);
                c.setHP(c.getHP() - damage2[1]);
                c.setAtkbr(c.getAtkspd());
            }
        }
        
        if (mc.getHP() <= 0) {
            ((MC) mc).defeat();
        }
        else {
            ((MC) mc).pickUp(((Enemy) c).defeat());
        }
    }

对话应该是这样的: enter image description here

但这就是代码运行和 while 循环运行时显示的内容(基本上是空的,没有任何组件): enter image description here

这些组件仅在“假定的”战斗场景结束后(在

((MC) mc).pickUp(((Enemy) c).defeat());
行之后由代码完成模拟)以及最终更新的数据显示在 JDialogue 上。

有人可以帮我吗?我真的很感激。

我尝试延长

Thread.sleep(10);
时间,因为我认为这是因为10毫秒对于计算机更新对话上的任何信息来说是短的两秒。但它似乎暂停了一切,而不仅仅是 while 循环本身。

java jframe game-development jdialog
1个回答
0
投票

Swing 是单线程的,这意味着您不应该在其上下文中执行长时间运行或阻塞操作(例如执行诸如

Thread.sleep
之类的操作)。

相反,您需要以某种方式解耦工作流程,要么在单独的线程中运行逻辑,要么利用某种定时回调(即

Timer
)。您将使用哪个取决于您想要实现的目标。

请注意,以下示例旨在演示解决方案如何工作,以便您可以采用该概念并在代码中实现它们,而不是提供基于您的代码的“复制粘贴”解决方案。这些是完全可运行的示例,因此您可以复制并运行它们以查看它们是如何工作的。

SwingWorker

请参阅工作线程和 SwingWorker 了解更多详细信息...

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("Test");
                frame.add(
                        new FightPane(
                                new Entity("A", 100),
                                new Entity("B", 100)
                        )
                );
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class Entity {
        private String name;
        private int hitPonts;
        private int health;

        private Random rnd = new Random();

        public Entity(String name, int hitPonts) {
            this.name = name;
            this.health = hitPonts;
            this.hitPonts = hitPonts;
        }

        public String getName() {
            return name;
        }

        public int getHitPonts() {
            return hitPonts;
        }

        public int getHealth() {
            return health;
        }

        public int attack() {
            return rnd.nextInt(0, 25);
        }

        public void damage(int amount) {
            health = Math.max(0, health - amount);
        }

        public void heal(int amount) {
            health = Math.min(hitPonts, health + amount);
        }

        @Override
        public String toString() {
            return getName() + " - " + getHealth() + "/" + getHitPonts();
        }
    }

    public class EntityPane extends JPanel {
        private JLabel name;
        private JLabel health;
        private JProgressBar pbHealth;

        private Entity entity;

        public EntityPane(Entity entity) {
            this.entity = entity;

            name = new JLabel();
            name.setFont(name.getFont().deriveFont(Font.BOLD, 16));

            health = new JLabel();
            pbHealth = new JProgressBar();
            pbHealth.setMinimum(0);

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.anchor = GridBagConstraints.LINE_START;
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            add(name, gbc);
            add(health, gbc);

            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.weightx = 1;
            add(pbHealth, gbc);

            setBorder(new CompoundBorder(new LineBorder(Color.BLACK, 1, true), new EmptyBorder(8, 8, 8, 8)));

            refreshDetails();
        }

        public void didAttack() {
            setBackground(null);
            refreshDetails();
        }

        public void wasAttacked() {
            setBackground(Color.ORANGE);
            refreshDetails();
        }

        public void didWin() {
            setBackground(Color.GREEN);
            refreshDetails();
        }

        public void didLose() {
            setBackground(Color.RED);
            refreshDetails();
        }

        public Entity getEntity() {
            return entity;
        }

        // This would be better handled via an observer directly on Entity
        // but for example purposes
        public void refreshDetails() {
            Entity entity = getEntity();
            if (entity == null) {
                name.setText("---");
                health.setText("---");
                pbHealth.setValue(0);
            }
            name.setText(entity.getName());
            health.setText(entity.getHealth() + "/" + entity.getHitPonts());
            pbHealth.setMaximum(entity.getHitPonts());
            pbHealth.setValue(entity.getHealth());
        }
    }

    protected class FightPane extends JPanel {

        private EntityPane fighterOnePane;
        private EntityPane fighterTwoPane;

        public FightPane(Entity fighter1, Entity fighter2) {
            setBorder(new EmptyBorder(8, 8, 8, 8));
            setLayout(new GridBagLayout());
            JLabel title = new JLabel("Fight!");
            title.setFont(title.getFont().deriveFont(Font.BOLD, 32));

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.insets = new Insets(8, 8, 8, 8);
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            add(title, gbc);

            fighterOnePane = new EntityPane(fighter1);
            fighterTwoPane = new EntityPane(fighter2);

            JPanel fighters = new JPanel(new GridBagLayout());
            GridBagConstraints gbcFight = new GridBagConstraints();
            gbcFight.gridheight = GridBagConstraints.REMAINDER;
            gbcFight.insets = new Insets(8, 8, 8, 8);
            fighters.add(fighterOnePane, gbcFight);
            fighters.add(new JLabel("vs"), gbcFight);
            fighters.add(fighterTwoPane, gbcFight);

            add(fighters, gbc);

            JButton fightButton = new JButton("Start");
            add(fightButton, gbc);

            fightButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    fightButton.setEnabled(false);
                    FightWorker worker = new FightWorker(fighterOnePane.getEntity(), fighterTwoPane.getEntity(), new FightWorker.Obsever() {
                        @Override
                        public void update(Entity fighter) {
                            if (fighter == fighterOnePane.getEntity()) {
                                fighterOnePane.wasAttacked();
                                fighterTwoPane.didAttack();
                            } else if (fighter == fighterTwoPane.getEntity()) {
                                fighterOnePane.didAttack();
                                fighterTwoPane.wasAttacked();
                            }
                        }

                        @Override
                        public void winner(Entity fighter) {
                            EntityPane target = null;
                            if (fighter == fighterOnePane.getEntity()) {
                                fighterOnePane.didWin();
                                fighterTwoPane.didLose();
                                title.setText(fighter.getName() + " did win!");
                            } else if (fighter == fighterTwoPane.getEntity()) {
                                fighterOnePane.didLose();
                                fighterTwoPane.didWin();
                                title.setText(fighter.getName() + " did win!");
                            } else {
                                fighterOnePane.didLose();
                                fighterTwoPane.didLose();
                                title.setText("No one won!");
                            }

                            // Don't tap the button twice, but this shows away
                            // you could re-enable controls or do things
                            // when the fight is over
                            fightButton.setEnabled(true);
                        }
                    });
                    worker.execute();
                }
            });
        }        
    }

    protected class FightWorker extends SwingWorker<Entity, Entity> {
        // This provides a simpilified callback workflow and decouples
        // the worker from the obsever.  The worker focucs only on the
        // interaction between the entities and nothing else
        public interface Obsever {
            public void update(Entity fighter);
            public void winner(Entity fighter);
        }

        private Entity fighter1;
        private Entity fighter2;
        private Obsever obsever;

        public FightWorker(Entity fighter1, Entity fighter2, Obsever obsever) {
            this.fighter1 = fighter1;
            this.fighter2 = fighter2;
            this.obsever = obsever;

            // This allows for a self monitoring workflow, so when the
            // worker is "done", we can easily notify the obeserver.  These
            // notifications occur on the EDT
            addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    if (isDone()) {
                        try {
                            Entity winner = get();
                            obsever.winner(winner);
                        } catch (InterruptedException | ExecutionException ex) {
                            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
                            obsever.winner(null);
                        }
                    }
                }
            });
        }

        @Override
        protected void process(List<Entity> chunks) {
            // This is called within the EDT, so we can update the UI
            for (Entity entity : chunks) {
                obsever.update(entity);
            }
        }

        @Override
        protected Entity doInBackground() throws Exception {
            Random rnd = new Random();

            while (fighter1.getHealth() > 0 && fighter2.getHealth() > 0) {
                // Randomly choose who gets to attack
                if (rnd.nextBoolean()) {
                    fighter2.damage(fighter1.attack());
                    publish(fighter2);
                } else {
                    fighter1.damage(fighter2.attack());
                    publish(fighter1);
                }
                Thread.sleep(500);
            }

            Entity winner = null;
            if (fighter1.getHealth() > 0 && fighter2.getHealth() == 0) {
                winner = fighter1;
            } else if (fighter1.getHealth() == 0 && fighter2.getHealth() > 0) {
                winner = fighter2;
            }

            return winner;
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.