You are being forwarded to the lastest updates ot his page!
Or you can Click Here if it doesn't work or you don't wish to wait.

Leading

Joseph George
I'll do my part to the new forum by posting the question I'm currently working on:

{velocities are "per loop" of the game}

Consider a target (T) with a position (xT,yT), and a velocity (deltaXT,deltaYT). Now consider a rotating gun turret (G) with a position (xG,yG), and an orientation theta, in degrees. Said gun turret fires a bullet (B) originating from (xG,yG) (or very close to it), with the velocity in radians (s,theta) changed into rectangular, where 's' is a given magnitude, and theta is the above-mentioned orientation of the GunShip. All coords can be changed to radians and back to rectangular with great ease.

given (x1,y1), (deltaX1,deltaY1), (x2,y2), and s, solve for theta such that the bullet will hit the target.

Note that finding (deltaXB,deltaYB) is not an issue, for once theta is found, it is simply (s,theta) in rectangular.


The game I've been working on has involved endless math and equation-making, and if this forum can serve as a place to bounce ideas around with people way smarter than me, it may be a great addition to the ranch.

-Joe

[Clarification]
[Emboldened main question, so as to draw attention to its significance. Kna'mean?]

[ July 31, 2004: Message edited by: Joseph George ]


David Garland
The projectile and target both need to travel different distances at different speeds, but it takes them each the same amount of time to cover the distance. Since speed is distance over time, and time is distance over speed, we set the times equal and get this:

Distance Turret / Speed Turret = Distance Target / Speed Target

Distance Turret / Distance Target = Speed Turret / Speed Target

Since we know the speeds, we know this:

The initial distance between the Turret and Target, and the ratio of the distances Turret-Crash and Target-Crash. We also know the measure of angle Turret-Target-Crash. Let's pause to rephrase this without unnecessary infO:

We have a triangle ABC, we know the length of AB, the measure of B, and the value of AC/BC.

The law of sines tells us that AC/sin(b)=BC/sin(a). Thus sin(a)=sin(b)*BC/AC.

We know sin(b) since B is the angle formed by drawing a line from the turret to the target and then in the direction the target is moving. We know BC/AC since that's the ratio of the distances Target-Crash/Turret-Crash. It's just a matter of calculating A, the angle to fire at (where 0 means firing straight at the target, not necessarily due east). I've glossed over the conversion between absolute angles and angles in the triangle formed by the significant points, but here's some code that does the full calculation:

code:

public class Projectile extends JComponent {
private final Timer _timer = createTimer();

public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setContentPane(new Projectile());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

public Projectile() {
setBackground(Color.WHITE);
getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "start");
getActionMap().put("start", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
startSimulation();
}
});
setPreferredSize(new Dimension(600, 600));
}

float _playerSpeed = 5.0f;
float _projectileSpeed = 4.0f;

Point2D _playerLocation = new Point2D.Float(100, -180);
Point2D _turretLocation = new Point2D.Float(-100, 40);

Point2D _projectileLocation = (Point2D) _turretLocation.clone();

float _playerAngle = (float) Math.PI / 2 ;
float _projectileAngle = (float) calculateProjectileAngle();

private double calculateProjectileAngle() {
double originToPlayerAngle = getAngle(_turretLocation, _playerLocation);
double playerAngle = originToPlayerAngle + Math.PI - _playerAngle;
double playerToCrashOverTurretToCrash = _playerSpeed / projectileSpeed ;
double turretAngle = Math.asin(Math.sin(playerAngle) * playerToCrashOverTurretToCrash);
return turretAngle + originToPlayerAngle;
}

private double getAngle(Point2D from, Point2D to) {
double run = to.getX() - from.getX();
double rise = to.getY() - from.getY();
return Math.atan2(rise, run);
}

protected void paintComponent(Graphics g) {
g.setColor(getBackground());
Rectangle clipBounds = g.getClipBounds();
g.fillRect(clipBounds.x, clipBounds.y, clipBounds.width, clipBounds.height);

g.setColor(Color.BLACK);
int midX = getWidth() / 2;
int midY = getHeight() / 2;
g.drawLine(0, midY, getWidth(), midY);
g.drawLine(midX, 0, midX, getHeight());

drawCircle(g, _playerLocation, Color.GREEN, 10);
drawCircle(g, _turretLocation, Color.BLACK, 10);
drawCircle(g, _projectileLocation, Color.RED, 5);
}

private void drawCircle(Graphics g, Point2D playerLocation, Color color, int width) {
g.setColor(color);
Point2D visualLocation = getVisualLocation(playerLocation);
g.fillOval((int) (visualLocation.getX() - width / 2), (int) (visualLocation.getY() - width / 2), width,
width);
}

private Point2D getVisualLocation(Point2D location) {
return new Point2D.Float((float) (getWidth() / 2 + location.getX()),
(float) (getHeight() / 2 - location.getY()));
}

private void startSimulation() {
if (Float.isNaN(_projectileAngle)) {
JOptionPane.showMessageDialog(this, "The projectile can't catch the target!");
return;
}

if (!_timer.isRunning()) _timer.start();
}

private Timer createTimer() {
final Timer timer = new Timer(50, new ActionListener() {
public void actionPerformed(ActionEvent e) {
updateLocation(_playerLocation, _playerSpeed, _playerAngle);
updateLocation(_projectileLocation, _projectileSpeed, _projectileAngle);

Rectangle bounds = getBounds();
if (!bounds.contains(getVisualLocation(_playerLocation)) ||
!bounds.contains(getVisualLocation(_projectileLocation))) {
_timer.stop();
}
repaint();
}
});
return timer;
}

private void updateLocation(Point2D startingLocation, float speed, float angle) {
double playerX = startingLocation.getX() + speed * Math.cos(angle);
double playerY = startingLocation.getY() + speed * Math.sin(angle);
startingLocation.setLocation(playerX, playerY);
}
}




(Edited code. Behavior is identical but removed the laziness where playerToCrashOverTurretToCrash was misnamed)

[ August 01, 2004: Message edited by: David Weitzman ]


Joseph George
Wow. It would be tough to imagine a more helpful and thourough answer. I was able to implement your triangle comparatively quickly. I am very grateful. Now, it is 4:30 AM here, and time for the ol' Joe to go to bed. Thanks.