1
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 和后台计算。

4

2 回答 2

1

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

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 成为 while(running) 循环,并在需要更改时使用标志和/或某种等待/通知方案参数或计算转弯。

于 2013-10-24T17:46:16.807 回答
0

您可以使用 javax.swing.Timer 安排某事在未来 N 毫秒内发生。请注意,10在 Java 中,时间通常意味着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");
        }
    }
}
于 2013-10-24T17:58:26.840 回答