如何让精灵旋转以面向鼠标?

时间:2017-07-07 00:19:12

标签: python rotation pygame mouse

经过几个小时的完全混乱之后,我仍然没有找到一个旋转精灵来面向鼠标位置的解决方案。我已经从其他帖子中实现了几个例子,链接在下面,但它们似乎都没有效果。任何有关我的问题的帮助,将非常感激:)

  1. Pygame Making A Sprite Face The Mouse

  2. https://ubuntuforums.org/showthread.php?t=1823825

  3. https://gamedev.stackexchange.com/questions/132163/how-can-i-make-the-player-look-to-the-mouse-direction-pygame-2d

  4. 原始代码:

    class Player(pygame.sprite.Sprite):
        def __init__(self, game, x, y):
            pygame.sprite.Sprite.__init__(self)
            self.game = game
            sprite_sheet = Spritesheet("Sprites/ships_spritesheet.png")
            image = sprite_sheet.get_image(204, 115, 66, 113)
            self.image = pygame.transform.flip(image, False, True)
            self.orig_img = self.image
            self.rect = self.image.get_rect()
            self.pos = vec(x, y)
            self.vel = vec(0, 0)
            self.health = PLAYER_HEALTH
    
        def update(self):
            self.rotate()
            self.pos += self.vel
            self.rect.x = self.pos.x
            self.collide_with_tiles(self.game.obstacle_list, "x")
            self.rect.y = self.pos.y
            self.collide_with_tiles(self.game.obstacle_list, "y")
    
        def rotate(self):
            mouse_x, mouse_y = pygame.mouse.get_pos()
            rel_x, rel_y = mouse_x - self.rect.x, mouse_y - self.rect.y
            angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
            self.image = pygame.transform.rotate(self.orig_img, int(angle))
            self.rect = self.image.get_rect(center=self.pos)
    

    新守则:

    class Player(pygame.sprite.Sprite):
        def __init__(self, game, pos):
            pygame.sprite.Sprite.__init__(self)
            self.game = game
            sprite_sheet = Spritesheet("Sprites/ships_spritesheet.png")
            image = sprite_sheet.get_image(204, 115, 66, 113)
            self.image = pygame.transform.flip(image, False, True)
            self.orig_img = self.image
            self.rect = self.image.get_rect(center=pos)
            self.pos = vec(pos)
            self.vel = vec(0, 0)
            self.health = PLAYER_HEALTH
    
        def update(self):
            self.rotate()
            self.pos += self.vel
            self.rect.centerx = self.pos.x
            #self.rect.x = self.pos.x
            self.collide_with_tiles(self.game.obstacle_list, "x")
            self.rect.centery = self.pos.y
            #self.rect.y = self.pos.y
            self.collide_with_tiles(self.game.obstacle_list, "y")
            self.vel = vec(0, 0)
    
        def rotate(self):
            mouse_pos = pygame.mouse.get_pos()
            rel_x, rel_y = mouse_pos - self.pos
            angle = -math.degrees(math.atan2(rel_y, rel_x))
            self.image = pygame.transform.rotate(self.orig_img, angle)
            self.rect = self.image.get_rect(center=self.rect.center)
    
        def collide_with_tiles(self, group, dir):
            if dir == "x":
                hits = pygame.sprite.spritecollide(self, group, False)
                if hits:
                    if self.vel.x > 0:
                        self.rect.right = hits[0].rect.left
                    if self.vel.x < 0:
                        self.rect.left = hits[0].rect.right
                    self.pos.x = self.rect.centerx
    
            if dir == "y":
                hits = pygame.sprite.spritecollide(self, group, False)
                if hits:
                    if self.vel.y > 0:
                        self.rect.bottom = hits[0].rect.top
                    if self.vel.y < 0:
                        self.rect.top = hits[0].rect.bottom
                    self.pos.y = self.rect.centery
    

    我的游戏类

    import pygame, pytmx, sys, os
    
    from Settings import *
    from Obstacle import *
    from Player import *
    from Camera import *
    from TiledMap import *
    from MainMenu import *
    from PauseMenu import *
    from OptionsMenu import *
    from HUD import *
    
    class Game:
        def __init__(self):
            pygame.init()
            pygame.mixer.init()
            os.environ['SDL_VIDEO_CENTERED'] = '1'
            pygame.display.set_caption(GAME_TITLE)
            self.window = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
            self.clock = pygame.time.Clock()
            self.click_sound = pygame.mixer.Sound("Sounds/click1.ogg")
            self.main_menu = MainMenu(self, self.window)
            self.obstacle_list = pygame.sprite.Group()
            self.island_boundary_list = pygame.sprite.Group()
            self.pause_menu = PauseMenu(self.window)
            self.options_menu = OptionsMenu(self.window)
            self.hud = HUD()
            self.display_pause_menu = False
            self.display_options_menu = False
            self.display_main_menu = True
    
        def get_map(self):
            map_dir = TiledMap("Sprites/Maps/map_01.tmx")
            self.map = map_dir.generate_map()
            self.map_rect = self.map.get_rect()
    
            for tile_obj in map_dir.tmxdata.objects:
                if tile_obj.name == "Obstacle":
                    obstacle = Obstacle(self, tile_obj.x, tile_obj.y, 64, 64)
                    self.obstacle_list.add(obstacle)
                if tile_obj.name == "PLAYER":
                    self.player = Player(self, (tile_obj.x, tile_obj.y))
    
        def game_loop(self):
            self.get_map()
            self.camera = Camera(5120, 5120)
    
            while True:
                self.clock.tick(FPS)
                self.game_events()
    
                if not self.display_pause_menu and not self.display_options_menu and not self.display_main_menu:
                self.update_game()
    
                self.draw_game()
    
        def game_events(self):
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.run_game = False
                    pygame.quit()
                    sys.exit()
    
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_p and not self.display_options_menu:
                        self.display_pause_menu = not self.display_pause_menu
                    if event.key == pygame.K_o and not self.display_pause_menu:
                        self.display_options_menu = not self.display_options_menu
    
                if event.type == pygame.MOUSEBUTTONDOWN and self.display_main_menu:
                    x, y = event.pos
                    self.click_sound.play()
                    if self.main_menu.play_button.collidepoint(x, y):
                        self.display_main_menu = False
                    if self.main_menu.credits_button.collidepoint(x, y):
                        pass
                    if self.main_menu.exit_button.collidepoint(x, y):
                        pygame.quit()
                        sys.exit()
    
                if event.type == pygame.MOUSEBUTTONDOWN and self.display_pause_menu:
                    x, y = event.pos
                    self.click_sound.play()
                    if self.pause_menu.pause_resume_button.collidepoint(x, y) or self.pause_menu.pause_x_button.collidepoint(x, y):
                        self.display_pause_menu = False
                    if self.pause_menu.pause_options_button.collidepoint(x, y):
                        self.display_pause_menu = False
                        self.display_options_menu = True
                    if self.pause_menu.pause_quit_button.collidepoint(x, y):
                        pygame.quit()
                        sys.exit()
    
                if event.type == pygame.MOUSEBUTTONDOWN and self.display_options_menu:
                    x, y = event.pos
                    self.click_sound.play()
                    if self.options_menu.options_x_button.collidepoint(x, y):
                        self.display_options_menu = False
                    if self.options_menu.options_reset_button.collidepoint(x, y):
                        #reset options to original options if modified
                        pass
                    if self.options_menu.options_home_button.collidepoint(x, y):
                        self.display_options_menu = False
                        self.display_main_menu = True
                    if self.options_menu.options_ok_button.collidepoint(x, y):
                        #save settings
                        self.display_options_menu = False
    
                mouse_x, mouse_y = pygame.mouse.get_pos()
                if mouse_x > self.main_menu.play_button.x and mouse_x < self.main_menu.play_button.x + self.main_menu.play_button.width and mouse_y > self.main_menu.play_button.y and mouse_y < self.main_menu.play_button.y + self.main_menu.play_button.height:
                    self.main_menu.img_id = 1
    
                elif mouse_x > self.main_menu.credits_button.x and mouse_x < self.main_menu.credits_button.x + self.main_menu.credits_button.width and mouse_y > self.main_menu.credits_button.y and mouse_y < self.main_menu.credits_button.y + self.main_menu.credits_button.height:
                    self.main_menu.img_id = 2
    
                elif mouse_x > self.main_menu.exit_button.x and mouse_x < self.main_menu.exit_button.x + self.main_menu.exit_button.width and mouse_y > self.main_menu.exit_button.y and mouse_y < self.main_menu.exit_button.y + self.main_menu.exit_button.height:
                    self.main_menu.img_id = 3
    
                else:
                    self.main_menu.img_id = 0
    
                if mouse_x > self.pause_menu.pause_resume_button.x and mouse_x < self.pause_menu.pause_resume_button.x + self.pause_menu.pause_resume_button.width and mouse_y > self.pause_menu.pause_resume_button.y and mouse_y < self.pause_menu.pause_resume_button.y + self.pause_menu.pause_resume_button.height:
                    self.pause_menu.img_id = 1
    
                elif mouse_x > self.pause_menu.pause_x_button.x and mouse_x < self.pause_menu.pause_x_button.x + self.pause_menu.pause_x_button.width and mouse_y > self.pause_menu.pause_x_button.y and mouse_y < self.pause_menu.pause_x_button.y + self.pause_menu.pause_x_button.height:
                    self.pause_menu.img_id = 2
    
                elif mouse_x > self.pause_menu.pause_options_button.x and mouse_x < self.pause_menu.pause_options_button.x + self.pause_menu.pause_options_button.width and mouse_y > self.pause_menu.pause_options_button.y and mouse_y < self.pause_menu.pause_options_button.y + self.pause_menu.pause_options_button.height:
                    self.pause_menu.img_id = 3
    
                elif mouse_x > self.pause_menu.pause_quit_button.x and mouse_x < self.pause_menu.pause_quit_button.x + self.pause_menu.pause_quit_button.width and mouse_y > self.pause_menu.pause_quit_button.y and mouse_y < self.pause_menu.pause_quit_button.y + self.pause_menu.pause_quit_button.height:
                    self.pause_menu.img_id = 4
    
                else:
                    self.pause_menu.img_id = 0
    
                if mouse_x > self.options_menu.options_x_button.x and mouse_x < self.options_menu.options_x_button.x + self.options_menu.options_x_button.width and mouse_y > self.options_menu.options_x_button.y and mouse_y < self.options_menu.options_x_button.y + self.options_menu.options_x_button.height:
                    self.options_menu.img_id = 1
    
                elif mouse_x > self.options_menu.options_reset_button.x and mouse_x < self.options_menu.options_reset_button.x + self.options_menu.options_reset_button.width and mouse_y > self.options_menu.options_reset_button.y and mouse_y < self.options_menu.options_reset_button.y + self.options_menu.options_reset_button.height:
                    self.options_menu.img_id = 2
    
                elif mouse_x > self.options_menu.options_home_button.x and mouse_x < self.options_menu.options_home_button.x + self.options_menu.options_home_button.width and mouse_y > self.options_menu.options_home_button.y and mouse_y < self.options_menu.options_home_button.y + self.options_menu.options_home_button.height:
                    self.options_menu.img_id = 3
    
                elif mouse_x > self.options_menu.options_ok_button.x and mouse_x < self.options_menu.options_ok_button.x + self.options_menu.options_ok_button.width and mouse_y > self.options_menu.options_ok_button.y and mouse_y < self.options_menu.options_ok_button.y + self.options_menu.options_ok_button.height:
                    self.options_menu.img_id = 4
    
                else:
                    self.options_menu.img_id = 0
    
            keys = pygame.key.get_pressed()
            if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
                self.player.vel.x = PLAYER_SPEED
            if keys[pygame.K_LEFT] or keys[pygame.K_a]:
                self.player.vel.x = -PLAYER_SPEED
            if keys[pygame.K_UP] or keys[pygame.K_w]:
                self.player.vel.y = -PLAYER_SPEED
            if keys[pygame.K_DOWN] or keys[pygame.K_s]:
                self.player.vel.y = PLAYER_SPEED
    
        def update_game(self):
            self.player.update()
            self.camera.update(self.player)
    
            if self.player.rect.x <= 0:
                self.player.rect.x = 0
            if self.player.rect.right >= 5120:
                self.player.rect.right = 5120
            if self.player.rect.y <= 0:
                self.player.rect.y = 0
            if self.player.rect.bottom >= 5120:
                self.player.rect.bottom = 5120
    
        def draw_game(self):
            self.window.blit(self.map, self.camera.apply_rect(self.map_rect))
            self.window.blit(self.player.image, self.camera.apply(self.player))
            self.hud.draw_health(self.window, 10, 10, self.player.health / PLAYER_HEALTH)
    
            if self.display_main_menu:
                self.main_menu.draw()
    
            if self.display_pause_menu:
                self.pause_menu.draw()
    
            if self.display_options_menu:
                self.options_menu.draw()
    
            pygame.display.flip()
    
    def main():
        g = Game()
        g.game_loop()
    
    if __name__ == "__main__":
        main()
    

    相机课程

    import pygame
    
    from Settings import *
    
    class Camera:
        def __init__(self, width, height):
            self.camera = pygame.Rect(0, 0, width, height)
            self.width = width
            self.height = height
    
        def apply(self, target):
            return target.rect.move(self.camera.topleft)
    
        def apply_rect(self, rect):
            return rect.move(self.camera.topleft)
    
        def update(self, target):
            x = -target.rect.centerx + int(WINDOW_WIDTH/2)
            y = -target.rect.centery + int(WINDOW_HEIGHT/2)
            x = min(0, x)
            y = min(0, y)
            x = max(-(self.width - WINDOW_WIDTH), x)
            y = max(-(self.height - WINDOW_HEIGHT), y)
            self.camera = pygame.Rect(x, y, self.width, self.height)
    

1 个答案:

答案 0 :(得分:1)

使用中心点self.rect.centerxself.rect.centery或仅self.rect.centerself.pos代替self.rect.x self.rect.y(topleft坐标)。

以下是我用来测试代码的完整示例:

import math
import pygame
from pygame.math import Vector2


class Player(pygame.sprite.Sprite):
    def __init__(self, pos):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((50, 30), pygame.SRCALPHA)
        pygame.draw.polygon(
            self.image,
            pygame.Color('dodgerblue1'),
            ((0, 0), (50, 15), (0, 30)))
        self.rect = self.image.get_rect(center=pos)
        self.orig_img = self.image
        self.pos = Vector2(pos)
        self.vel = Vector2(0, 0)

    def update(self):
        self.rotate()
        self.pos += self.vel
        self.rect.centerx = self.pos.x
        self.rect.centery = self.pos.y

    def rotate(self):
        mouse_pos = pygame.mouse.get_pos()
        # Calculate the vector to the mouse position by subtracting
        # the self.pos vector from the mouse_pos.
        rel_x, rel_y = mouse_pos - self.pos
        # Use math.atan2 to get the angle in radians and convert it to degrees.
        angle = -math.degrees(math.atan2(rel_y, rel_x))
        # Rotate the image.
        self.image = pygame.transform.rotozoom(self.orig_img, angle, 1)
        # Update the rect and keep the center at the old position.
        self.rect = self.image.get_rect(center=self.rect.center)


def main():
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    sprite_group = pygame.sprite.Group()
    player = Player((300, 200))
    sprite_group.add(player)

    done = False

    while not done:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True

        sprite_group.update()
        screen.fill((30, 30, 30))
        sprite_group.draw(screen)

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


if __name__ == '__main__':
    pygame.init()
    main()
    pygame.quit()

Edit3:在这个例子中,我使用另一个pygame.Rect(称为hitbox)来处理与墙壁的碰撞。玩家的rect无法用于碰撞检测,因为它会随着每次旋转而改变其大小,如果玩家触摸墙壁则会导致跳跃。我使用Rect.colliderect因为spritecollide使用rect而非hitbox,但您也可以将自定义collided回调函数传递给spritecollide。 (红色和绿色显示player.rectplayer.hitbox。)

import math
import pygame
from pygame.math import Vector2 as vec


class Player(pygame.sprite.Sprite):
    def __init__(self, x, y, walls):
        pygame.sprite.Sprite.__init__(self)
        self.walls = walls
        self.image = pygame.Surface((50, 30), pygame.SRCALPHA)
        pygame.draw.polygon(
            self.image,
            pygame.Color('dodgerblue1'),
            ((0, 0), (50, 15), (0, 30)))
        self.rect = self.image.get_rect(center=(x, y))
        self.hitbox = pygame.Rect(x, y, 50, 50)
        self.orig_img = self.image
        self.pos = vec(x, y)
        self.vel = vec(0, 0)

    def update(self):
        self.rotate()
        self.pos += self.vel
        self.hitbox.centerx = self.pos.x
        self.collide_with_tiles(self.walls, "x")
        self.hitbox.centery = self.pos.y
        self.collide_with_tiles(self.walls, "y")
        self.rect.center = self.pos

    def rotate(self):
        rel_x, rel_y = pygame.mouse.get_pos() - self.pos
        angle = -math.degrees(math.atan2(rel_y, rel_x))
        self.image = pygame.transform.rotate(self.orig_img, int(angle))
        self.rect = self.image.get_rect(center=self.pos)

    def collide_with_tiles(self, group, dir):
        if dir == "x":
             for wall in self.walls:
                 if self.hitbox.colliderect(wall.rect):
                     if self.vel.x > 0:
                         self.hitbox.right = wall.rect.left
                     if self.vel.x < 0:
                         self.hitbox.left = wall.rect.right
                     self.vel.x = 0
                     self.pos.x = self.hitbox.centerx

        if dir == "y":
            for wall in self.walls:
                if self.hitbox.colliderect(wall.rect):
                    if self.vel.y > 0:
                        self.hitbox.bottom = wall.rect.top
                    if self.vel.y < 0:
                        self.hitbox.top = wall.rect.bottom
                    self.vel.y = 0
                    self.pos.y = self.hitbox.centery


class Wall(pygame.sprite.Sprite):
    def __init__(self, x, y, w, h):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((w, h))
        self.image.fill(pygame.Color('sienna1'))
        self.rect = self.image.get_rect(topleft=(x, y))


def main():
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()

    sprite_group = pygame.sprite.Group()
    walls = pygame.sprite.Group()

    wall = Wall(100, 200, 300, 30)
    walls.add(wall)
    sprite_group.add(wall)

    player = Player(300, 400, walls)
    sprite_group.add(player)

    done = False

    while not done:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_w:
                    player.vel.y = -3
                elif event.key == pygame.K_s:
                    player.vel.y = 3
                elif event.key == pygame.K_a:
                    player.vel.x = -3
                elif event.key == pygame.K_d:
                    player.vel.x = 3
            elif event.type == pygame.KEYUP:
                player.vel = vec(0, 0)

        sprite_group.update()
        screen.fill((30, 30, 30))
        sprite_group.draw(screen)
        pygame.draw.rect(screen, (200, 30, 30), player.rect, 2)
        pygame.draw.rect(screen, (0, 200, 30), player.hitbox, 2)

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


if __name__ == '__main__':
    pygame.init()
    main()
    pygame.quit()

编辑4:要在计算中包含相机,请将Player相机作为属性,并在rotate方法中更改此行:

rel_x, rel_y = pg.mouse.get_pos() - vec(self.camera.apply(self).center)