001 package physics3d; 002 003 import java.util.ArrayList; 004 import java.util.List; 005 import java.util.Set; 006 007 import components.Ball; 008 import components.Flipper; 009 import components.GameObject; 010 011 /** 012 * GamePhysics is an immutable class with static methods that does the physics calculations 013 * 014 * @specfield geometry : Geometry3D // the object that does calculations 015 */ 016 017 /* 018 * Rep Invariant: 019 * geometry != null 020 */ 021 022 public strictfp class GamePhysics { 023 024 //fields 025 private static Geometry3D geometry = new Geometry3D(); //the brains of the operation 026 027 ////// THE CRUX OF THE MATTER ///// 028 029 /** Move the given ball. * 030 * @throws FallBackException */ 031 public static List<GameObject> moveBall(Ball ball) { 032 try { 033 List<GameObject> collisions = new ArrayList<GameObject>(); //to be returned 034 if (ball.isFrozen()) 035 return collisions; 036 037 //System.err.println("entering moveBall with "+ ball); 038 geometry.setForesightAndSubintervals(1.0/(double)ball.getGameSpace().getSettings().getFPS(), 20); 039 040 //some local variables 041 double timeleft = geometry.getMaximumForesight(); 042 //System.err.println("foresight/timeleft: "+timeleft); 043 Collision coll = new Collision(ball); 044 045 while (timeleft > 0.0) { 046 //finds the next collision and sets its parameters: time till coll, object, shape 047 coll = findNext(ball, timeleft); 048 049 if (coll == null) { 050 freeMove(ball, timeleft); 051 return collisions; 052 } else { 053 collisions.add(coll.object()); 054 constantVelMove(ball, coll.time()); 055 reflect(coll); 056 //freeMove(ball, 0.0001); //just a smidgen 057 Collision c2 = findNext(ball, timeleft); 058 if (c2 != null && c2.time() < GameConstants.TOLERANCE) { 059 //damn itttttttt 060 //ball.setFrozen(true); 061 System.err.println("stupid collision!"+c2); 062 // try { 063 // Thread.currentThread().sleep(1000); 064 // } catch (InterruptedException e) { 065 // e.printStackTrace(); 066 // } 067 throw new NumberFormatException("Crap!"); 068 // return collisions; 069 //throw new RuntimeException("stupid collision! "+c2); 070 } 071 072 //freeMove(ball, timeleft-coll.time()); 073 timeleft = timeleft - coll.time(); // - 0.0001; 074 } 075 } //loop while still in this frame 076 077 //System.err.println("exiting moveBall"); 078 return collisions; 079 } 080 catch (NumberFormatException e) { //hackish -- an exception not used elsewhere 081 //System.out.println("Crap, that was bad, lets try the old version!"); 082 return OldGamePhysics.moveBall(ball); 083 } 084 } //moveBall 085 086 /** finds information about the next collision **/ 087 private static Collision findNext(Ball ball, double timeleft) { 088 //some local variables 089 Set<GameObject> allObjects = ball.getGameSpace().getObjects(); 090 Collision next = new Collision(ball); 091 Sphere ballShape = (Sphere)(world(ball.getBounds(), ball)); 092 093 094 // get the possible collisions using bounding spheres??? 095 List<GameObject> candidates = willCollideWithBound(ball, allObjects, timeleft); 096 if (candidates.size() == 0) { 097 return null; //leave time/object/shape in their initial/null states 098 } 099 else { 100 //System.out.println("candidates! " + candidates); 101 } 102 103 ///* i live on the wild side Set<GameObject> candidates = allObjects; 104 105 // now check only those possible collisions 106 for (GameObject g : candidates) { 107 //check each specific physics3d shape 108 if (g instanceof Flipper) { 109 for (PhysicsShape s : g.getShape().getParts()) { 110 PhysicsShape sShape = world(s, g); 111 Vect3 center = g.getCenter(); 112 Vect3 angularVel = ((Flipper)g).getAngularVelocity(); 113 double time = -1; //will always be set 114 if (angularVel.isAbout(Vect3.ZERO)) 115 time = geometry.timeUntilCollision(sShape, ballShape, ball.getVelocity()); 116 else { //TODO, also move flipper to nextTIme 117 time = geometry.timeUntilRotatingCollision(sShape, center, angularVel, ballShape, ball.getVelocity()); 118 } 119 120 if (time < next.time()) { 121 next.set(time, g, sShape); 122 //System.out.println("time until ball "+ ballShape + " collides with " + sShape + ": "+ time); 123 } 124 } 125 } else if (g instanceof Ball) { 126 Sphere sphere1 = ballShape; 127 Vect3 vel1 = ball.getVelocity(); 128 Sphere sphere2 = (Sphere) world(g.getBounds(), g); 129 Vect3 vel2 = g.getVelocity(); 130 double time = geometry.timeUntilSphereSphereCollision(sphere1, vel1, sphere2, vel2); 131 if (time < next.time()) { 132 next.set(time, g, sphere2); 133 //System.out.println("time until ball "+ ballShape + " collides with " + sphere2 + ": "+ time); 134 } 135 } else { 136 for (PhysicsShape s : g.getShape().getParts()) { 137 PhysicsShape sShape = world(s, g); 138 double time = geometry.timeUntilCollision(sShape, ballShape, ball.getVelocity()); 139 if (time < next.time()) { 140 //System.out.println("time until ball "+ ballShape + " collides with " + sShape + ": "+ time); 141 next.set(time, g, sShape); 142 } 143 } 144 } 145 } 146 if (next.time() < timeleft) { 147 //System.out.println("collision! " + next); 148 return next; //return the collision 149 } 150 else 151 return null; //not in this frame, baby 152 } //findNext 153 154 /** Returns those GameObjects where the ball will collide with their bounds in the next 155 * timeleft seconds. */ 156 public static List<GameObject> willCollideWithBound(Ball ball, Set<GameObject> objects, double timeleft) { 157 //System.err.println("entering willCollideWithBound"); 158 List<GameObject> candidates = new ArrayList<GameObject>(); 159 Sphere ballShape = (Sphere)(world(ball.getBounds(), ball)); 160 161 for (GameObject g : objects) { 162 PhysicsShape gShape = world(g.getShape().getBound(), g); 163 ////System.out.println(gShape + ", " 164 // + ballShape + ", " + ball.getVelocity()); 165 double time = geometry.timeUntilCollision(gShape, ballShape, ball.getVelocity()); 166 ////System.out.println("time until ball "+ ballShape + "with velocity "+ ball.getVelocity() + " collides with bound " + gShape + ": "+ time); 167 168 //does ballShape overlap with gShape? 169 boolean overlaps = false; 170 if (gShape instanceof Sphere) { 171 double distance = ((Sphere)gShape).getCenter().minus(ballShape.getCenter()).rho(); 172 overlaps = distance < ((Sphere)gShape).getRadius() + ballShape.getRadius(); 173 } else if (gShape instanceof PlanePolygon) { //walls 174 double distance = ((PlanePolygon)gShape).minDistanceToObjectFromP(ballShape.getCenter()); 175 overlaps = distance < ballShape.getRadius(); 176 } else 177 throw new RuntimeException("unknown bound: "+gShape); 178 179 if (time < timeleft || overlaps) 180 candidates.add(g); 181 } 182 return candidates; 183 } 184 185 /** Move the given ball for the specified amount of time, under only the 186 * influence of gravity and air resistance 187 * @effects ball.velocity, ball.position 188 */ 189 public static void freeMove(Ball ball, double time) { 190 Vect3 vel = ball.getVelocity(); 191 double mass = 1.0; //ball.getMass(); 192 Vect3 pos = ball.getCenter(); 193 194 //f_d = -bv, b = mu + mu2*|v| 195 double b = ball.getGameSpace().getSettings().getMu() + ball.getGameSpace().getSettings().getMu2()*vel.rho(); 196 Vect3 f_drag = vel.times(-1.3 * b); 197 //f_g = mg 198 Vect3 f_gravity = ball.getGameSpace().getSettings().getGravity().times(mass); //TODO, REMOVE THE FUDGE 199 //f_net = f_d + f_g 200 Vect3 f_net = f_drag.plus(f_gravity); 201 202 //where the ball should move 203 //x_f = x_i + t*v_i + 1/2at^2 //assumes constant acceleration 204 Vect3 next_pos = pos.plus( vel.times(time) ).plus ( f_net.times(1/mass).times(0.5).times(time*time)); 205 //v_f = v_i + f_net/mass * t 206 Vect3 next_vel = vel.plus(f_net.times(1/mass).times(time)); 207 208 ball.setCenter(next_pos); 209 ball.setVelocity(next_vel); 210 } //freeMove 211 212 /** move the ball at its current velocity for time time **/ 213 public static void constantVelMove(Ball b 214 , double time) { 215 //x_f = x_i + v*dt 216 b.setCenter(b.getCenter().plus(b.getVelocity().times(time))); 217 } 218 219 220 /** return p in the worldspace of o **/ 221 public static PhysicsShape world(PhysicsShape p, GameObject o) { 222 ////System.out.println("world: p="+p+", o="+o+", pos="+o.getPosition()); 223 return p.translateByT(o.getCenter()).rotateAboutCwithAxisAandAngleT(o.getCenter(), o.getOrientVector(), o.getOrientAngle()); 224 //return p.rotateAboutCwithAxisAandAngleT(o.getCenter(), o.getOrientVector(), o.getOrientAngle()).translateByT(o.getCenter()); 225 } 226 227 /** reflect the collisioner off the collisionee 228 * @require fields not null 229 * @require collisioner and collisionee are adjacent 230 */ 231 private static void reflect(Collision coll) { 232 Ball b = coll.ball(); 233 //System.out.print("reflect! p="+b.getCenter()+", v_before="+b.getVelocity()); 234 GameObject obj = coll.object(); 235 Sphere bShape = (Sphere)b.getBounds(); 236 //reflect appropriately 237 if (obj instanceof Flipper) { //rotating reflect 238 Vect3 center = obj.getCenter(); 239 Vect3 angularVel = ((Flipper)obj).getAngularVelocity(); 240 if (angularVel.isAbout(Vect3.ZERO)) 241 b.setVelocity( geometry.reflect(coll.shape(), bShape, b.getVelocity(), obj.getCoRef())); 242 else 243 b.setVelocity( geometry.reflectRotating(coll.shape(), center, angularVel, bShape, b.getVelocity(), obj.getCoRef())); 244 } else if (obj instanceof Ball) { //reflecting off another ball o_o 245 Sphere sphere1 = (Sphere) world(b.getBounds(), b); 246 Vect3 velocity1 = b.getVelocity(); 247 Sphere sphere2 = (Sphere)coll.shape(); 248 Vect3 velocity2 = obj.getVelocity(); 249 Vect3Pair newVel = geometry.reflectSpheres(sphere1, 1.0, velocity1, sphere2, 1.0, velocity2); 250 b.setVelocity(newVel.v1); 251 obj.setVelocity(newVel.v2); 252 253 } else { //regular reflect 254 b.setVelocity( geometry.reflect(coll.shape(), bShape, b.getVelocity(), obj.getCoRef())); 255 } 256 //System.out.println(", v_after="+b.getVelocity()); 257 } //reflect 258 } //GamePhysics