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