001 package physics3d; 002 import java.util.ArrayList; 003 import java.util.List; 004 import java.util.Set; 005 006 import components.Ball; 007 import components.GameObject; 008 009 /** 010 * <p> 011 * OldGamePhysics is an immutable class with static methods that does physics calculations. 012 * 013 * OldGamePhysics does some things better than GamePhysics but does not have as many features, 014 * such as calculating collisions between balls. 015 * 016 * </p> 017 */ 018 019 /* 020 * Rep Invariant: 021 * 022 * geometry != null 023 */ 024 025 public class OldGamePhysics { 026 027 //fields 028 private static Geometry3D geometry = new Geometry3D(); //the brains of the operation 029 030 ////// THE CRUX OF THE MATTER ///// 031 032 /** Move the given ball. **/ 033 public static List<GameObject> moveBall(Ball ball) { 034 List<GameObject> collisions = new ArrayList<GameObject>(); //to be returned 035 if (ball.isFrozen()) 036 return collisions; 037 038 //System.err.println("entering moveBall with "+ ball); 039 geometry.setForesightAndSubintervals(1.0/(double)ball.getGameSpace().getSettings().getFPS(), 10); 040 041 //some local variables 042 double timeleft = geometry.getMaximumForesight(); 043 Set<GameObject> allObjects = ball.getGameSpace().getObjects(); 044 045 while (timeleft > 0.0) { 046 //////System.out.println("timeleft="+timeleft); 047 double nextTime = Double.NaN; //maybe not a good idea 048 049 // get the possible collisions using bounding spheres 050 List<GameObject> candidates = willCollideWithBound(ball, allObjects, timeleft); 051 if (candidates.size() == 0) { 052 nextTime = timeleft; 053 freeMove(ball, timeleft); 054 } else { 055 //Collections.sort(candidates, new DistanceFrom(ball.getCenter())); 056 057 // get the next collision (with a PhysicsShape, at a time) 058 PhysicsShape nextShape = nextShape(ball, candidates, timeleft, collisions); 059 if (nextShape == null) { 060 nextTime = timeleft; 061 freeMove(ball, timeleft); 062 } else { 063 064 Sphere ballShape = (Sphere)(world(ball.getBounds(), ball)); 065 nextTime = geometry.timeUntilCollision(nextShape, ballShape, ball.getVelocity()); 066 if (nextTime == 0) { 067 System.err.println("In old GamePhysics, nextTime = 0"); 068 } 069 //////System.out.println("sancheck: nextTime=" + nextTime); 070 071 if (farther(ball,candidates.get(0))) { 072 //Farther returns true, if the ball is actually getting father from the 073 //candidate, even though we think this should never happen, but it does. 074 //To deal with this, we let the ball naturally move (unimpeded by a superflourous reflection) 075 076 nextTime = 0.0003; //Just move a tiny bit 077 freeMove(ball, 0.0003); 078 } 079 080 // if collision, move for time s, call reflect() method, loop 081 if (nextTime < timeleft) { //which happens, btw, to be less than infinity 082 //////System.out.println("old old vel=" + ball.getVelocity()); 083 freeMove(ball, nextTime); 084 //////System.out.println("old vel=" + ball.getVelocity()); 085 ball.setVelocity(geometry.reflect(nextShape, ballShape, ball.getVelocity(), candidates.get(0).getCoRef())); 086 //////System.out.println("new vel=" + ball.getVelocity()); 087 } else { // else move for timeleft, loop 088 nextTime = timeleft; 089 freeMove(ball, timeleft); 090 } 091 } //if not actually colliding with anything 092 } //if there are candidates 093 timeleft -= nextTime; 094 } 095 096 //System.err.println("exiting moveBall"); 097 return collisions; 098 } //moveBall 099 100 /** Returns those GameObjects where the ball will collide with their bounds in the next 101 * timeleft seconds. */ 102 public static List<GameObject> willCollideWithBound(Ball ball, Set<GameObject> objects, double timeleft) { 103 //System.err.println("entering willCollideWithBound"); 104 List<GameObject> candidates = new ArrayList<GameObject>(); 105 Sphere ballShape = (Sphere)(world(ball.getBounds(), ball)); 106 107 for (GameObject g : objects) { 108 PhysicsShape gShape = world(g.getShape().getBound(), g); 109 //////System.out.println(gShape + ", " 110 // + ballShape + ", " + ball.getVelocity()); 111 double time = geometry.timeUntilCollision(gShape, ballShape, ball.getVelocity()); 112 //////System.out.println("time until ball "+ ballShape + "with velocity "+ ball.getVelocity() + " collides with bound " + gShape + ": "+ time); 113 if (time < timeleft) 114 candidates.add(g); 115 } 116 return candidates; 117 } 118 119 /** Return the closest PhysicsShape that this ball will collide with in the 120 * next timeleft seconds. If it will not collide with any PhysicsShape, 121 * null 122 * @returns a physicsshape set in WORLD SPACE, or null 123 * @requires: candidates in sorted order by distance from Ball 124 * @effects: adds object to collisions if there is one 125 */ 126 public static PhysicsShape nextShape(Ball ball, List<GameObject> candidates, double timeleft, List<GameObject> collisions) { 127 //System.err.println("entering nextShape"); 128 PhysicsShape next = null; //arbitrary 129 Sphere ballShape = (Sphere)(world(ball.getBounds(), ball)); 130 131 //what we need is the gameobject, its physicsshape, and time for the first collision 132 GameObject leastObj = null; 133 PhysicsShape leastSh = null; 134 double leastTime = Double.MAX_VALUE; 135 136 for (GameObject g : candidates) { 137 //check each specific physics3d shape 138 for (PhysicsShape s : g.getShape().getParts()) { 139 PhysicsShape sShape = world(s, g); 140 double time = geometry.timeUntilCollision(sShape, ballShape, ball.getVelocity()); 141 //////System.out.println("time until ball "+ ballShape + " collides with " + sShape + ": "+ time); 142 if (time < leastTime) { 143 leastTime = time; 144 leastSh = sShape; 145 leastObj = g; 146 } //if this is the least so far, set vars 147 } 148 } 149 if (leastTime < timeleft) { //if will collide in this frame 150 collisions.add(leastObj); 151 152 //I want to get the least candidate back! 153 candidates.clear(); 154 candidates.add(leastObj); 155 next = leastSh; 156 } 157 //////System.out.println("nextShape: " + next); 158 return next; 159 } //nextShape 160 161 /** Move the given ball for the specified amount of time, under only the 162 * influence of gravity and air resistance 163 * @effects ball.velocity, ball.position 164 */ 165 public static void freeMove(Ball ball, double time) { 166 Vect3 vel = ball.getVelocity(); 167 double mass = 1.0; //ball.getMass(); 168 Vect3 pos = ball.getCenter(); 169 170 //f_d = -bv, b = mu + mu2*|v| 171 double b = ball.getGameSpace().getSettings().getMu() + ball.getGameSpace().getSettings().getMu2()*vel.rho(); 172 Vect3 f_drag = vel.times(-1.0 * b); 173 //f_g = mg 174 Vect3 f_gravity = ball.getGameSpace().getSettings().getGravity().times(mass); 175 //f_net = f_d + f_g 176 Vect3 f_net = f_drag.plus(f_gravity); 177 178 //where the ball should move 179 //x_f = x_i + t*v_i + 1/2at^2 //assumes constant acceleration 180 Vect3 next_pos = pos.plus( vel.times(time) ).plus ( f_net.times(1/mass).times(0.5).times(time*time)); 181 //v_f = v_i + f_net/mass * t 182 Vect3 next_vel = vel.plus(f_net.times(1/mass).times(time)); 183 184 ball.setCenter(next_pos); 185 ball.setVelocity(next_vel); 186 } //freeMove 187 188 /** return p in the worldspace of o **/ 189 public static PhysicsShape world(PhysicsShape p, GameObject o) { 190 //////System.out.println("world: p="+p+", o="+o+", pos="+o.getPosition()); 191 return p.rotateAboutCwithAxisAandAngleT(Vect3.ZERO, 192 o.getOrientVector(), o.getOrientAngle()).translateByT(o.getCenter()); 193 } 194 195 /** 196 * Given too game objects, ball and canidate, father will return true 197 * only if ball is getting closer to canidate. 198 * @requires ball and canidate != null 199 * @returns true iff ball is moving towards canidate 200 */ 201 public static boolean farther(GameObject ball, GameObject canidate) { 202 if (canidate.getCenter().plus(ball.getVelocity()).rho() > canidate.getCenter().rho()) { 203 ////System.out.println("===>According to my math, I'm getting farther away, so I'm gonna free move!"); 204 return true; 205 } 206 return false; 207 } 208 209 } //GamePhysics