1

In my traffic simulation project, I want to change the states(color of lights) of the traffic light objects in a period. I wrote TrafficLightGroup class to group 7 traffic lights (t-lights) into 3 groups. In this class, there is a simulate() function which adds t-light to the groups and change their states in a period. Here is the TrafficLightGroup class:

class TrafficLightGroup
{
    TrafficLight* head, tail, greenLight;
    float duration; //Period
public:
    float time; //Current time elapsed in seconds
    // duration: TrafficLight period
    TrafficLightGroup(float duration): head(NULL), tail(NULL), greenLight(NULL), time(0){
        this->duration = duration;
    }
    // light: Pointer to the traffic light object to be added to the group
    void add(TrafficLight* light) {
        if(head == NULL) {
            head = light;
            head->state = light->getState();
            tail = head;
        }
        else {
            tail->next = light;
            tail = tail->next;
        }
    }

    /* If the traffic light group timer reaches the switching period (i.e., duration member variable) of
    the group, the next light in the group is turned into green while the others are turned into red
    */

    void simulate(float timestep) { //timestep in seconds
        this->duration = timestep;
        this->time = timestep * 6;

        TrafficLightGroup group1(timestep); //TTOP (road at t-top junction )
        TrafficLightGroup group2(timestep); //TRIGHT (road at t-right junction )

        TrafficLight* light1 = new TrafficLight(2*239 + 70, 250, 180, GREEN);
        light1->setState(GREEN);
        TrafficLight* light2 = new TrafficLight(2*239 + 240, 170, 90, RED);
        light2->setState(RED);

        group1.add(light1);
        group2.add(light2);
        //There are more t-lights but I don't include them

        float i = 0;
        while(i <= time) {
            if(i == time - 5 * timestep) {
                light1->setState(RED);
                light2->setState(GREEN);
            }

            else if(i == time - 4 * timestep) {
                light1->setState(GREEN);
                light2->setState(RED);
            }

            else if(i == time - 3 * timestep) {
                light1->setState(RED);
                light2->setState(GREEN);
            }

            else if(i == time - 2 * timestep) {
                light1->setState(GREEN);
                light2->setState(RED);
            }

            else if(i == time - timestep) {
                light1->setState(RED);
                light2->setState(GREEN);
            }

            else if(i == time) {
                light1->setState(GREEN);
                light2->setState(RED);
            }

            i += 1;
        }
    }
};

If you also want to look at the TrafficLight class, I added here:

typedef enum {
    GREEN = 0,
    RED = 1
}tLightState;

class TrafficLight {
    float x, y;
    float dir; //direction of the traffic light (determines the orientation of the traffic light on the map)
    sf::Texture redTexture;
    sf::Texture greenTexture;
    sf::Sprite sprite;

public:
    tLightState state; //current state of the light (either green or red). tLightState should be an enum
    TrafficLight* next; //pointer to the next traffic light in the traffic light group

    //x: x coordinate of the traffic light
    //y: y coordinate of the traffic light
    //dir: traffic light direction, i.e., orientation
    //state: initial state of the traffic light
    TrafficLight(float x, float y,  float dir, tLightState state)
    {
        this->x=x; this->y=y; this->dir=dir;
        if(state == GREEN){
            greenTexture.loadFromFile("images/trafficlights/green.png");
            sprite.setTexture(greenTexture);
        }
        else{
            redTexture.loadFromFile("images/trafficlights/red.png");
            sprite.setTexture(redTexture);
        }

        sprite.setPosition(sf::Vector2f(x,y));
        sprite.setRotation(dir);
        sprite.setOrigin(0, 0);
    }

    //Returns the position and the direction of the traffic light
    //dir: orientation
    void getPosition(float &x, float &y, float &dir) {x = this->x; y = this->y; dir = this->dir;}

    //Draws the traffic lights
    void draw(sf::RenderWindow *window) {window->draw(this->sprite);}

    //Returns current traffic light state
    tLightState getState() {return state;}

    //Sets the traffic light state
    void setState(tLightState state) {
        this->state = state;

        if(state == GREEN) {
            greenTexture.loadFromFile("images/trafficlights/green.png");
            sprite.setTexture(greenTexture);
        }
        else {
            redTexture.loadFromFile("images/trafficlights/red.png");
            sprite.setTexture(redTexture);
        }
    }
};

This is the main() function:

TrafficLight tlights[] = {
    {2*239 + 70, 250, 180, GREEN}, {2*239 + 240, 170, 90, RED},
    {4*239 + 70, 2*239 + 70, 180, RED}, {4*239 + 50, 2*239 + 170, 0, GREEN},
    {2*239 + 40, 2*239 + 170, 90, GREEN}, {2*239 + 70, 2*239 + 70, 180, RED},
    {2*239 + 200, 2*239 + 70, 270, RED}
};

TrafficLightGroup tlg1{10};
TrafficLightGroup tlg2{10};
TrafficLightGroup tlg3{10};

tlg1.add(new TrafficLight (2*239 + 70, 250, 180, GREEN));
tlg1.add(new TrafficLight (2*239 + 240, 170, 90, RED));
tlg2.add(new TrafficLight (4*239 + 70, 2*239 + 70, 180, RED));
tlg2.add(new TrafficLight (4*239 + 50, 2*239 + 170, 0, GREEN));
tlg3.add(new TrafficLight (2*239 + 40, 2*239 + 170, 90, GREEN));
tlg3.add(new TrafficLight (2*239 + 70, 2*239 + 70, 180, RED));
tlg3.add(new TrafficLight (2*239 + 200, 2*239 + 70, 270, RED));

tlg1.simulate(10); // these simulate calls are in *while (window.isOpen())* loop
tlg2.simulate(10);
tlg3.simulate(10);

I want to simulate t-lights with 10 second period. In simulate() function, I changed the states of t-lights at some specific time. However, I cannot see this change when I run the app, the program just draws the t-lights. For this reason, I added a std::cout command in simulate() function to see how elapsed time increase. Then, I saw that the elapsed time increases too fast; hence I cannot see the change in the states of t-lights.

I researched ctime header to find a function suitable for me, but I could not find anything for this issue.

How can I solve this time issue to simulate traffic lights correctly?

Utku Demir
  • 373
  • 1
  • 6
  • 14
  • Did you already consider multi-threading? – Dominique Jun 08 '20 at 13:16
  • 1
    Your `while` loop does not do any sort of waiting for 1 second per loop, the loop executes super fast. A simple solution would be to add at the end of the loop `std::chrono::milliseconds timespan(1000); std::this_thread::sleep_for(timespan)`. This will delay for 1 second but will not allow anything else in your program to function while waiting. – DNT Jun 08 '20 at 13:19
  • @Dominique, no, I did not consider it. I am a beginner in C++; therefore, I did not know this topic. Thank you for your idea. I will research it. – Utku Demir Jun 08 '20 at 13:19
  • @DNT, I use `sf::sleep(sf::seconds(0.01));` in the main() function. I tried this also in simulate() function, but the program opens after a 10 - 15 seconds delay. I did not used chrono, I will be try it, but I did not use chrono in anywhere of the code. I consider just adding a one chrono statement at the end of the loop may not change the behaviour. – Utku Demir Jun 08 '20 at 13:21
  • @Utku Yes,that is exactly the problem unless you run the traffic light code in a separate thread to allow the rest of your program to do other things. Another way is to use a sort of cooperative multitasking by not running the whole simulation in a loop, but running one step at a time, then do something else, then call the function to do the next step. This will work, also it will be more complicated, and not as smooth as running in a thread. – DNT Jun 08 '20 at 13:28
  • @DNT Okey, I should consider multi-threading. Could you recommend any website or book to study on this topic? – Utku Demir Jun 08 '20 at 13:30
  • I suggest looking into STL `std::thread`, `boost::thread` and related examples. Also you'll find many references here in SO. There are many resources online. Example: https://www.educative.io/blog/modern-multithreading-and-concurrency-in-cpp – DNT Jun 08 '20 at 13:38
  • Just another remark: you are using quite some fixed values, like 239, 170, and so on. If you keep this piece of code, and within a year you need to do some modifications, you'll have no idea anymore what those values mean. Therefore I'd advise you the usage of constants or #defines to give some meaning to those numbers, in order to increase readability (and maintainability) of your source code. – Dominique Jun 08 '20 at 14:06
  • I have one question about time elapsed. Why the time elapsed increases too fast? Maybe, there is a way to slower this time. I researched the multi-threading but I cannot see any relation between slow or fast increment of time. – Utku Demir Jun 08 '20 at 14:39
  • @Utku It's not the thread per se that will introduce the delay, but the wait you can do within the code that runs on the thread. This wait (or sleep) will only make that thread sleep and the rest of your program will be unaffected. Also look at my previous comment where I explain this. – DNT Jun 08 '20 at 14:43

1 Answers1

0

In SFML there is an sf::Clock class that solves your exact issue. You can start it using restart(), and get the elapsed time in seconds, milliseconds, or microseconds. Check out this tutorial for more info.

VoidNUL
  • 56
  • 3