Преобразование Pygame: поворот фигуры при нажатии клавиши

#python #pygame

#python #pygame

Вопрос:

Я довольно новичок в Pygame и, похоже, не могу найти четкого ответа на этот вопрос. У меня есть фигура, в частности эллипс, которую я хочу поворачивать как влево, так и вправо. Привязкой клавиш будет a и d , поскольку клавиши со стрелками уже привязаны для перемещения влево и вправо по осям x, y.

Я знаю, что это связано pygame.transform.rotate , однако, похоже, я не могу правильно это реализовать.

 def main():
    #Setup basic variables and methods for pygame
    pygame.init()
    windowWidth = 800
    windowHeight = 700
    fps = 45
    clock = pygame.time.Clock()
    gameWindow = pygame.display.set_mode((windowWidth, windowHeight))
    surface = pygame.Surface((50,50))
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)

    shipX = windowWidth/2
    shipY = windowWidth/2
    shipSpeed = 4

    while(True):

        pygame.draw.ellipse(gameWindow, WHITE, (shipX, shipY, 20, 30))

        #Monitor the FPS of the game
        clock.tick(fps)

        for event in pygame.event.get():
            # ________________________________________
            if event.type == pygame.QUIT:
                gameExit()

        rotate = 0
        pressed = pygame.key.get_pressed()
        if pressed[pygame.K_UP] and shipY > shipSpeed: shipY -= shipSpeed
        if pressed[pygame.K_DOWN] and shipY < windowHeight - shipSpeed - 20: shipY  = shipSpeed
        if pressed[pygame.K_LEFT] and shipX > shipSpeed:shipX -= shipSpeed
        if pressed[pygame.K_RIGHT] and shipX < windowWidth - shipSpeed - 20: shipX  = shipSpeed
        if pressed[ord('a')]: rotate = pygame.transform.rotate(surface, -20)
        if pressed[ord('d')]: rotate = pygame.transform.rotate(surface, 20)

        gameWindow.fill(BLACK)
        # 'flip' display - always after drawing...
        pygame.display.flip()
  

Ожидаемый результат заключается в том, что фигура изменит свой угол, а затем переместится соответствующим образом.

Опять же, я очень новичок в pygame, поэтому буду признателен за любую подробную помощь.

Ответ №1:

Ваша проблема в том, что вы рисуете эллипс прямо на экране, но вы должны нарисовать свой эллипс на другом Surface .

Затем вы можете повернуть эту новую фигуру Surface с pygame.transform.rotate помощью.

Вот простой пример:

 import pygame
import random

def main():
    pygame.init()
    screen = pygame.display.set_mode((500, 500))
    screen_rect = screen.get_rect()
    clock = pygame.time.Clock()
    surface = pygame.Surface((100, 200))
    surface.set_colorkey((2, 3, 4))
    surface.fill((2, 3, 4))
    rect = surface.get_rect(center=(100, 100))
    pygame.draw.ellipse(surface, pygame.Color('white'), (0, 0, 100, 200))
    angle = 0    
    dt = 0
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return

        pressed = pygame.key.get_pressed()
        if pressed[pygame.K_UP]: rect.move_ip(0, -5)
        if pressed[pygame.K_DOWN]: rect.move_ip(0, 5)
        if pressed[pygame.K_LEFT]: rect.move_ip(-5, 0)
        if pressed[pygame.K_RIGHT]: rect.move_ip(5, 0)
        if pressed[pygame.K_a]: angle  = 1
        if pressed[pygame.K_d]: angle -= 1

        rotated = pygame.transform.rotate(surface, angle)
        rect = rotated.get_rect(center=rect.center)

        rect.clamp_ip(screen_rect)

        screen.fill(pygame.Color('dodgerblue'))
        screen.blit(rotated, rect.topleft)
        pygame.display.update()
        dt = clock.tick(60)

if __name__ == '__main__':
    main()
  

Обратите внимание, что я использую a Rect для сохранения положения объекта, потому что затем его легко повернуть Surface вокруг его центра (установив его center атрибут), и для того, чтобы он Surface не выходил за пределы экрана (используя clamp_ip ).

Кроме того, важно всегда поворачивать исходный код Surface , а не уже повернутый Surface . В противном случае вы получите искажения.


Обратите внимание, что здесь у нас есть три вещи: изображение, позиция и некоторая логика поведения. Всякий раз, когда вы видите эти вещи вместе, подумайте о том, чтобы объединить их в класс. Pygame уже предлагает для этого хороший класс под названием Sprite .

Вот тот же пример, но Sprite основанный:

 import pygame
import random

class Thingy(pygame.sprite.Sprite):
    def __init__(self, area):
        super().__init__()
        # image is what get's painted on the screen
        self.image = pygame.Surface((100, 200))
        self.image.set_colorkey((2, 3, 4))
        self.image.fill((2, 3, 4))
        pygame.draw.ellipse(self.image, pygame.Color('white'), (0, 0, 100, 200))
        # we keep a reference to the original image
        # since we use that for rotation to prevent distortions
        self.original = self.image.copy()
        # rect is used to determine the position of a sprite on the screen
        # the Rect class also offers a lot of useful functions
        self.rect = self.image.get_rect(center=(100, 100))
        self.angle = 0  
        self.area = area

    def update(self, events, dt):
        pressed = pygame.key.get_pressed()
        if pressed[pygame.K_UP]: self.rect.move_ip(0, -5)
        if pressed[pygame.K_DOWN]: self.rect.move_ip(0, 5)
        if pressed[pygame.K_LEFT]: self.rect.move_ip(-5, 0)
        if pressed[pygame.K_RIGHT]: self.rect.move_ip(5, 0)
        if pressed[pygame.K_a]: self.angle  = 1
        if pressed[pygame.K_d]: self.angle -= 1

        # let's rotate the image, but ensure that we keep the center position
        # so it doesn't "jump around"
        self.image = pygame.transform.rotate(self.original, self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)
        self.rect.clamp_ip(self.area)

def main():
    pygame.init()
    screen = pygame.display.set_mode((500, 500))
    screen_rect = screen.get_rect()
    clock = pygame.time.Clock()
    sprites = pygame.sprite.Group(Thingy(screen_rect))
    dt = 0
    while True:
        # nice clean main loop
        # all game logic goes into the sprites

        # handle "global" events
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return

        # update all sprites
        sprites.update(events, dt)

        # draw everything
        screen.fill(pygame.Color('dodgerblue'))
        sprites.draw(screen)
        pygame.display.update()
        dt = clock.tick(60)

if __name__ == '__main__':
    main()
  

Ответ №2:

Вы должны создать класс для своего объекта:

 class myRect(pygame.Surface):
    def __init__(self, parent, xpos, ypos, width, height):
      super(myRect, self).__init__(width, height)
      self.xpos = xpos
      self.ypos = ypos
      self.parent = parent

    def update(self, parent):
      parent.blit(self, (self.xpos, self.ypos))

    def rotate(self, angle):
      #(your rotation code goes here)
  

Комментарии:

1. Я вижу. Итак, код преобразования входит в указанное определение поворота, и указанный класс вызывается при нажатии клавиши? Имеет смысл. Я попробую это

2. Класс вызывается, когда вы хотите создать свой объект. Теперь вы можете просто вызывать class instance.rotate() при нажатии клавиши