public override void Redraw(float currentTime, float elapsedTime) { // update camera, tracking test vehicle Demo.UpdateCamera(elapsedTime, _vehicle); // draw "ground plane" (make it 4x map size) const float S = MapDriver.WORLD_SIZE * 2; const float U = -0.2f; Drawing.DrawQuadrangle(new Vector3(+S, U, +S), new Vector3(+S, U, -S), new Vector3(-S, U, -S), new Vector3(-S, U, +S), new Color((byte)(255.0f * 0.8f), (byte)(255.0f * 0.7f), (byte)(255.0f * 0.5f))); // "sand" // draw map and path if (MapDriver.DemoSelect == 2) _vehicle.DrawPath(); _vehicle.DrawMap(); // draw test vehicle _vehicle.Draw(); // QQQ mark origin to help spot artifacts const float TICK = 2; Drawing.DrawLine(new Vector3(TICK, 0, 0), new Vector3(-TICK, 0, 0), Color.Green); Drawing.DrawLine(new Vector3(0, 0, TICK), new Vector3(0, 0, -TICK), Color.Green); // compute conversion factor miles-per-hour to meters-per-second const float METERS_PER_MILE = 1609.344f; const float SECONDS_PER_HOUR = 3600; // ReSharper disable InconsistentNaming const float MPSperMPH = METERS_PER_MILE / SECONDS_PER_HOUR; // ReSharper restore InconsistentNaming // display status in the upper left corner of the window StringBuilder status = new StringBuilder(); status.AppendFormat("Speed: {0} mps ({1} mph), average: {2:0.0} mps\n\n", (int)_vehicle.Speed, (int)(_vehicle.Speed / MPSperMPH), _vehicle.TotalDistance / _vehicle.TotalTime); status.AppendFormat("collisions avoided for {0} seconds", (int)(Demo.Clock.TotalSimulationTime - _vehicle.TimeOfLastCollision)); if (_vehicle.CountOfCollisionFreeTimes > 0) { status.AppendFormat("\nmean time between collisions: {0} ({1}/{2})", (int)(_vehicle.SumOfCollisionFreeTimes / _vehicle.CountOfCollisionFreeTimes), (int)_vehicle.SumOfCollisionFreeTimes, _vehicle.CountOfCollisionFreeTimes); } status.AppendFormat("\n\nStuck count: {0} ({1} cycles, {2} off path)", _vehicle.StuckCount, _vehicle.StuckCycleCount, _vehicle.StuckOffPathCount); status.Append("\n\n[F1] "); if (1 == MapDriver.DemoSelect) status.Append("wander, "); if (2 == MapDriver.DemoSelect) status.Append("follow path, "); status.Append("avoid obstacle"); if (2 == MapDriver.DemoSelect) { status.Append("\n[F2] path following direction: "); status.Append(_vehicle.PathFollowDirection > 0 ? "+1" : "-1"); status.Append("\n[F3] path fence: "); status.Append(_usePathFences ? "on" : "off"); } status.Append("\n[F4] rocks: "); status.Append(_useRandomRocks ? "on" : "off"); status.Append("\n[F5] prediction: "); status.Append(_vehicle.CurvedSteering ? "curved" : "linear"); if (2 == MapDriver.DemoSelect) { status.AppendFormat("\n\nLap {0} (completed: {1}%)", _vehicle.LapsStarted, ((_vehicle.LapsStarted < 2) ? 0 : (int)(100 * ((float)_vehicle.LapsFinished / (_vehicle.LapsStarted - 1)))) ); status.AppendFormat("\nHints given: {0}, taken: {1}", _vehicle.HintGivenCount, _vehicle.HintTakenCount); } status.Append("\n"); qqqRange("WR ", MapDriver.SavedNearestWR, status); qqqRange("R ", MapDriver.SavedNearestR, status); qqqRange("L ", MapDriver.SavedNearestL, status); qqqRange("WL ", MapDriver.SavedNearestWL, status); Vector3 screenLocation = new Vector3(15, 50, 0); Vector3 color = new Vector3(0.15f, 0.15f, 0.5f); Drawing.Draw2dTextAt2dLocation(status.ToString(), screenLocation, new Color(color.ToXna())); { float v = Drawing.GetWindowHeight() - 5; const float M = 10; float w = Drawing.GetWindowWidth(); float f = w - (2 * M); // limit tick mark float l = _vehicle.AnnoteMaxRelSpeed; Drawing.Draw2dLine(new Vector3(M + (f * l), v - 3, 0), new Vector3(M + (f * l), v + 3, 0), Color.Black); // two "inverse speedometers" showing limits due to curvature and // path alignment if (Math.Abs(l) > float.Epsilon) { float c = _vehicle.AnnoteMaxRelSpeedCurve; float p = _vehicle.AnnoteMaxRelSpeedPath; Drawing.Draw2dLine(new Vector3(M + (f * c), v + 1, 0), new Vector3(w - M, v + 1, 0), Color.Red); Drawing.Draw2dLine(new Vector3(M + (f * p), v - 2, 0), new Vector3(w - M, v - 1, 0), Color.Green); } // speedometer: horizontal line with length proportional to speed Drawing.Draw2dLine(new Vector3(M, v, 0), new Vector3(M + (f * S), v, 0), Color.White); // min and max tick marks Drawing.Draw2dLine(new Vector3(M, v, 0), new Vector3(M, v - 2, 0), Color.White); Drawing.Draw2dLine(new Vector3(w - M, v, 0), new Vector3(w - M, v - 2, 0), Color.White); } }
public override void Redraw(float currentTime, float elapsedTime) { // update camera, tracking test vehicle Demo.UpdateCamera(elapsedTime, _vehicle); // draw "ground plane" (make it 4x map size) const float S = MapDriver.WORLD_SIZE * 2; const float U = -0.2f; Drawing.DrawQuadrangle(new Vector3(+S, U, +S), new Vector3(+S, U, -S), new Vector3(-S, U, -S), new Vector3(-S, U, +S), new Color((byte)(255.0f * 0.8f), (byte)(255.0f * 0.7f), (byte)(255.0f * 0.5f))); // "sand" // draw map and path if (MapDriver.DemoSelect == 2) { _vehicle.DrawPath(); } _vehicle.DrawMap(); // draw test vehicle _vehicle.Draw(); // QQQ mark origin to help spot artifacts const float TICK = 2; Drawing.DrawLine(new Vector3(TICK, 0, 0), new Vector3(-TICK, 0, 0), Color.Green); Drawing.DrawLine(new Vector3(0, 0, TICK), new Vector3(0, 0, -TICK), Color.Green); // compute conversion factor miles-per-hour to meters-per-second const float METERS_PER_MILE = 1609.344f; const float SECONDS_PER_HOUR = 3600; // ReSharper disable InconsistentNaming const float MPSperMPH = METERS_PER_MILE / SECONDS_PER_HOUR; // ReSharper restore InconsistentNaming // display status in the upper left corner of the window StringBuilder status = new StringBuilder(); status.AppendFormat("Speed: {0} mps ({1} mph), average: {2:0.0} mps\n\n", (int)_vehicle.Speed, (int)(_vehicle.Speed / MPSperMPH), _vehicle.TotalDistance / _vehicle.TotalTime); status.AppendFormat("collisions avoided for {0} seconds", (int)(Demo.Clock.TotalSimulationTime - _vehicle.TimeOfLastCollision)); if (_vehicle.CountOfCollisionFreeTimes > 0) { status.AppendFormat("\nmean time between collisions: {0} ({1}/{2})", (int)(_vehicle.SumOfCollisionFreeTimes / _vehicle.CountOfCollisionFreeTimes), (int)_vehicle.SumOfCollisionFreeTimes, _vehicle.CountOfCollisionFreeTimes); } status.AppendFormat("\n\nStuck count: {0} ({1} cycles, {2} off path)", _vehicle.StuckCount, _vehicle.StuckCycleCount, _vehicle.StuckOffPathCount); status.Append("\n\n[F1] "); if (1 == MapDriver.DemoSelect) { status.Append("wander, "); } if (2 == MapDriver.DemoSelect) { status.Append("follow path, "); } status.Append("avoid obstacle"); if (2 == MapDriver.DemoSelect) { status.Append("\n[F2] path following direction: "); status.Append(_vehicle.PathFollowDirection > 0 ? "+1" : "-1"); status.Append("\n[F3] path fence: "); status.Append(_usePathFences ? "on" : "off"); } status.Append("\n[F4] rocks: "); status.Append(_useRandomRocks ? "on" : "off"); status.Append("\n[F5] prediction: "); status.Append(_vehicle.CurvedSteering ? "curved" : "linear"); if (2 == MapDriver.DemoSelect) { status.AppendFormat("\n\nLap {0} (completed: {1}%)", _vehicle.LapsStarted, ((_vehicle.LapsStarted < 2) ? 0 : (int)(100 * ((float)_vehicle.LapsFinished / (_vehicle.LapsStarted - 1)))) ); status.AppendFormat("\nHints given: {0}, taken: {1}", _vehicle.HintGivenCount, _vehicle.HintTakenCount); } status.Append("\n"); qqqRange("WR ", MapDriver.SavedNearestWR, status); qqqRange("R ", MapDriver.SavedNearestR, status); qqqRange("L ", MapDriver.SavedNearestL, status); qqqRange("WL ", MapDriver.SavedNearestWL, status); Vector3 screenLocation = new Vector3(15, 50, 0); Vector3 color = new Vector3(0.15f, 0.15f, 0.5f); Drawing.Draw2dTextAt2dLocation(status.ToString(), screenLocation, new Color(color.ToXna())); { float v = Drawing.GetWindowHeight() - 5; const float M = 10; float w = Drawing.GetWindowWidth(); float f = w - (2 * M); // limit tick mark float l = _vehicle.AnnoteMaxRelSpeed; Drawing.Draw2dLine(new Vector3(M + (f * l), v - 3, 0), new Vector3(M + (f * l), v + 3, 0), Color.Black); // two "inverse speedometers" showing limits due to curvature and // path alignment if (Math.Abs(l) > float.Epsilon) { float c = _vehicle.AnnoteMaxRelSpeedCurve; float p = _vehicle.AnnoteMaxRelSpeedPath; Drawing.Draw2dLine(new Vector3(M + (f * c), v + 1, 0), new Vector3(w - M, v + 1, 0), Color.Red); Drawing.Draw2dLine(new Vector3(M + (f * p), v - 2, 0), new Vector3(w - M, v - 1, 0), Color.Green); } // speedometer: horizontal line with length proportional to speed Drawing.Draw2dLine(new Vector3(M, v, 0), new Vector3(M + (f * S), v, 0), Color.White); // min and max tick marks Drawing.Draw2dLine(new Vector3(M, v, 0), new Vector3(M, v - 2, 0), Color.White); Drawing.Draw2dLine(new Vector3(w - M, v, 0), new Vector3(w - M, v - 2, 0), Color.White); } }
// draw the GCRoute as a series of circles and "wide lines" // (QQQ this should probably be a method of Path (or a // closely-related utility function) in which case should pass // color in, certainly shouldn't be recomputing it each draw) public void DrawPath() { Vector3 pathColor = new Vector3(0, 0.5f, 0.5f); Vector3 sandColor = new Vector3(0.8f, 0.7f, 0.5f); Vector3 vColor = Vector3.Lerp(sandColor, pathColor, 0.1f); Color color = new Color(vColor.ToXna()); Vector3 down = new Vector3(0, -0.1f, 0); for (int i = 0; i < Path.PointCount; i++) { Vector3 endPoint0 = Path.Points[i] + down; if (i > 0) { Vector3 endPoint1 = Path.Points[i - 1] + down; float legWidth = Path.Radii[i]; Drawing.DrawXZWideLine(endPoint0, endPoint1, color, legWidth * 2); Drawing.DrawLine(Path.Points[i], Path.Points[i - 1], new Color(pathColor.ToXna())); Drawing.DrawXZDisk(legWidth, endPoint0, color, 24); Drawing.DrawXZDisk(legWidth, endPoint1, color, 24); } } }