pythonpygamepython-multiprocessingpython-multithreadingpygame-clock

How to freeze specific instance in pygame without affecting others


I am using pygame to develop a train simulator (using simply a rect to represent a train) I have a class train and this class has a stop function to stop trains at each station (defined by an x coordinate):

def stop(self):

    current_time = pg.time.get_ticks()

    while pg.time.get_ticks() - current_time < self.stopping_Time:
        continue
    pass

This implementation works with one train instance but my problem is that when adding more trains, if an instance stop at its station all the other train instances stop even if they are not in there station!

I have tried this implementaion also and it didn't work:

def stop(self):
    while self.stopping_Time> 0:
        self.stopping_Time -= 1
    pass

This answer didn't work for me also: https://stackoverflow.com/a/46801334/11334093

Is it a multithreading problem? do I need to create a thread for each train instance, so they can execute the stop function independently? Or how can I use a multiprocessing trick for this function?

Here is my whole train class:

class train(object):
    """docstring for train"""
    def __init__(self, x, y, width, height, vel):

        self.x = x
        self.y = y
        self.width = width
        self.height = height

        self.vel = vel
        self.right = True
        self.left = False
        self.headway = 20
        self.stopping_Time = 2500
        self.capacity = 200
        self.start_time = pg.time.get_ticks()

    def draw(self, win):
        #trains
        pg.draw.rect(win, (255,0,0), (self.x, self.y, self.width, self.height))


    def stop(self):

        self.current_time = pg.time.get_ticks()

        while pg.time.get_ticks() - self.current_time < self.stopping_Time:
            continue
        pass

    def move(self):

        if self.right:

            if self.x == 235 or self.x == 510 or self.x == 1295:

                self.stop()

                if self.x == 1295:
                    self.right = False
                    self.left = True
                    self.x -= self.vel

                else:
                    self.x += self.vel

            else:
                self.x += self.vel 

        else:

            if self.x == 235 or self.x == 510:
                #train.stop = 3 * 100
                self.stop()

                if self.x == 235:
                    self.right = True
                    self.left = False
                    self.x += self.vel

                else:
                    self.x -= self.vel
            else:
                self.x -= self.vel

and I have another function which in it I call:

for train in op_trains:
    train.move()

op_train is a list containing all train instances and it is filled by one train at a time.


Solution

  • I would strongly recommend against using multi-threading/processing for this problem of yours. While multi-threading/processing may be able to solve your problem, introducing it might make it hard to add to or improve the code in the future and will more than likely cause more bugs. This problem can be solved by just tweaking the design of the code.

    The reason why all trains stop at once is due to the while loop in the stop function.

    def stop(self):
    
        current_time = pg.time.get_ticks()
    
        while pg.time.get_ticks() - current_time < self.stopping_Time:
            continue
        pass
    

    This while loop blocks the main thread, preventing pygame from processing other trains. The stop function is called from the move function within the train class. The goal now is to replace this while loop with a way to keep track of how long to keep the train stopped that does not block the main thread.

    What you need to do is to have a state in your train class to represent whether or not the train is stopped or not.

        def __init__(self, x, y, width, height, vel):
    
            self.x = x
            self.y = y
            self.width = width
            self.height = height
    
            self.vel = vel
            self.right = True
            self.left = False
            self.headway = 20
            self.stopping_Time = 2500
            self.capacity = 200
            self.start_time = pg.time.get_ticks()
            # new attributes
            self.stopped = False
            self.stopped_time = None
    

    To stop the train, toggle the state and record down the time the stop started.

        def stop(self):
            self.stopped_time = pg.time.get_ticks()
            self.stopped = True
    

    In your move function, have an if condition to check if this flag is True and if it is, how long has it been stopped. If it has stopped for long enough, set the flag to False and move on. I have went ahead and reordered the function as well as cleaned up the conditions as well.

        def move(self):
            if self.stopped and pg.time.get_ticks() - self.stopped_time < self.stopping_Time:
                    return
            self.stopped = False
            if self.right:
                self.x += self.vel 
            else:
                self.x -= self.vel
    
            if self.x == 235 or self.x == 510 or self.x == 1295:
                self.stop()
    
            if self.x == 235:
                self.right = True
                self.left = False
            elif self.x == 1295:
                self.right = False
                self.left = True
    

    Notice that there are no loops.

    This code assumes that your calls to train.move() is in a main event loop of some sort where it is continuously being called.