代码之家  ›  专栏  ›  技术社区  ›  ScalaBoy

如何找到精灵和屏幕角点之间的距离(以像素为单位)?

  •  2
  • ScalaBoy  · 技术社区  · 6 年前

    worker 对象和屏幕的4个角(左上、右上、左下、右下)。 哪个功能 pygame update 迭代。

    import pygame, random
    import sys
    
    WHITE = (255, 255, 255)
    GREEN = (20, 255, 140)
    GREY = (210, 210 ,210)
    RED = (255, 0, 0)
    PURPLE = (255, 0, 255)
    
    SCREENWIDTH=1000
    SCREENHEIGHT=578
    
    IMG_BACKGROUND = "background.jpg"
    IMG_WORKER_RUNNING = "images/workers/worker_1.png"
    IMG_WORKER_IDLE = "images/workers/worker_2.png"
    IMG_WORKER_ACCIDENT = "images/workers/accident.png"
    
    
    class Background(pygame.sprite.Sprite):
        def __init__(self, image_file, location, *groups):
            # we set a _layer attribute before adding this sprite to the sprite groups
            # we want the background to be actually in the back
            self._layer = -1
            pygame.sprite.Sprite.__init__(self, groups)
            # let's resize the background image now and only once
            self.image = pygame.transform.scale(pygame.image.load(image_file).convert(), (SCREENWIDTH, SCREENHEIGHT))
            self.rect = self.image.get_rect(topleft=location)
    
    
    
    class GeoFenceInfluenceZone(pygame.sprite.Sprite):
        def __init__(self, rect, *groups):
            # we set a _layer attribute before adding this sprite to the sprite groups
            self._layer = 0
            pygame.sprite.Sprite.__init__(self, groups)
            self.image = pygame.surface.Surface((rect.width, rect.height))
            self.image.fill(GREY)
            self.rect = rect
    
    
    class GeoFence(pygame.sprite.Sprite):
        def __init__(self, rect, risk_level, *groups):
            # we set a _layer attribute before adding this sprite to the sprite groups
            self._layer = 1
            pygame.sprite.Sprite.__init__(self, groups)
            self.image = pygame.surface.Surface((rect.width, rect.height))
            self.image.fill(GREEN)
            self.rect = rect
            self.risk_level = risk_level
            self.font = pygame.font.SysFont('Arial', 20)
            text = self.font.render(risk_level, 1, (255,0,0), GREEN)
            text_rect = text.get_rect(center=(rect.width/2, rect.height/2))
            self.image.blit(text, text_rect)
    
    
    
    class Worker(pygame.sprite.Sprite):
    
        # we introduce to possible states: RUNNING and IDLE
        RUNNING = 0
        IDLE = 1
        ACCIDENT = 2
        NUMBER_OF_ACCIDENTS = 0
    
        def __init__(self, image_running, image_idle, image_accident, location, *groups):
    
            self.font = pygame.font.SysFont('Arial', 10)
    
            # each state has it's own image
            self.images = {
                Worker.RUNNING: pygame.transform.scale(get_image(image_running), (45, 45)),
                Worker.IDLE: pygame.transform.scale(get_image(image_idle), (20, 45)),
                Worker.ACCIDENT: pygame.transform.scale(get_image(image_accident), (40, 40))
            }
    
            # we set a _layer attribute before adding this sprite to the sprite groups
            # we want the workers on top
            self._layer = 2
            pygame.sprite.Sprite.__init__(self, groups)
    
            # let's keep track of the state and how long we are in this state already            
            self.state = Worker.IDLE
            self.ticks_in_state = 0
    
            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)
            self.set_random_direction()
    
    
        def set_random_direction(self):
            # random new direction or standing still
            vec = pygame.math.Vector2(random.randint(-100,100), random.randint(-100,100)) if random.randint(0, 5) > 1 else pygame.math.Vector2(0, 0)
    
            # check the new vector and decide if we are running or fooling around
            length = vec.length()
            speed = sum(abs(int(v)) for v in vec.normalize() * self.speed) if length > 0 else 0
    
            if (length == 0 or speed == 0) and (self.state != Worker.ACCIDENT):
                new_state = Worker.IDLE
                self.direction = pygame.math.Vector2(0, 0)
            elif self.state != Worker.ACCIDENT:
                new_state = Worker.RUNNING
                self.direction = vec.normalize()
            else:
                new_state = Worker.ACCIDENT
    
            self.ticks_in_state = 0
            self.state = new_state
    
            # use the right image for the current state
            self.image = self.images[self.state]
    
    
        def update(self, screen):
            self.ticks_in_state += 1
            # the longer we are in a certain state, the more likely is we change direction
            if random.randint(0, self.ticks_in_state) > 70:
                self.set_random_direction()
    
            # now let's multiply our direction with our speed and move the rect
            vec = [int(v) for v in self.direction * self.speed]
            self.rect.move_ip(*vec)
    
            # if we're going outside the screen, change direction
            if not screen.get_rect().contains(self.rect):
                self.direction = self.direction * -1
    
            # spritecollide returns a list of all sprites in the group that collide with
            # the given sprite, but if the sprite is in this group itself, we have
            # to ignore a collision with itself
            if any(s for s in pygame.sprite.spritecollide(self, building_materials, False) if s != self):
                self.direction = self.direction * -1
    
            if any(s for s in pygame.sprite.spritecollide(self, machines, False) if s != self):
                self.direction = self.direction * -1
    
            # Risk handling
            self.handle_risks()
    
            self.rect.clamp_ip(screen.get_rect())
    
    
        def handle_risks(self):
            for s in pygame.sprite.spritecollide(self, fences, False):
                if s != self:
                    self.speed = 0
                    self.state = Worker.ACCIDENT
                    self.image = self.images[self.state]
                    Worker.NUMBER_OF_ACCIDENTS += 1
    
    
    
    class BuildingMaterials(pygame.sprite.Sprite):
        def __init__(self, image_file, location, *groups):
            # we set a _layer attribute before adding this sprite to the sprite groups
            self._layer = 2
            pygame.sprite.Sprite.__init__(self, groups)
            self.image = pygame.transform.scale(pygame.image.load(image_file).convert_alpha(), (40, 40))
            self.rect = self.image.get_rect(topleft=location)
    
    
    
    class Excavator(pygame.sprite.Sprite):
        def __init__(self, image_file, location, *groups):
            # we set a _layer attribute before adding this sprite to the sprite groups
            self._layer = 3
            pygame.sprite.Sprite.__init__(self, groups)
            self.image = pygame.transform.scale(pygame.image.load(image_file).convert_alpha(), (170, 170))
            self.rect = self.image.get_rect(topleft=location)
    
    
    
    image_cache = {}
    def get_image(key):
        if not key in image_cache:
            image_cache[key] = pygame.image.load(key)
        return image_cache[key]
    
    
    pygame.init()
    
    # currently, one group would be enough
    # but if you want to use some collision handling in the future
    # it's best to group all sprites into special groups (no pun intended)
    all_sprites = pygame.sprite.LayeredUpdates()
    workers = pygame.sprite.Group()
    building_materials = pygame.sprite.Group()
    fences = pygame.sprite.Group()
    fences_infl_zones = pygame.sprite.Group()
    
    screen = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT))
    pygame.display.set_caption("TEST")
    
    # create multiple workers
    for pos in ((30,30), (50, 400), (200, 100), (700, 200)):
        Worker(IMG_WORKER_RUNNING, IMG_WORKER_IDLE, IMG_WORKER_ACCIDENT, pos, all_sprites, workers, building_materials, machines, fences)
    
    # create multiple building material stocks
    for pos in ((50,460),(50,500),(100,500),(850,30),(800,30)):
        BuildingMaterials("images/materials/building_blocks{}.png".format(random.randint(1,3)), pos, all_sprites, building_materials)
    
    # create multiple geo-fences
    risks = ["H","M","L"]
    for rect in (pygame.Rect(510,150,75,52), pygame.Rect(450,250,68,40), pygame.Rect(450,370,68,48),
                 pygame.Rect(0,0,20,SCREENHEIGHT),pygame.Rect(0,0,SCREENWIDTH,20),
                 pygame.Rect(SCREENWIDTH-20,0,20,SCREENHEIGHT),pygame.Rect(0,SCREENHEIGHT-20,SCREENWIDTH,20)):
        risk = risks[random.randint(0,2)]
        GeoFence(rect, risk, all_sprites, fences)
    
    # create influence zones for all geo-fences
    for rect in (pygame.Rect(495,135,105,80), pygame.Rect(435,235,98,68), pygame.Rect(435,355,98,76)):
        GeoFenceInfluenceZone(rect, all_sprites, fences_infl_zones)
    
    # and the background
    Background(IMG_BACKGROUND, [0,0], all_sprites)
    
    carryOn = True
    clock = pygame.time.Clock()
    while carryOn:
        for event in pygame.event.get():
            if event.type==pygame.QUIT:
                carryOn = False
                pygame.display.quit()
                pygame.quit()
                quit()
    
        all_sprites.update(screen)
        all_sprites.draw(screen)
    
        pygame.display.flip()
    
        clock.tick(20)
    
    1 回复  |  直到 6 年前
        1
  •  2
  •   skrx    6 年前

    您可以为角点创建点向量,然后减去精灵的位置(这里我只使用鼠标位置)来获得指向角点的向量,最后调用 length 方法获取这些向量的长度。

    import pygame as pg
    from pygame.math import Vector2
    
    
    pg.init()
    screen = pg.display.set_mode((640, 480))
    width, height = screen.get_size()
    clock = pg.time.Clock()
    BG_COLOR = pg.Color('gray12')
    # Create point vectors for the corners.
    corners = [
        Vector2(0, 0), Vector2(width, 0),
        Vector2(0, height), Vector2(width, height),
        ]
    
    done = False
    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True
    
        mouse_pos = pg.mouse.get_pos()
        # Subtract the position from the point vectors and call the `length`
        # method of the resulting vectors to get the distances to the points.
        # Subtract the position from the point vectors and call the `length`
        # method of the resulting vectors to get the distances to the points.
        distances = []
        for vec in corners:
            distance = (vec - mouse_pos).length()
            distances.append(distance)
    
        # The 4 lines above can be replaced by a list comprehension.
        # distances = [(vec - mouse_pos).length() for vec in corners]
    
        # I just display the distances in the window title here.
        pg.display.set_caption('{:.1f}, {:.1f}, {:.1f}, {:.1f}'.format(*distances))
        screen.fill(BG_COLOR)
        pg.display.flip()
        clock.tick(60)