如何找到 Sprite 和屏幕角之间的距离(以像素为单位)? [英] How to find the distance (in pixels) between the Sprite and screen corners?

查看:40
本文介绍了如何找到 Sprite 和屏幕角之间的距离(以像素为单位)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要找到动画 worker 对象与屏幕的 4 个角(左上、右上、左下、右下)之间的距离(以像素为单位).pygame 的哪个函数给出了这个信息?我需要在每次 update 迭代时获取此信息.

I need to find the distance (in pixels) between the animated worker objects and the 4 corners of the screen (upper left, upper right, lower left, lower right). Which function of pygame gives this information? I need to get this information at each update iteration.

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)

推荐答案

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

You could create point vectors for the corners and then just subtract the position of the sprite (I just use the mouse pos here) to get vectors that point to the corners and finally call the length method to get the lengths of these vectors.

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)

这篇关于如何找到 Sprite 和屏幕角之间的距离(以像素为单位)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆