Reputation: 11
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;
}
}
}
}
}
}
}
I'm attempting to build a very simple combat simulation frame. It builds 2 combo-boxes and assigns stats based upon the selections therein.
User then hits the calculate button, and out pops the result into the Results field. That all works fine.
I'd like the GUI to append each 'turn' of combat into the Results textfield outText - and I think I could do that easily enough. I'd like it to do so with a slight delay on any turn there is combat - I'd use a Thread.sleep(10) in each if-statement to do with an active turn for each ship.
The only problem with all that is I can't work out how to hold the GUI on one thread - which updates when prompted by the combat calculations after the button is clicked - and the thread handling all the calculations with delays.
I know if I try and run both on the same thread, the GUI will simply freeze up, do all the calculations with applicable delays, then throw out the entire appended lump at once into the results field.
Can anyone offer me any pointers, advice or cyanide? Anything, anything to make the pain go away...
Suspect that SwingWorker would be a good idea, but still can't work out how to actually implement that within my code.
Clarification: My biggest concern is that I can't work out how to implement SwingWorker, or some similar multi-thread process, in order to run the GUI and background calculations in parallel.
Upvotes: 1
Views: 93
Reputation: 37835
I will just take a stab at this since I think I understand what you are trying to do. Maybe what you are looking for is to create a new runnable each time the button is clicked:
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) {}
}
}
It sounds like you just want a scrolling list of the attacks as they happen and that's what this will do for you. IMO it is the right way to do it and ultimately the simplest.
When the user clicks the button you do:
new Thread(new Turn(e.getSource(), (String)attackCombo.getSelectedItem(), (String)targetCombo.getSelectedItem())).start();
(Or make a variable for the new thread if you need to be able to end it early for some reason or alter parameters.)
This pattern can easily be adapted to SwingWorker if it is long-running, just extend SwingWorker and do the same thing but override doInBackground()
instead of run()
.
Otherwise if you are intending to make a real-time simulation and need a background thread you will need to make run/doInBackground be a while(running) loop and use flags and/or some kind of wait/notify scheme when you need to change parameters or calculate a turn.
Upvotes: 1
Reputation: 10136
You could use javax.swing.Timer to schedule something to happen N milliseconds in the future. Note that 10
in Java time usually means 10
milliseconds, which is a nearly imperceptible amount of time. If you want to sleep or delay for a second, use 1000 milliseconds, 10 seconds = 10000 milliseconds, etc. Example:
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");
}
}
}
Upvotes: 0