难以将线程与 Swing 一起使用


package combatframe;
import javax.swing.*;
import java.awt.event.*;
import static java.lang.Math.PI;
import javax.swing.border.*;
public class CombatFrame extends JFrame
{
    String[] Ships = {"Battleship", "Cruiser", "Frigate"};
    JComboBox attackCombo = new JComboBox(Ships);
    JComboBox targetCombo = new JComboBox(Ships);
    JButton calculate = new JButton();
    JPanel mainPanel = new JPanel();
    JPanel outPanel = new JPanel();
    JPanel shipPanel = new JPanel();
    JTextArea outText = new JTextArea("Results: ", 11, 30);
    JTextArea attackStats = new JTextArea("Attacker stats.", 10,10);
    JTextArea targetStats = new JTextArea("Target stats.", 10,10);
    public static void main(String[] args) 
    {
        CombatFrame combatFrame = new CombatFrame();
    }
    public CombatFrame()
    {
        this.setSize(500,500);
        this.setTitle("OTG Combat Simulator");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        EventListener c1 = new EventListener();
        EventListener c2 = new EventListener();
        EventListener calc = new EventListener();
        calculate.setText("Calculate!");
        Border b1 = BorderFactory.createTitledBorder("Result");
        outPanel.setBorder(b1);        
        Border b2 = BorderFactory.createTitledBorder("Ship Classes");
        shipPanel.setBorder(b2);
        mainPanel.add(shipPanel);
        mainPanel.add(outPanel);
        shipPanel.add(attackStats);
        shipPanel.add(attackCombo);
        shipPanel.add(targetCombo);
        shipPanel.add(targetStats);
        outPanel.add(calculate);
        outPanel.add(outText);
        attackCombo.addActionListener(c1);
        attackCombo.setSelectedItem("Battleship");
        targetCombo.addActionListener(c2);
        targetCombo.setSelectedItem("Battleship");
        calculate.addActionListener(calc);
        this.add(mainPanel);
        this.setVisible(true);
    } 
    public double hitProbability(double factor)
    {
        double hitCount = (double)(Math.random() * 1.001 * factor );
        return hitCount;
    }
    public double Combat(double[] turnArray)
    {
        double rRange = turnArray[0];
        double rAngular = turnArray[1];
        double rRadius = turnArray[2];
        double damage = turnArray[3];
        double appliedDamage = 0;
        int[] attHit = 
        {
            0, 0, 0
        };
        int[] strikes =
        {
            0, 0, 0, 0, 0
        };
        double[] damageMod =
        {
            0,0.4,0.75,1.0,1.25
        };
        double roll1, roll2, roll3;
        roll1 = hitProbability(rRadius);
        roll2 = hitProbability(rAngular);
        roll3 = hitProbability(rRange);
        if (roll1 <=1)
            attHit[0]++;
        if (roll2 <=1)
            attHit[1]++;
        if (roll3 <=1)
            attHit[2]++;
        switch (attHit[0]+attHit[1]+attHit[2])
        {
            case 3:
                double wrecker = Math.random();
                if (wrecker >= 0.95 - Math.random())
                    strikes[4]++;
                else
                    strikes[3]++;
                break;
            case 2:
                strikes[2]++;
                break;
            case 1:
                strikes[1]++;
                break;
            case 0:
                strikes[0]++;
                break;
        }
        for (int x=0; x<5; x++)
        {
        appliedDamage += strikes[x]*Damage*
                (Math.random()+Math.random())*damageMod[x];
        }

        return appliedDamage;
    }
    private class EventListener implements ActionListener
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {
            if (e.getSource()== attackCombo)
            {
                switch ((String)attackCombo.getSelectedItem())
                {
                    case "Battleship":
                        Attacker attackBS = new Attacker(50000.0, 400.0, 0.01, 45000.0, 400.0,
                                300.0, 10.0, 10000.0, 250.0);
                        break;
                    case "Cruiser":
                        Attacker attackCr = new Attacker(25000.0, 125.0, 0.23, 22500.0, 150.0, 
                                600.0, 5.0, 5000.0, 75.0);
                        break;
                    case "Frigate":
                        Attacker attackFr = new Attacker(2500.0, 40.0, 0.365, 1000.0, 39.0, 
                                900.0, 2.0, 1200.0, 25.0);
                        break;
                }
                attackStats.setText("Optimal: " + Attacker.Optimal 
                        + "n Weapon Sig: " +Attacker.Attack_Signature 
                        + "n Tracking: " + Attacker.Tracking
                        + "n Range: " + Attacker.Range
                        + "n Ship Sig: " + Attacker.Target_Signature
                        + "n Velocity: " + Attacker.Orbital
                        + "n Cycle Time" + Attacker.Period
                        + "n Health: " + Attacker.Health
                        + "n Damage: " + Attacker.Damage);
                }
            if (e.getSource()==targetCombo)
            {
                switch ((String)targetCombo.getSelectedItem())
                {
                    case "Battleship":
                        Target targetBS = new Target(50000.0, 400.0, 0.01, 45000.0, 400.0,
                                300.0, 10.0, 10000.0, 250.0);
                        break;
                    case "Cruiser":
                        Target targetCr = new Target(25000.0, 125.0, 0.23, 22500.0, 150.0, 
                                600.0, 5.0, 5000.0, 75.0);
                        break;
                    case "Frigate":
                        Target targetFr = new Target(2500.0, 40.0, 0.365, 1000.0, 39.0, 
                                900.0, 2.0, 1200.0, 25.0);
                        break;
                }
                targetStats.setText("Optimal: " + Target.Optimal 
                    + "n Weapon Sig: " + Target.Attack_Signature 
                    + "n Tracking: " + Target.Tracking
                    + "n Range: " + Target.Range
                    + "n Ship Sig: " + Target.Target_Signature
                    + "n Velocity: " + Target.Orbital
                    + "n Cycle Time" + Target.Period
                    + "n Health: " + Target.Health
                    + "n Damage: " + Target.Damage);
            }
        if (e.getSource()==calculate)
        {
                double[] attackArray =
                {//RRange,RAngular,RRadius,Attacker.Damage
                    0,0,0,0
                };
                double[] targetArray =
                {//RRange,RAngular,RRadius,Target.Damage
                    0,0,0,0
                };
                double attackDamage = 0, targetDamage = 0;
                for (int i=1; i<1000; i++)
                {
                    if ((i+1)%Attacker.Period==0)
                    {
                        double rRange = Attacker.Optimal/Target.Range;
                        double rAngular = 
                            ((Math.sin(PI/4)*(Target.Orbital/Target.Range))
                            /Attacker.Tracking);
                        double rRadius = Attacker.Attack_Signature/
                                Target.Target_Signature;
                        attackArray[0] = rRange;
                        attackArray[1] = rAngular;
                        attackArray[2] = rRadius;
                        attackArray[3] = Attacker.Damage;
                        attackDamage += Combat(attackArray);
                        if (attackDamage >= Target.Health)
                        {
                            outText.setText("Attacker wins!");
                            break;
                        }
                    }

                    if (i%Target.Period==0)
                    {
                        double rRange = Target.Optimal/Attacker.Range;
                        double rAngular = 
                            ((Math.sin(PI/4)*(Attacker.Orbital/Attacker.Range))
                            /Target.Tracking);
                        double rRadius = Target.Attack_Signature/
                                Attacker.Target_Signature;
                        targetArray[0] = rRange;
                        targetArray[1] = rAngular;
                        targetArray[2] = rRadius;
                        targetArray[3] = Target.Damage;
                        targetDamage += Combat(targetArray);
                        if (targetDamage >= Attacker.Health)
                        {
                            outText.setText("Target wins!");
                            break;
                        }
                    }
                }
            }
        }
    }
}

我正在尝试构建一个非常简单的战斗模拟框架。它构建 2 个组合框,并根据其中的选择分配统计数据。

然后,用户点击计算按钮,并将结果弹出到结果字段中。这一切都很好。

我希望 GUI 将战斗的每个"回合"附加到结果文本字段 outText 中 - 我认为我可以很容易地做到这一点。我希望它在任何有战斗的回合中稍微延迟一下 - 我会在每个 if 语句中使用 Thread.sleep(10) 来处理每艘船的活动转弯。

唯一的问题是我无法弄清楚如何将 GUI 保存在一个线程上 - 单击按钮后战斗计算提示时会更新 - 并且线程延迟处理所有计算。

我知道如果我尝试在同一线程上运行两者,GUI 将简单地冻结,以适用的延迟进行所有计算,然后立即将整个附加块扔到结果字段中。

谁能给我任何指示、建议或氰化物?任何东西,任何能让痛苦消失的东西...

怀疑 SwingWorker 是个好主意,但仍然无法弄清楚如何在我的代码中实际实现它。

澄清:我最大的担忧是我无法弄清楚如何实现 SwingWorker 或一些类似的多线程进程,以便并行运行 GUI 和后台计算。

我只是试一试,因为我想我明白你想做什么。也许您正在寻找的是每次单击按钮时创建一个新的可运行对象:

class Turn implements Runnable {
    private final Object source;
    private final String attackComboMove;
    private final String targetComboMove;
    public Turn(Object src, String acm, String tcm) {
        source = src;
        attackComboMove = acm;
        targetComboMove = tcm;
    }
    @Override public void run() {
        // may want to disable GUI buttons here
        if (source == attackCombo) {
            switch (attackComboMove) {
                // ...
                // ...
                // ...
            }
            append("attack combo results");
        } else if (source == targetCombo) {
            switch (targetComboMove) {
                // ...
                // ...
                // ...
            }
            append("target combo results");
        } else if (source == calculate) {
            // ...
            for (int i = 1; i < 1000; i++) {
                // ...
                attackDamage += combat(attackArray);
                append("combat results: " + attackDamage + " total damage");
                if (attackDamage >= target.health) {
                    append("Attacker wins!");
                    break;
                }
            }
        }
        // and enable the buttons again when combat is over
    }
    private void append(String text) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override public void run() {
                outTextArea.append("n" + text);
            }
        });
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {}
    }
}

听起来您只需要一个滚动的攻击列表,这就是为您所做的。IMO 这是正确的方法,最终也是最简单的方法。

当用户单击该按钮时,您可以执行以下操作:

new Thread(new Turn(e.getSource(), (String)attackCombo.getSelectedItem(), (String)targetCombo.getSelectedItem())).start();

(或者,如果您出于某种原因需要能够提前结束它或更改参数,请为新线程创建一个变量。

如果这种模式是长时间运行的,可以很容易地适应 SwingWorker ,只需扩展 SwingWorker 并做同样的事情,但覆盖doInBackground()而不是run()

否则,如果您打算进行实时模拟并需要后台线程,则需要使 run/doInBackground 成为一段时间(运行)循环,并在需要更改参数或计算转弯时使用标志和/或某种等待/通知方案。

你可以使用 javax.swing.Timer 来安排将来 N 毫秒发生的事情。 请注意,Java 时间中的10通常意味着 10 毫秒,这是一个几乎察觉不到的时间量。 如果要休眠或延迟一秒钟,请使用 1000 毫秒、10 秒 = 10000 毫秒等。 例:

import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class GameFrame extends JFrame
{
    private JPanel _contentPane;
    private GameFrame _mainFrame;
    private JButton _jButton;
    private JTextArea _jTextArea;
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                GameFrame frame = new GameFrame();
                frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
                frame.setSize(640/2, 480/2);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
    public GameFrame()
    {
        _mainFrame = this;
        _contentPane = new JPanel();
        _mainFrame.setContentPane(_contentPane);
        _contentPane.setLayout(new BorderLayout());
        _jTextArea = new JTextArea();
        _contentPane.add(_jTextArea, BorderLayout.CENTER);
        _jButton = new JButton("Go");
        _jButton.addActionListener(new GoButtonListener());
        _contentPane.add(_jButton, BorderLayout.SOUTH);
    }
    class GoButtonListener implements ActionListener
    {
        public void actionPerformed(ActionEvent e)
        {
            _jTextArea.append("Please wait...n");
            Timer timer = new Timer(10000, new LaterListener());
            timer.start();
        }
    }
    class LaterListener implements ActionListener
    {
        public void actionPerformed(ActionEvent e)
        {
            _jTextArea.append("Ten seconds later!n");
        }
    }
}

最新更新