/* * acc specifies how much to accelerate, or break if it is negative. * It may not have absolute value larger than the acc parameter given to init * * da specifies how much to turn right or left. * It may not have absolute value larger than the turn parameter given to init * * x, y, dx, dy, alpha, dalpha, wp, and skidding are all global variables * x and y give the location of the car, while dx and dy give the velocity * alpha gives the angle the car is facing, while dalpha gives the rotational momentum when skidding * skidding indicates if the car was skidding in the previous time step * wp gives the current waypoint index * * The most complicated part of the code is dealing with friction and air resistance * Most importantly, when accelerating forward, the air resistance is removed from the forward * force before friction is checked (more as if the car were propelled by rockets than wheels). * In the cross direction, the forces of friction and air resistance are added to see if the car * can make the turn. */ void step(double acc, double da){ if (!skidding) { //not skidding, so reset dalpha = 0; } if(acc < -friction){ acc = -friction; } dalpha += da; alpha += dalpha; //adjust the direction we're facing if (alpha > Math.PI) alpha -= 2 * Math.PI; else if (alpha < -Math.PI) alpha += 2 * Math.PI; dalpha /= 2; //dampen the rotational momentum double cross = (dx * Math.sin(alpha) - dy * Math.cos(alpha)); //signed magnitude of velocity which is across direction we're facing double forward = (dx * Math.cos(alpha) + dy * Math.sin(alpha)); //signed magnitude of velocity which is in direction we're facing //now compute the forward and cross components of air resistance. Resistance is proportional to velocity squared double crossair = cross / Math.hypot(cross, forward) * air * Math.hypot(dx, dy) * Math.hypot(dx, dy); //signed double forwardair = Math.abs(forward / Math.hypot(cross, forward) * air * Math.hypot(dx, dy) * Math.hypot(dx, dy)); //unsigned if (cross == 0 && forward == 0) crossair = forwardair = 0; //avoid NaN acc -= forwardair; //remove some forward power due to air resistance if (acc < 0 && Math.abs(acc) > Math.abs(forward)) acc = -forward;//can only stop as much as we're moving else if (acc < 0 && forward < 0) acc = -acc;//breaking while going backwards results in positive force double mcross = 0, macc = 0; //next we have to compute how much of the force of friction goes in the forward direction, and how much goes in the cross direction mcross = friction * cross / Math.hypot(cross, acc); macc = friction * acc / Math.hypot(cross, acc); if (cross == 0 && acc == 0) { //avoid NaN when stopped mcross = 0; macc = 0; } mcross += crossair; //the air resistance slows us down in the cross direction //detect skidding. The cross velocity is the current cross velocity, minus the cross air resistance //the forward velocity used is either the acceleration, or the acceleration minus the air resistance //we do the second to avoid thinking that we are skidding when we are really just slowing rapidly due to air resistance skidding = Math.hypot(cross - crossair, acc) >= friction && Math.hypot(cross - crossair, acc + forwardair) > friction; //finally we check to see if the cross force or forward force on the car exceed the frictional allowance, and adjust accordingly if (cross > mcross && cross >= 0) { cross = mcross; } else if (cross < mcross && cross <= 0) { cross = mcross; } if (acc > macc && acc >= 0) { acc = macc; } //else case intentionally omitted //calculations all done, adjust velocity and position dx += cross * Math.cos(alpha + Math.PI / 2); dy += cross * Math.sin(alpha + Math.PI / 2); dx += acc * Math.cos(alpha); dy += acc * Math.sin(alpha); x += dx; y += dy; finally, check the waypoints. wx and wy give the locations while(wp < wx.length && Math.hypot(x-wx[wp],y-wy[wp]) < 1000){ wp++; } }