【问题标题】:How to put an additional image or rectangle on the bottom of Sprite?如何在 Sprite 的底部添加额外的图像或矩形?
【发布时间】:2018-08-12 16:42:54
【问题描述】:

我需要在 Sprite worker 的底部添加一个图标,然后在每次迭代时随机更改此图标。请注意,Sprite worker 有 2 个状态:RUNNINGIDLE。在这些状态中的每一个中,worker 都有一个特定的图像。我现在需要的是在 worker 的底部 上放置一个 附加 小图像,用于指定 情绪状态HAPPYANGRY .

Worker 类中,我创建了数组emo_images 并指定了变量emo_state。此变量表示worker 的情绪状态:高兴或生气。每个情绪状态的图像都存储在emotional_images

在代码中我随机生成变量state_indicator。如果大于9,则worker的情绪状态变为ANGRY。否则,它是快乐的。

   state_indicator = random.randint(0,10)
    if state_indicator > 9:
        print(state_indicator)
        self.alert_notif_worker()

    def alert_notif_worker(self):
        self.emo_state = Worker.ANGRY

但是我不知道如何将情感图像放在worker 图像的底部,因为我不想替换worker 图像(IDLE,RUNNING)。我只需要在底部添加另一个图像,这个附加图像应该与worker 一起移动。

如果很难做到,那么用红色和绿色两种颜色的矩形代替图像也可以,以表示情绪状态。

完整代码:

import sys
import pygame, random
from pygame.math import Vector2
from scipy.optimize import minimize
import math


WHITE = (255, 255, 255)
GREEN = (20, 255, 140)
GREY = (210, 210 ,210)
BLACK = (0, 0 ,0)
RED = (255, 0, 0)
PURPLE = (255, 0, 255)


SCREENWIDTH=1000
SCREENHEIGHT=578
# Create point vectors for the corners.
corners = [
    Vector2(0, 0), Vector2(SCREENWIDTH, 0),
    Vector2(SCREENWIDTH, SCREENHEIGHT), Vector2(0, SCREENHEIGHT)
    ]

ABS_PATH = "/Users/sb/Desktop/"
IMG_BACKGROUND = ABS_PATH + "images/background.jpg"
IMG_WORKER_RUNNING = ABS_PATH + "images/workers/worker_1.png"
IMG_WORKER_IDLE = ABS_PATH + "images/workers/worker_2.png"
IMG_WORKER_ACCIDENT = ABS_PATH + "images/workers/accident.png"
IMG_WORKER_HAPPY = ABS_PATH + "images/workers/happy.png"
IMG_WORKER_ANGRY = ABS_PATH + "images/workers/angry.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 Worker(pygame.sprite.Sprite):

    RUNNING = 0
    IDLE = 1
    HAPPY = 0
    ANGRY = 1
    IMAGE_CACHE = {}

    def __init__(self, idw, image_running, image_idle, image_happy, image_angry, location, *groups):

        self.font = pygame.font.SysFont('Arial', 20)

        # 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))
        }

        self.emo_images = {
            Worker.HAPPY: pygame.transform.scale(self.get_image(image_happy), (20, 20)),
            Worker.ANGRY: pygame.transform.scale(self.get_image(image_angry), (20, 20))
        }


        # we set a _layer attribute before adding this sprite to the sprite groups
        # we want the workers on top
        self._layer = 0
        pygame.sprite.Sprite.__init__(self, groups)

        self.idw = idw

        # let's keep track of the state and how long we are in this state already            
        self.state = Worker.IDLE
        self.emo_state = Worker.HAPPY
        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 not
        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)
        else:
            new_state = Worker.RUNNING
            self.direction = vec.normalize()

        self.ticks_in_state = 0
        self.state = new_state

        # use the right image for the current state
        self.image = self.images[self.state]
        #self.emo_image = self.emo_images[self.emo_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


        send_alert = random.randint(0,10)
        if send_alert > 9:
            print(send_alert)
            self.alert_notif_worker()

        self.rect.clamp_ip(screen.get_rect())


    def alert_notif_worker(self):
        self.emo_state = Worker.ANGRY


    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]




pygame.init()

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

screen = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT))
pygame.display.set_caption("TEST")

# create multiple workers
idw = 1
for pos in ((30,30), (50, 400), (200, 100), (700, 200)):
    Worker(idw, IMG_WORKER_RUNNING, IMG_WORKER_IDLE, 
           IMG_WORKER_HAPPY, IMG_WORKER_ANGRY, 
           pos, all_sprites, workers)
    idw+=1

# 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)

【问题讨论】:

    标签: python python-3.x pygame


    【解决方案1】:

    我要么使用 Micheal O'Dwyer 的解决方案并在单独的 for 循环中对图标图像进行 blit,要么创建一个 Icon sprite 类,它可以作为属性添加到 Worker 类。然后你可以在 update 方法中更新图标精灵的位置,并在工人状态发生变化时交换图像。

    您需要一个LayeredUpdates 组,以便图标出现在工人精灵上方。

    import pygame as pg
    from pygame.math import Vector2
    
    
    pg.init()
    WORKER_IMG = pg.Surface((30, 50))
    WORKER_IMG.fill(pg.Color('dodgerblue1'))
    ICON_HAPPY = pg.Surface((12, 12))
    ICON_HAPPY.fill(pg.Color('yellow'))
    ICON_ANGRY = pg.Surface((10, 10))
    ICON_ANGRY.fill(pg.Color('red'))
    
    
    class Worker(pg.sprite.Sprite):
    
        def __init__(self, pos, all_sprites):
            super().__init__()
            self._layer = 0
            self.image = WORKER_IMG
            self.rect = self.image.get_rect(center=pos)
            self.state = 'happy'
            self.emo_images = {'happy': ICON_HAPPY, 'angry': ICON_ANGRY}
            # Create an Icon instance pass the image, the position
            # and add it to the all_sprites group.
            self.icon = Icon(self.emo_images[self.state], self.rect.bottomright)
            self.icon.add(all_sprites)
    
        def update(self):
            # Update the position of the icon sprite.
            self.icon.rect.topleft = self.rect.bottomright
    
        def change_state(self):
            """Change the state from happy to angry and update the icon."""
            self.state = 'happy' if self.state == 'angry' else 'angry'
            # Swap the icon image.
            self.icon.image = self.emo_images[self.state]
    
    
    class Icon(pg.sprite.Sprite):
    
        def __init__(self, image, pos):
            super().__init__()
            self._layer = 1
            self.image = image
            self.rect = self.image.get_rect(topleft=pos)
    
    
    def main():
        screen = pg.display.set_mode((640, 480))
        clock = pg.time.Clock()
        all_sprites = pg.sprite.LayeredUpdates()
        worker = Worker((50, 80), all_sprites)
        all_sprites.add(worker)
    
        done = False
    
        while not done:
            for event in pg.event.get():
                if event.type == pg.QUIT:
                    done = True
                elif event.type == pg.MOUSEMOTION:
                    worker.rect.center = event.pos
                elif event.type == pg.KEYDOWN:
                    worker.change_state()
    
            all_sprites.update()
            screen.fill((30, 30, 30))
            all_sprites.draw(screen)
    
            pg.display.flip()
            clock.tick(60)
    
    
    if __name__ == '__main__':
        main()
        pg.quit()
    

    【讨论】:

      【解决方案2】:

      这可以相当容易地完成,只需将每个Worker 的情感图像,在特定位置,与Workerrect.xrect.y 坐标相关联。

      很遗憾,我无法测试下面的代码示例,因为您的代码使用了很多我没有的图像。在尝试实现此代码之前,我确实看到您需要解决的一个问题是您正在初始化的Background 对象正在添加到all_sprites,因此您可以考虑将all_sprites 更改为all_workers,并且也许将Background 添加到不同的组。

      您还需要将offset_xoffset_y 初始化为适合您的值。下面使用的值只会将图像移动到工人的左下角。

      示例代码如下:

      for worker in all_workers:
         offset_x = 0
         offset_y = worker.rect.height
         screen.blit(worker.emo_images[worker.emo_state], (worker.rect.x+offset_x, worker.rect.y+offset_y))
      

      希望这个回答对你有帮助!请让我知道这是否适合您,如果您有任何其他问题,请随时在下面发表评论。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-05-15
        • 1970-01-01
        • 1970-01-01
        • 2021-11-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-02
        相关资源
        最近更新 更多