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");
}
}
}