// A Universe contains SpaceObjects, and has an associated MetricSpace // and Gravity object. Universe owns physical objects, their // interactions, and the passage of time. package SpaceWar; import java.awt.*; import java.awt.geom.*; import java.util.*; class Universe implements Runnable { ////////////////////////////////////////////////////////////////////// // // Parameters. public static long millisPerTimeStep = 100; ////////////////////////////////////////////////////////////////////// // // Instance Variables. // Definitions of the "physics" of this Universe and the // physical objects contained within it. protected StartUp frame; // Frame into which we must display protected Gravity gravity; // Defines gravitation protected MetricSpace metric; // Defines geometry/movement protected LinkedList spaceObjectList; // Contains physical objects protected SpaceBackground background; // Passive background // Statistics variables to track run time. These are used to // measure what the maximum update rate can be. protected double currentTime; // Time steps since start protected long timeStepOverruns; // Time step too short protected long minSleepTime; protected long maxOverrun; protected long computeTime = 0; protected double computeTimeAvg; protected long displayTime = 0; protected double displayTimeAvg; // Working variables, mostly here to keep garbage collection // down to a dull roar. protected SpaceObjectListIterator iter1; protected SpaceObjectListIterator iter2; protected Point2D.Double p; // Momentum vector ////////////////////////////////////////////////////////////////////// // // Constructors. public Universe(StartUp newFrame, MetricSpace newMetric, Gravity newGravity, SpaceBackground newBackground) { spaceObjectList = new LinkedList(); iter1 = new SpaceObjectListIterator(); iter2 = new SpaceObjectListIterator(); p = new Point2D.Double(); frame = newFrame; metric = newMetric; gravity = newGravity; background = newBackground; } ////////////////////////////////////////////////////////////////////// // // Object-list management methods. // Add the specified SpaceObject to the list. synchronized public void addSpaceObject(SpaceObject so) { spaceObjectList.add(so); so.setUniverse(this); } // Remove all the SpaceObjects from the list. public void removeAllSpaceObjects() { SpaceObject sop; synchronized(this) { iter1.setIterator(spaceObjectList); while (iter1.hasNext()) { sop = iter1.next(); iter1.remove(); sop.stopExisting(); } } } ////////////////////////////////////////////////////////////////////// // // Game-management methods. // Restart game with standard setup (single centered sun). public void restartGame(int restartType) { removeAllSpaceObjects(); frame.setupObjects(restartType); } ////////////////////////////////////////////////////////////////////// // // Get-set methods. public double getCurrentTime() { return (currentTime); } // Set up new gravity definition. Amaze all your friends! public void setGravity(Gravity newGravity) { gravity = newGravity; } ////////////////////////////////////////////////////////////////////// // // Action-sequence methods. // Top-level control of progression of object movement and state. public void run() { long elapsedTime; long nextTime; long sleepTime; long startTime; nextTime = System.currentTimeMillis(); minSleepTime = millisPerTimeStep; maxOverrun = 0; while (true) { nextTime += millisPerTimeStep; // Update object state and position for this timestep. startTime = System.currentTimeMillis(); synchronized(this) { updateObjectState(); computeGravity(); doMovement(); checkCollisions(); } elapsedTime = System.currentTimeMillis() - startTime; computeTime += elapsedTime; computeTimeAvg = computeTime / (currentTime + 1); // Force redrawing. frame.repaint(); // Wait for next timestep to begin. try { sleepTime = nextTime - System.currentTimeMillis(); if (sleepTime > 0) { Thread.sleep(sleepTime); if (sleepTime < minSleepTime) { minSleepTime = sleepTime; } } else { Thread.yield(); timeStepOverruns++; if (sleepTime < maxOverrun) { maxOverrun = sleepTime; } } } catch (InterruptedException e) { System.exit(0); } currentTime += 1.0; } } // Update object state. protected void updateObjectState() { SpaceObject sop; iter1.setIterator(spaceObjectList); while (iter1.hasNext()) { sop = iter1.next(); if (!sop.updateState()) { iter1.remove(); } } } // Compute gravitational accelerations for all objects. protected void computeGravity() { Point2D.Double ap; Point2D.Double aq; double mp; double mq; Point2D.Double p; Point2D.Double q; double rp; double rq; SpaceObject sop; SpaceObject soq; // Initialize acceleration for all objects, and calculate // single-object graviational interactions (for example, to // simulate the bottom of a deep gravity well such as a // planet's surface. iter1.setIterator(spaceObjectList); while (iter1.hasNext()) { sop = iter1.next(); sop.initializeAcceleration(); p = sop.getPosition(); ap = sop.getAcceleration(); mp = sop.getMass(); gravity.computeGravity(ap, p, mp); } // Compute gravitational force and update accelerations for // all pairs of objects. iter1.setIterator(spaceObjectList); while (iter1.hasNext()) { sop = iter1.next(); p = sop.getPosition(); ap = sop.getAcceleration(); mp = sop.getMass(); rp = sop.getRadius(); iter2.setIterator(spaceObjectList, sop); while (iter2.hasNext()) { soq = iter2.next(); q = soq.getPosition(); aq = soq.getAcceleration(); mq = soq.getMass(); rq = soq.getRadius(); gravity.computeGravity(ap, p, mp, rp, aq, q, mq, rq); } } } // Capture old position and do movement. protected void doMovement() { boolean dm; Point2D.Double op; Point2D.Double p; SpaceObject sop; iter1.setIterator(spaceObjectList); while (iter1.hasNext()) { sop = iter1.next(); p = sop.getPosition(); op = sop.getOldPosition(); op.setLocation(p.getX(), p.getY()); dm = metric.updatePosition(sop.getAcceleration(), sop.getVelocity(), p, sop.getEffectiveRadius()); sop.setDiscontinuousMotion(dm); } } // Check for collisions between objects. protected void checkCollisions() { double m1; double m2; Point2D.Double p1; Point2D.Double p2; Point2D.Double q1; Point2D.Double q2; SpaceObject sop; SpaceObject soq; Point2D.Double v1; Point2D.Double v2; // Check for collisions between all pairs of objects. iter1.setIterator(spaceObjectList); while (iter1.hasNext()) { sop = iter1.next(); p1 = sop.getPosition(); p2 = sop.getOldPosition(); iter2.setIterator(spaceObjectList, sop); while (iter2.hasNext()) { soq = iter2.next(); q1 = soq.getPosition(); q2 = soq.getOldPosition(); if (metric.checkCollision(p1, p2, sop.getEffectiveRadius(), q1, q2, soq.getEffectiveRadius())) { sop.startExploding(soq); soq.startExploding(sop); v1 = sop.getVelocity(); v2 = soq.getVelocity(); m1 = sop.getMass(); m2 = soq.getMass(); metric.scaleVelocity(v1, m1); metric.scaleVelocity(v2, m2); p.setLocation(0., 0.); metric.addVelocity(p, v1); metric.addVelocity(p, v2); metric.scaleVelocity(p, 1. / (m1 + m2)); v1.setLocation(p.getX(), p.getY()); v2.setLocation(p.getX(), p.getY()); } } } } ////////////////////////////////////////////////////////////////////// // // Display methods. // Paint all physical objects. synchronized public void paint(SpaceGraphics g) { long elapsedTime; SpaceObject sop; long startTime; startTime = System.currentTimeMillis(); // Put code to update background here. if (background != null) { background.paint(g); } metric.paint(g); // Paint new images. iter1.setIterator(spaceObjectList); while (iter1.hasNext()) { sop = iter1.next(); sop.paint(g); } elapsedTime = System.currentTimeMillis() - startTime; displayTime += elapsedTime; displayTimeAvg = displayTime / (currentTime + 1); } }