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 - 在点击按钮后由战斗计算提示时更新 - 以及线程处理所有延迟的计算。 / p>
我知道如果我尝试在同一个线程上运行它们,GUI将简单地冻结,用适用的延迟进行所有计算,然后立即将整个附加的块丢弃到结果字段中。
任何人都可以向我提供任何指示,建议或氰化物吗?什么,任何让痛苦消失的东西......
怀疑SwingWorker是个好主意,但仍然无法弄清楚如何在我的代码中实际实现它。
澄清:我最关心的是我无法弄清楚如何实现SwingWorker或类似的多线程进程,以便并行运行GUI和后台计算。
答案 0 :(得分:1)
我会抓住这个,因为我想我明白你要做什么。也许你想要的是每次点击按钮时创建一个新的runnable:
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(运行)循环并使用标志和/或某种等待/通知方案需要更改参数或计算转弯。
答案 1 :(得分: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");
}
}
}