如何围绕矩形居中放置一个表面(子表面)?(缩放的精灵Hitbox /碰撞矩形)

发布于 2021-01-29 17:57:36

目前,我有工作代码,可以循环通过一个Spritesheet,将每个单元格/图像(总共9个)作为子表面添加到列表中。随着游戏的更新,我将Player图像设置为代码在其中索引的当前单元格。同时,我还有一个固定的矩形,用作精灵的“
hitbox” /碰撞矩形。

但是,将次表面设置为新图像,我发现精灵从碰撞矩形的左上角缩放。由于子图形比碰撞矩形大得多,因此将碰撞矩形放置在远离实际char模型/子图形的位置。

我试图将地下/曲面图像居中放置在碰撞矩形周围,而不是从左上角缩放。

这是我的代码:

import pygame as pg
from settings import *
vec = pg.math.Vector2

class Civilian(pg.sprite.Sprite):
    def __init__(self, game, x, y):
        self.groups = game.all_sprites, game.player1group
        pg.sprite.Sprite.__init__(self, self.groups)
        self.game = game
        self.image = pg.Surface((TILESIZE, TILESIZE))
        self.rect = self.image.get_rect()
        self.vel = vec(0, 0)
        self.pos = vec(x , y)
        self.move = 0

    def animate(self, direction):
        if direction == 'right':
            self.spritesheet = pg.image.load('walk right.png') # Loading the right directional movement spritesheet into the variable
        if direction == 'left':
            self.spritesheet = pg.image.load('walk left.png')
        if direction == 'up':
            self.spritesheet = pg.image.load('walk up.png')
        if direction == 'down':
            self.spritesheet = pg.image.load('walk down.png')
        self.frames = [] # List which will contain each cell of the spritesheet
        # Adding the cells to the list #
        self.frames.append(self.spritesheet.subsurface(pg.Rect(0, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(61, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(122, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(183, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(244, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(305, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(366, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(427, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(488, 0, 61, 67)).convert_alpha())
        # Number of frames/cells
        self.frames_number = len(self.frames)
        # Current animation frame
        self.current_frame = 0
        # Frame rectangle
        self.frame_rect = self.frames[0].get_rect()


    def get_keys(self):
        self.vel= vec(0, 0)
        keys = pg.key.get_pressed()

        if keys[pg.K_a]:    # Const. subtracts player speed from velocity (E.g. Moves sprite to the left)
            self.vel.x= -PLAYER_SPEED
            self.move += 1
            self.moving = 'left' # Uses different spritesheet depending on direction
        elif keys[pg.K_d]:    # Const. adds player speed value to velocity (E.g. Moves sprite to the right)
            self.vel.x= PLAYER_SPEED
            self.move += 1
            self.moving = 'right'
        elif keys[pg.K_w]:    # Const. subtracts player speed value from y velocity (Moves player upwards; opposite)
            self.vel.y= -PLAYER_SPEED
            self.move += 1
            self.moving = 'up'
        elif keys[pg.K_s]: # Const. adds player speed value to y velocity (Moves player downwards; opposite)
            self.vel.y= PLAYER_SPEED
            self.move += 1
            self.moving = 'down'
        if self.vel.x != 0 and self.vel.y != 0:   # Offsetting increased vecocity when moving diagonally (Has both x and y velocity)
            self.vel *= 0.7071

    def collide_with_player2(self, dir, ifColliding):
        if dir == 'x':
            collides = pg.sprite.spritecollide(self, self.game.player2group, False)
            if collides:
                if self.vel.x > 0:
                    self.pos.x = collides[0].rect.left - self.rect.width
                if self.vel.x < 0:
                    self.pos.x = collides[0].rect.right
                self.vel.x = 0
                self.rect.x = self.pos.x
                print("collide x")
                self.ifColliding = True

        if dir == 'y':
            collides = pg.sprite.spritecollide(self, self.game.player2group, False)
            if collides:
                if self.vel.y > 0:
                    self.pos.y = collides[0].rect.top - self.rect.height
                if self.vel.y < 0:
                    self.pos.y = collides[0].rect.bottom
                self.vel.y = 0
                self.rect.y = self.pos.y
                print("collide y")
                self.ifColliding = True



    def collide_with_walls(self, dir):
        if dir == 'x':
            collides = pg.sprite.spritecollide(self, self.game.walls, False)
            if collides:
                if self.vel.x > 0:
                    self.pos.x = collides[0].rect.left - self.rect.width
                if self.vel.x < 0:
                    self.pos.x = collides[0].rect.right
                self.vel.x = 0
                self.rect.x = self.pos.x
        if dir == 'y':
            collides = pg.sprite.spritecollide(self, self.game.walls, False)
            if collides:
                if self.vel.y > 0:
                    self.pos.y = collides[0].rect.top - self.rect.height
                if self.vel.y < 0:
                    self.pos.y = collides[0].rect.bottom
                self.vel.y = 0
                self.rect.y = self.pos.y





    def update(self):
        # frame updates
        self.moving = 'idle'
        self.animate('down') # Sets the down spritesheet as default
        self.get_keys()
        if self.moving == 'up':
            self.animate(self.moving) # Uses the up-movement spritesheet if char moving upwards
        if self.moving == 'down':
            self.animate(self.moving) # Same as above, different direction
        if self.moving == 'left':
            self.animate(self.moving)
        if self.moving == 'right':
            self.animate(self.moving)
        # frame updates
        self.ifColliding = False

        self.pos += self.vel * self.game.dt
        self.rect.x = self.pos.x
        self.collide_with_walls('x'), self.collide_with_player2('x', self.ifColliding)
        self.rect.y = self.pos.y
        self.collide_with_walls('y'), self.collide_with_player2('y', self.ifColliding)
        if self.ifColliding == True:
            Thief.health -= COL_DAMAGE
            print(Thief.health)
        self.current_frame = (self.current_frame + self.move) % self.frames_number
        if self.moving == 'idle':
            self.current_frame = 0
        self.image = self.frames[self.current_frame] # Image of sprite changes as program cycles through the sheet

简而言之,Id希望将self.image曲面居中在self.rect(碰撞矩形)上。

[编辑]

我试图在colliion函数(collide_with_player2,collide_with_walls)中将对self.rect的引用更改为self.col_rect,希望这样可以起作用,但是事实并非如此。

如前所述,我创建了一个新的矩形,希望将其用于碰撞,以便将self.rect用于图像斑点处理,并将self.col_rect用于碰撞。尽管效率低下,但我仍然希望将此作为解决此问题的临时方法。我是pygame的新手,所以我希望有人可以帮助我将碰撞中使用的矩形从self.rect更改为self.col_rect。再次,任何反馈将不胜感激!

更新的代码:

import pygame as pg
from settings import *
vec = pg.math.Vector2

class Civilian(pg.sprite.Sprite):
    def __init__(self, game, x, y):
        self.groups = game.all_sprites, game.player1group, game.bothplayers
        pg.sprite.Sprite.__init__(self, self.groups)
        self.game = game
        self.image = pg.Surface((61, 67))
        self.rect = self.image.get_rect()
        self.col_rect = self.rect.inflate(-40, -40)
        self.vel = vec(0, 0)
        self.pos = vec(x , y)
        self.move = 0

    def animate(self, direction):
        if direction == 'right':
            self.spritesheet = pg.image.load('walk right civ.png') # Loading the right directional movement spritesheet into the variable
        if direction == 'left':
            self.spritesheet = pg.image.load('walk left civ.png')
        if direction == 'up':
            self.spritesheet = pg.image.load('walk up civ.png')
        if direction == 'down':
            self.spritesheet = pg.image.load('walk down civ.png')
        self.frames = [] # List which will contain each cell of the spritesheet
        # Adding the cells to the list #
        self.frames.append(self.spritesheet.subsurface(pg.Rect(0, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(61, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(122, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(183, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(244, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(305, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(366, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(427, 0, 61, 67)).convert_alpha())
        self.frames.append(self.spritesheet.subsurface(pg.Rect(488, 0, 61, 67)).convert_alpha())
        # Number of frames/cells
        self.frames_number = len(self.frames)
        # Current animation frame
        self.current_frame = 0
        # Frame rectangle
        self.frame_rect = self.frames[0].get_rect()


    def get_keys(self):
        self.vel= vec(0, 0)
        keys = pg.key.get_pressed()

        if keys[pg.K_a]:    # Const. subtracts player speed from velocity (E.g. Moves sprite to the left)
            self.vel.x= -PLAYER_SPEED
            self.move += 1
            self.moving = 'left' # Uses different spritesheet depending on direction
        elif keys[pg.K_d]:    # Const. adds player speed value to velocity (E.g. Moves sprite to the right)
            self.vel.x= PLAYER_SPEED
            self.move += 1
            self.moving = 'right'
        elif keys[pg.K_w]:    # Const. subtracts player speed value from y velocity (Moves player upwards; opposite)
            self.vel.y= -PLAYER_SPEED
            self.move += 1
            self.moving = 'up'
        elif keys[pg.K_s]: # Const. adds player speed value to y velocity (Moves player downwards; opposite)
            self.vel.y= PLAYER_SPEED
            self.move += 1
            self.moving = 'down'
        if self.vel.x != 0 and self.vel.y != 0:   # Offsetting increased vecocity when moving diagonally (Has both x and y velocity)
            self.vel *= 0.7071

    def collide_with_player2(self, dir, ifColliding):
        if dir == 'x':
            collides = pg.sprite.spritecollide(self, self.game.player2group, False)
            if collides:
                if self.vel.x > 0:
                    self.pos.x = collides[0].rect.left - self.rect.width
                if self.vel.x < 0:
                    self.pos.x = collides[0].rect.right
                self.vel.x = 0
                self.rect.x = self.pos.x
                print("collide x")
                self.ifColliding = True

        if dir == 'y':
            collides = pg.sprite.spritecollide(self, self.game.player2group, False)
            if collides:
                if self.vel.y > 0:
                    self.pos.y = collides[0].rect.top - self.rect.height
                if self.vel.y < 0:
                    self.pos.y = collides[0].rect.bottom
                self.vel.y = 0
                self.rect.y = self.pos.y
                print("collide y")
                self.ifColliding = True



    def collide_with_walls(self, dir):
        if dir == 'x':
            collides = pg.sprite.spritecollide(self, self.game.walls, False)
            if collides:
                if self.vel.x > 0:
                    self.pos.x = collides[0].rect.left - self.rect.width
                if self.vel.x < 0:
                    self.pos.x = collides[0].rect.right
                self.vel.x = 0
                self.rect.x = self.pos.x
        if dir == 'y':
            collides = pg.sprite.spritecollide(self, self.game.walls, False)
            if collides:
                if self.vel.y > 0:
                    self.pos.y = collides[0].rect.top - self.rect.height
                if self.vel.y < 0:
                    self.pos.y = collides[0].rect.bottom
                self.vel.y = 0
                self.rect.y = self.pos.y





    def update(self):
        # frame updates
        self.moving = 'idle'
        self.animate('down') # Sets the down spritesheet as default
        self.get_keys()
        if self.moving == 'up':
            self.animate(self.moving) # Uses the up-movement spritesheet if char moving upwards
        if self.moving == 'down':
            self.animate(self.moving) # Same as above, different direction
        if self.moving == 'left':
            self.animate(self.moving)
        if self.moving == 'right':
            self.animate(self.moving)

        self.ifColliding = False
        self.pos += self.vel * self.game.dt
        self.rect.x = self.pos.x
        self.collide_with_walls('x'), self.collide_with_player2('x', self.ifColliding)
        self.col_rect.centerx = self.rect.centerx
        self.rect.y = self.pos.y
        self.collide_with_walls('y'), self.collide_with_player2('y', self.ifColliding)
        self.col_rect.centery = self.rect.centery
        if self.ifColliding == True:
            Thief.health -= COL_DAMAGE
            print(Thief.health)
        self.current_frame = (self.current_frame + self.move) % self.frames_number
        if self.moving == 'idle':
            self.current_frame = 0
        self.image = self.frames[self.current_frame] # Image of sprite changes as program cycles through the sheet
关注者
0
被浏览
37
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    如果您想要缩放的碰撞矩形/点击框,则需要给精灵一个第二矩形(我hitbox在这里称呼它)。您必须这样做,因为pygame会在的左上角坐标处涂抹图像/表面self.rect。因此,第一个rectself.rect用作blit位置,并且self.hitbox用于碰撞检测。

    您还需要为碰撞检测定义一个自定义的回调函数,您必须将其pygame.sprite.spritecollide作为第四个参数传递给该函数。

    def collided(sprite, other):
        """Check if the `hitbox` rects of the two sprites collide."""
        return sprite.hitbox.colliderect(other.hitbox)
    
    collided_sprites = pg.sprite.spritecollide(player, enemies, False, collided)
    

    这是一个完整的示例(self.rects是绿色矩形,self.hitboxes是红色矩形):

    import pygame as pg
    from pygame.math import Vector2
    
    
    class Entity(pg.sprite.Sprite):
    
        def __init__(self, pos, *groups):
            super().__init__(*groups)
            self.image = pg.Surface((70, 50))
            self.image.fill((0, 80, 180))
            self.rect = self.image.get_rect(center=pos)
            # A inflated copy of the rect as the hitbox.
            self.hitbox = self.rect.inflate(-42, -22)
            self.vel = Vector2(0, 0)
            self.pos = Vector2(pos)
    
        def update(self):
            self.pos += self.vel
            self.rect.center = self.pos
            self.hitbox.center = self.pos  # Also update the hitbox coords.
    
    
    def collided(sprite, other):
        """Check if the hitboxes of the two sprites collide."""
        return sprite.hitbox.colliderect(other.hitbox)
    
    
    def main():
        screen = pg.display.set_mode((640, 480))
        clock = pg.time.Clock()
        all_sprites = pg.sprite.Group()
        player = Entity((300, 200), all_sprites)
        enemies = pg.sprite.Group(
            Entity((100, 250), all_sprites),
            Entity((400, 300), all_sprites),
            )
    
        done = False
    
        while not done:
            for event in pg.event.get():
                if event.type == pg.QUIT:
                    done = True
                elif event.type == pg.MOUSEMOTION:
                    player.pos = event.pos
    
            all_sprites.update()
            # Pass the custom collided callback function to spritecollide.
            collided_sprites = pg.sprite.spritecollide(
                player, enemies, False, collided)
            for sp in collided_sprites:
                print('Collision', sp)
    
            screen.fill((30, 30, 30))
    
            all_sprites.draw(screen)
            for sprite in all_sprites:
                # Draw rects and hitboxes.
                pg.draw.rect(screen, (0, 230, 0), sprite.rect, 2)
                pg.draw.rect(screen, (250, 30, 0), sprite.hitbox, 2)
    
            pg.display.flip()
            clock.tick(30)
    
    
    if __name__ == '__main__':
        pg.init()
        main()
        pg.quit()
    


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看