2013-10-24 38 views
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个组合框,并根据其中的选择分配统计数据。使用Swing难度

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

我想的GUI到每个作战“转向”追加到结果文本框和输出文本 - 我想我能做到这一点很容易不够。我希望这样做可以在任何一个战斗中稍微延迟 - 我会在每个if语句中使用一个Thread.sleep(10)来处理每个船只的主动轮次。

唯一的问题就是我无法弄清楚如何在一个线程上保存GUI - 当单击按钮后,当单击按钮时进行战斗计算时会进行更新 - 以及处理所有延迟计算的线程。

我知道,如果我尝试在同一个线程同时运行,GUI会简单地冻结起来,做所有适用的延迟计算,再一次抛出了整个一次性追加到结果领域。

任何人都可以给我任何指针,建议或氰化物?什么,什么使疼痛消失......

怀疑的SwingWorker将是一个不错的主意,但仍无法工作,如何真正实现我的代码中。

澄清:我最关心的是,我可以不知道如何实现的SwingWorker,或一些类似的多线程处理,以并行运行的图形用户界面和后台计算。

+1

好吧,你可能会做一个大循环,每次迭代运行一次,结束睡眠。你可以做一些事情,例如将结果追加到JTextArea。如果你正在睡觉,你不一定非得使用SwingWorker,尽管SwingWorker由于各种原因是“更好”的。无论哪种方式,你将不得不用'invokeLater'更新GUI。你能缩小一点吗?这是如此开放的结局我个人不知道超出了我的建议和我的建议似乎相当明显。 – Radiodef

+0

请坚持使用Java命名约定,变量位于lowerCamelCase中。你的代码很难理解,因为它很难说什么是变量和什么是类。 –

+0

也是真的@BoristheSpider说。特别是在你的代码基础上得到颜色编码。根据命名规则而变得更加难以阅读。 – Radiodef

回答

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

这听起来像你只是想袭击滚动列表,因为它们发生,这就是这会为你做。国际海事组织正是这样做的最终最简单的方式。

当用户点击该按钮,你这样做:(或做新线程的变量,如果你需要能够及早因某种原因停止它或改变参数)

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

如果SwingWorker运行时间很长,则可以轻松地调整此模式,只需扩展SwingWorker并执行相同的操作即可,而不是run(),而是覆盖doInBackground()

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

+0

我真的很抱歉 - 或真的很慢 - 但我实际上无法解决如何将这个应用到我自己的代码中。我知道我对Java并不擅长,但是我并没有意识到自己真的很厚道:'(谢谢你的尝试,尽管如此。 – Panda

+0

@Panda你只需在actionPerformed中插入所有的计算就可以运行()。所有这些让你可以安全的使用Thread.sleep来关闭事件调度线程 – Radiodef

+0

编辑我的答案,试图使它更清楚如何使用它 – Radiodef

0

您可以使用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"); 
     } 
    } 
} 
+0

这当然看起来是我所追求的。在这一点上,工作还在继续,但是我有机会将我的代码重写到这个框架中,并看看会发生什么,我会在明天再回到你身边。手指交叉! – Panda

+0

此外,刚刚发现了布局代码。谢谢!我害怕试图从我拥有的位置移动任何东西,因为它刚刚落在我认为应该整齐地进行的地方。 – Panda