Summary of Simulator Classes¶
Read time: 11 minutes (2769 words)
To help you design your simulator, here are the example header files I used in building the demonstration code you have seen in class.
Highway Class¶
The highway provides the logic that makes the demo run. Vehicles were designed like the pool balls that inspired this demo code. After discussing the design in class last week, this design is not that great. But it is a start.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #pragma once
#include "Vehicle.h"
#include "Random.h"
class Highway {
public:
int lanes; // number of lanes to display
int lWidth; // width of a lane in pixels
int length; // length of highway section
int speed; // speed limit in pixels/sec
int safe; // safe passing distance
double sScale; // used to somtrol simulation speed
double xScale; // distance scale
int xRoad;
int yRoad;
int rampSpeed; // controls on ramp injection speed
Highway(); // default contsructor
void drawLanes( void );
void setPosition( int x, int y );
void setScaling( double speed, double length );
// traffic setup
static const int MAX_CARS = 20;
Vehicle cars[MAX_CARS];
Random sim_rand;
// vehicle management
void trafficSetup( int safe );
void initVehicle( int c );
void setRampSpeed( int speed );
bool checkSafe( int c, int lane );
void changeLanes( int c, int speed );
void checkCollisions( void);
void drawVehicle( void );
void move( void );
void inject( int time );
void retire( int car );
};
|
If you look at the routines provided here, you see methods that are doing the
checking to see if a car needs to modify its behavior. For example, the
check_safe
method determines if a car could change lanes when it is told to
move. Figuring this out involves looking to see if any other car is in the way
of a possible lane change. In this simple simulation, the lane changes happen
instantly.
The checkCollisions
method is right out of the pool ball simulation, and is
used to alter the speed of the offending vehicle.
Vehicles¶
Vehicles in the demo are limited gadgets. Basically, they store a bit of data used to decide how they move.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #pragma once
class Vehicle {
public:
int size; // how big should this vehicle be
int xPos; // screen position in pixels
int yPos;
int xSpeed; // current speed
int ySpeed;
int lane;
int preferred_lane;
bool active;
int color;
Vehicle();
void setPosition( int x, int y );
void setSpeed( int xv, int yv );
void move( void );
};
|
Here, the move
method is the most important. The vehicle will only move
away from the current lane if it is “safe”. You need to think about this to
decide what should happen. (It depends on which way you need to go.)
The logic needed here is up to the car, not the highway. The idea is for the highway to provide enough information to the car for it to decide the next move.
Demo Main Code¶
Here is the original demo main logic used to run the simulation. This went together pretty quickly so I could show it in class. It needs refactoring a bit, which we will see next.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #include "Graphics.h"
#include "Highway.h"
#include "Vehicle.h"
#include <iostream>
using namespace std;
// highway setup --------------------------------
Highway I35;
int xRoad = 50;
int yRoad = 50;
const int SAFE_LEVEL = 10;
int rampSpeed = 0;
const double xScale = 900/5280.0;
double sScale = 0.2; // controls simulation speed
int cycles = 0;
void showTime( void ) {
string msg = "Simulation time: 000000";
sprintf(&msg[17], "%6d", cycles);
showString(50, 450, msg);
}
void drawScene(void) {
clearWindow(); // clear the last scene
showTime();
I35.drawLanes();
I35.drawVehicle();
glutSwapBuffers(); // double buffering control
}
void animate() {
// move your objects here
I35.move();
glutPostRedisplay();
pause_ms(16);
cycles++;
}
// keyboard handler to terminate the program when "q" key is pressed
void handleKey(unsigned char key, int x, int y) {
switch(key) {
case 'q':
exit(0);
break;
}
}
int main(int argc, char **argv) {
if( argc > 1)
sScale = atof(argv[1]);
// set up the highway
I35.setPosition(xRoad,yRoad);
I35.setScaling( xScale, sScale );
I35.trafficSetup( SAFE_LEVEL );
graphicsSetup(argc, argv); // initialize the graphics system
glutDisplayFunc(drawScene); // tell GLUT what function draws the scene
glutIdleFunc(animate); // Move objects when animating
glutKeyboardFunc(handleKey); // set up the "q" key to quit
glutMainLoop(); // GLUT will control the action
glutSwapBuffers(); // double buffering control
}
|
This is pretty ugly, since it combines application logic and graphics engine logic. A better design would isolate those two aspects of the design.
Main Refactored¶
Pulling the graphics logic out of main
, and placing it in its own
file is what is needed here. Here is the new main
code.
1 2 3 4 5 6 7 8 | #include "SimGraphics.h"
#include <iostream>
int main(int argc, char *argv[]) {
std::cout << "i35sim (v2)" << std::endl;
sim_main(argc, argv);
}
|
That is a lot easier. It also sets us up ot run simulations that do not use
graphics at all. We would bypass the call to sim_main
and use only console
output to see what happens.
Simulator Graphics¶
The heart of the visual demo is the graphics library. I put all of the startup
code in a separate file to clean up main
. Here is the header file for the
new graphics module:
1 2 3 4 5 | // Copyright 2018 Roie R. Black
#pragma once
void sim_main(int argc, char *argv[]);
|
Here is a start on the Graphics Code. In this code I have added two key commands to make running the simulator more interesting.
The b
command stops the action, allowing you to study the current
situation. While the simulation is paused, you can press the s
key to
single step it one cycle. I wanted to get this running before I add crash logic
to the demo. That will happen soon (I hope!)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | // Copyright 2018 Roie R. Black
#include "Graphics.h"
#include "SimGraphics.h"
int cycles = 0; // simulation loop counter
bool step_cmd = false; // single step simulation
bool break_cmd = false; // stop simulation toggle
void animate() {
// move your objects here
if (!break_cmd) {
//I35.move();
cycles++;
glutPostRedisplay();
pause_ms(16);
} else {
if (step_cmd) {
// I35.move();
cycles++;
glutPostRedisplay();
step_cmd = !step_cmd;
}
}
}
void handleKey(unsigned char key, int x, int y) {
switch(key) {
case 'q':
exit(0);
break;
case '+':
//I35.setRampSpeed(rampSpeed++);
break;
case '-':
//I35.setRampSpeed(rampSpeed--);
break;
case 'b':
break_cmd = ! break_cmd;
break;
case 's':
step_cmd = true;
break;
}
}
void showTime( void ) {
string msg = "Simulation time: 000000";
sprintf(&msg[17], "%6d", cycles);
showString(50, 450, msg);
}
void drawScene(void) {
clearWindow(); // clear the last scene
// draw your highway here
showTime();
// leave this call as the last one here
glutSwapBuffers(); // double buffering control
}
void sim_main(int argc, char *argv[]) {
graphicsSetup(argc, argv); // initialize the graphics system
glutDisplayFunc(drawScene); // set function that draws the scene
glutIdleFunc(animate); // Move objects when animating
glutKeyboardFunc(handleKey); // set up the "q" key to quit
glutMainLoop(); // GLUT will control the action
}
|
Feel free to use this code, either directly (of course, you need to provide the implementation logic), or as a model for your own code. If you get stuck, see me for guidance. I may let you peek at my code to move you forward.