2

I am creating the Environment class. The function init should build the simulated environment, while the function run should execute different events in this environment during 10 seconds.

Below I share some major parts of my code. When I run env = Environment("TEST") env.run() (see below), the window with grey screen appears. Then it closes in 10 seconds. Nothing is visible in this screen. It's just a grey background. However, I do not get any error message.

What am I doing wrong in the Environment class?

Just to mention that the same code worked fine when I had the whole code of Environment placed directly in main loop, i.e. when Environment class has not existed.

import numpy as np
import pygame
import random

WHITE = (255, 255, 255)
GREEN = (20, 255, 140)
GREY = (210, 210, 210)

SCREENWIDTH = 1000
SCREENHEIGHT = 578

IMG_WORKER_RUNNING = "images/workers/worker_1.png"
IMG_WORKER_IDLE = "images/workers/worker_2.png"
IMG_WORKER_ACCIDENT = "images/workers/accident.png"


class Worker(pygame.sprite.Sprite):
    RUNNING = 0
    IDLE = 1
    ACCIDENT = 2
    NUMBER_OF_ACCIDENTS = 0
    IMAGE_CACHE = {}

    def __init__(self, idw, image_running, image_idle, image_accident,
                 all_sprites, location, *groups):
        # Each state has it's own image
        self.images = {
            Worker.RUNNING: pygame.transform.scale(
                self.get_image(image_running),
                (45, 45)
            ),
            Worker.IDLE: pygame.transform.scale(
                self.get_image(image_idle),
                (20, 45)
            ),
            Worker.ACCIDENT: pygame.transform.scale(
                self.get_image(image_accident),
                (40, 40)
            )
        }

        self._layer = 2
        pygame.sprite.Sprite.__init__(self, groups)
        self.idw = idw
        self.reset(location)

    def reset(self, location):
        self.state = Worker.IDLE
        self.ticks_in_state = 0

        self.location = location
        self.image = self.images[self.state]
        self.rect = self.image.get_rect(topleft=location)

        self.direction = pygame.math.Vector2(0, 0)
        self.speed = random.randint(1, 3)

    def get_image(self, key):
        if not key in Worker.IMAGE_CACHE:
            Worker.IMAGE_CACHE[key] = pygame.image.load(key)
        return Worker.IMAGE_CACHE[key]


class Environment:
    def __init__(self, title):
        pygame.init()

        self.screen = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT))
        pygame.display.set_caption(title)

        self.all_sprites = pygame.sprite.LayeredUpdates()
        self.workers = pygame.sprite.Group()
        self.fences = pygame.sprite.Group()

        # create a worker
        idw = 1
        Worker(idw, IMG_WORKER_RUNNING, IMG_WORKER_IDLE,
               IMG_WORKER_ACCIDENT, self.all_sprites, (50, 50), 
               self.workers)

    def run(self):
        carry_on = True
        clock = pygame.time.Clock()
        simulation_time = 10  # time in seconds

        while carry_on:
            for event in pygame.event.get():
                if (event.type == pygame.QUIT) or (simulation_time == 0):
                    carry_on = False
                    pygame.display.quit()
                    pygame.quit()
                    quit()

                simulation_time -= 1

            agent_action = 0
            send_alert = np.random.normal(0, 0.1, 1)

            if send_alert > 0.2:
                agent_action = 1

            self.all_sprites.update(self.screen, agent_action)
            self.all_sprites.draw(self.screen)

        pygame.display.flip()
        clock.tick(20)


if __name__ == "__main__":
    env = Environment("TEST")
    env.run()
colidyre
  • 4,170
  • 12
  • 37
  • 53
ScalaBoy
  • 3,254
  • 13
  • 46
  • 84
  • Please fix the indentation of the line `def reset(self, location):`. After fixing that I'm getting `AttributeError: 'Worker' object has no attribute 'get_image'`. Is the code you are running the same as the code posted? – Micheal O'Dwyer Aug 14 '18 at 20:38
  • @MichealO'Dwyer: Fixed. Here I posted a simplified version of my code. Sorry, I missed `get_image` method and `IMAGE_CACHE` variable. Now it should be the same as I have in my laptop and you should see a blank grey screen. – ScalaBoy Aug 15 '18 at 08:35
  • http://idownvotedbecau.se/nomcve/ -- still there are missings. `IMAGE_CACHE` is not in worker class, to reproduce pictures have been set and also `GeoFence` is not available. This code isn't very helpful. Also it seems that you haven't done any debugging yourself. – colidyre Aug 15 '18 at 08:59
  • @colidyre: I've just debugged it in my laptop. Now it's running and reproduces the issue that I commented in the thread. – ScalaBoy Aug 15 '18 at 09:43
  • Just wondering. Are you really supposed not to keep a copy of the Worker object you create in `__init__(...)`? Seems like it should be added to a workers list as a member of the env object. Maybe the worker object gets destroyed when you exit `__init__`. Just a hunch, not sure. – André C. Andersen Aug 15 '18 at 10:12
  • @AndréChristofferAndersen: Could you please post an answer with your suggestion and demonstrate how it works? – ScalaBoy Aug 15 '18 at 20:05
  • @ScalaBoy I don't feel a hunch is worthy of a full answer. – André C. Andersen Aug 15 '18 at 21:26

1 Answers1

2

You have to pass the all_sprites argument in the Worker.__init__ method to the __init__ method of the Sprite class, so that the sprite will be added to this group.

pygame.sprite.Sprite.__init__(self, all_sprites, groups)

You could also rearrange the parameters of the __init__ method and pass the two groups as the last arguments (they will be added to the groups list).

# In the Worker class.
def __init__(self, idw, image_running, image_idle, image_accident,
             location, *groups):

# Pass the two groups as the `groups` argument.
Worker(idw, IMG_WORKER_RUNNING, IMG_WORKER_IDLE,
       IMG_WORKER_ACCIDENT, (50, 50), self.all_sprites, 
       self.workers)

In the while loop you need to blit a background surface or fill the screen every frame in order to clear it, then draw the sprites and finally update the screen with pygame.display.flip() (you need to indent it in your example).

while carry_on:
    for event in pygame.event.get():
        if (event.type == pygame.QUIT) or (simulation_time == 0):
            carry_on = False
        # You probably don't want to decrement the
        # simulation_time once per event. Dedent
        # this line to decrement it once per frame.
        simulation_time -= 1

    agent_action = 0
    send_alert = np.random.normal(0, 0.1, 1)

    if send_alert > 0.2:
        agent_action = 1

    self.all_sprites.update(self.screen, agent_action)
    # Blit a background surface or fill the screen.
    self.screen.fill((0, 40, 0))  # Fill with dark green.
    # Blit all sprites.
    self.all_sprites.draw(self.screen)
    # Update the screen.
    pygame.display.flip()
    clock.tick(20)
skrx
  • 19,980
  • 5
  • 34
  • 48
  • If you need a timer, check out these solutions: https://stackoverflow.com/questions/30720665/countdown-timer-in-pygame – skrx Aug 15 '18 at 12:50
  • Thanks. I followed your indications. I have problems with `all_sprites`. If I pass `all_sprites` in the `groups` list as you showed, then in my real code I get the error `NameError: name 'all_sprites' is not defined` at the lines: `self.icon = Icon(self.emo_images[self.emo_state], self.rect.bottomright). self.icon.add(all_sprites)` in the init` function of `Worker`. However, I was not getting this error when I was not using `Environment` class. Do you know how to fix it? – ScalaBoy Aug 15 '18 at 19:31
  • If you need the `all_sprites` argument in the `__init__` method, then either pass it as a separate argument again or replace `all_sprites` with `groups[0]`. The `groups` argument is actually a list and the star `*` means all arguments that you pass after `location` will be appended to this list. So if you pass `self.all_sprites, self.workers` as the sixth and seventh arguments, groups will be a list with `self.all_sprites` as the first and `self.workers` as the second element. https://stackoverflow.com/questions/3394835/args-and-kwargs – skrx Aug 15 '18 at 21:58