在Pygame中旋转和移动精灵

问题描述:

我是Python,Pygame和一般编程的新手。我试图创建一个小行星克隆,但我无法弄清楚如何让我的玩家精灵同时移动和旋转。我使用了this answer中的Vector2D类(试图修改键盘而不是鼠标),所以我的精灵有点旋转(它不会在一个完整的圆中旋转),但现在它不会移动。我正在尝试使用向上和向下键来加速/减速以及左右箭头来转动。我希望这艘船能够面对它正在移动的方向。在Pygame中旋转和移动精灵

下面的代码迄今:

# an asteroids clone 

try: 
    import sys 
    import random 
    import math 
    import os 
    import getopt 
    import pygame 
    from socket import * 
    from pygame.locals import * 
    from pygame.mixer import Sound 
except ImportError, err: 
    print "couldn't load module. %s" % (err) 
    sys.exit(2) 

# these are warnings if font or sound modules are not available. 
if not pygame.font: print 'Warning, fonts disabled' 
if not pygame.mixer: print 'Warning, sound disabled' 


# VECTOR CLASS 
class Vector2D(object): 

    def __init__(self, x, y): 
     self.x = x 
     self.y = y 

    def __add__(self, other): 
     return Vector2D(self.x + other.x, self.y + other.y) 

    def __sub__(self, other): 
     return Vector2D(self.x - other.x, self.y - other.y) 

    def __mul__(self, other): 
     if isinstance(other, Vector2D): 
      # Vector multiplication 
      return self.x * other.x + self.y * other.y 
     else: 
      # Scalar multiplication 
      return Vector2D(self.x * other, self.y * other) 

    __radd__ = __add__ 
    __rsub__ = __sub__ 
    __rmul__ = __mul__ 

    def get_length(self): 
     return (self.x ** 2 + self.y ** 2) ** (1/2) 

    def get_angle(self, other, radians=False): 
     """Will return the angle between this vector and the other vector.""" 
     if self.get_length() == 0 or other.get_length() == 0: 
      return 0 
     if not radians: 
      return (360/(2 * math.pi)) * (math.atan2(other.y, other.x) - math.atan2(self.y, self.x)) 
     else: 
      return math.atan2(other.y, other.x) - math.atan2(self.y, self.x) 

    def normalize(self): 
     if self.get_length() == 0: 
      return Vector2D(0, 0) 
     return Vector2D(self.x/self.get_length(), self.y/self.get_length()) 

class Player(pygame.sprite.Sprite): 
    """moves ship on screen""" 
    def __init__(self, image_file, pos=(0, 0)): 
     super(Player, self).__init__() # call Sprite initializer 
     self.original_image = pygame.image.load(image_file).convert() # RemEMBER TO CONVERT 
     self.image = self.original_image # this will reference our rotated copy 
     self.rect = self.image.get_rect() 
     self.position = Vector2D(*pos) 

     self.moving = 0 # won't move at start of game 
     screen = pygame.display.get_surface() 
     self.area = screen.get_rect() 
     self.speed = 10 
     self.state = "still" 
     degree = 0 
     self.reinit() 

    def reinit(self): 
     self.state = "still" 
     self.movepos = [0,0] 

    def update(self): 
     newpos = self.rect.move(self.movepos) 
     if self.area.contains(newpos): 
      self.rect = newpos 

     # Create a vector pointing at the key position 
     key_pos = self.rect.move(self.movepos) 

     # Create a vector pointing from the image towards the key direction 
     rel_key_pos = key_pos - self.position 

     # Calculate the angle between the y_axis and the vector pointing from the 
     # image towards the mouse position 
     y_axis = Vector2D(0, -1) 
     angle = -y_axis.get_angle(rel_key_pos) # Negating bc pygame rotates counter-clockwise 

     # Create the rotated copy 
     self.image = pygame.transform.rotate(self.original_image, angle).convert() # Angle is absolute value!! 

     # Make sure your rect represent the actual Surface 
     self.rect = self.image.get_rect() 

     # Since the dimension probably changed you should move its center back to where it was. 
     self.rect.center = self.position.x, self.position.y 
     pygame.event.pump() 


    def accelerate(self): 
     self.speed += 1 
     self.state = "accelerate" 


    def decelerate(self): 
     self.speed -= 1 
     self.state = "decelerate" 

    def moveleft(self): 
     self.movepos[0]-=(self.speed) 
     self.state="moveleft" 

    def moveright(self): 
     self.movepos[0]+=(self.speed) 
     self.state="moveright" 

class Bullet(): 
    pass 

class Asteroid(): 
    pass 

class Background(pygame.sprite.Sprite): 
    def __init__(self, image_file, location): 
     pygame.sprite.Sprite.__init__(self) #call Sprite initializer 
     self.image = pygame.image.load(image_file) 
     self.rect = self.image.get_rect() 
     self.rect.left, self.rect.top = location 

# main event loop 
def main(): 
    # initialize screen 
    pygame.init() 
    pygame.mixer.init() 
    pygame.key.set_repeat(500,30) 

    screen = pygame.display.set_mode((1280, 1024)) 
    pygame.display.set_caption('Asteroids') 
    pygame.mouse.set_visible(0) 

    # make background 

    background = Background('data\stars1.jpg', [0,0]) 

    # prepare background music 
    pygame.mixer.music.load('data\patakasmusic.wav') 
    pygame.mixer.music.play(-1) 

    # load player sprite 
    global player 
    player = Player('data\ship.png', [0,0]) 
    # initialize player sprite 
    playersprite = pygame.sprite.RenderPlain((player)) 

    # initialize clock 
    clock = pygame.time.Clock() 

    # event loop 
    while 1: 
     clock.tick(60) 
     # event loop 
     for event in pygame.event.get(): 
      if event.type == QUIT: 
       return 
      elif event.type == KEYDOWN: 
       if event.key == K_UP: 
        player.accelerate() 
       if event.key == K_DOWN: 
        player.decelerate() 
       if event.key == K_LEFT: 
        player.moveleft() 
       if event.key == K_RIGHT: 
        player.moveright() 
      elif event.type == KEYUP: 
       if event.key == K_UP or event.key == K_DOWN: 
        player.movepos = [0,0] 




     screen.fill([255, 255, 255]) 
     screen.blit(background.image, background.rect) 
     screen.blit(player.image, (500,500)) 
     playersprite.draw(screen) 
     playersprite.update() 

     pygame.display.update() 
     playersprite.update() 

     pygame.display.flip() 

if __name__ == '__main__': main() 
+0

请大家看看[MCVE页面(https://开头计算器.COM /帮助/ MCVE)。 – skrx

我会用另一种载体来存储精灵的direction,并且还添加angle_speed属性。当用户想要转动船舶时,将angle_speed设置为所需的度数值,并在update方法中将方向矢量旋转角度速度。要移动精灵,请将direction乘以self.speed以获取速度并将其添加到self.position,然后更新该rect。

我建议使用pygame.math.Vector2类,因为它有更多的功能,也是相当快的,因为它是在C.

实施
import math 
import pygame as pg 
from pygame.math import Vector2 


class Player(pg.sprite.Sprite): 

    def __init__(self, pos=(420, 420)): 
     super(Player, self).__init__() 
     self.image = pg.Surface((70, 50), pg.SRCALPHA) 
     pg.draw.polygon(self.image, (50, 120, 180), ((0, 0), (0, 50), (70, 25))) 
     self.original_image = self.image 
     self.rect = self.image.get_rect(center=pos) 
     self.position = Vector2(pos) 
     self.direction = Vector2(1, 0) # A unit vector pointing rightward. 
     self.speed = 2 
     self.angle_speed = 0 
     self.angle = 0 

    def update(self): 
     if self.angle_speed != 0: 
      # Rotate the direction vector and then the image. 
      self.direction.rotate_ip(self.angle_speed) 
      self.angle += self.angle_speed 
      self.image = pg.transform.rotate(self.original_image, -self.angle) 
      self.rect = self.image.get_rect(center=self.rect.center) 
     # Update the position vector and the rect. 
     self.position += self.direction * self.speed 
     self.rect.center = self.position 


def main(): 
    pg.init() 
    screen = pg.display.set_mode((1280, 720)) 
    player = Player((420, 420)) 
    playersprite = pg.sprite.RenderPlain((player)) 

    clock = pg.time.Clock() 
    done = False 
    while not done: 
     clock.tick(60) 
     for event in pg.event.get(): 
      if event.type == pg.QUIT: 
       done = True 
      elif event.type == pg.KEYDOWN: 
       if event.key == pg.K_UP: 
        player.speed += 1 
       elif event.key == pg.K_DOWN: 
        player.speed -= 1 
       elif event.key == pg.K_LEFT: 
        player.angle_speed = -4 
       elif event.key == pg.K_RIGHT: 
        player.angle_speed = 4 
      elif event.type == pg.KEYUP: 
       if event.key == pg.K_LEFT: 
        player.angle_speed = 0 
       elif event.key == pg.K_RIGHT: 
        player.angle_speed = 0 

     playersprite.update() 

     screen.fill((30, 30, 30)) 
     playersprite.draw(screen) 
     pg.display.flip() 

if __name__ == '__main__': 
    main() 
    pg.quit() 
+0

绝对是一个使用pygame矢量类的好主意,但为什么不直接存储角度而不是从每次更新中的方向矢量计算角度呢? –

+0

@WyattIsrael是的,这很简单。我只需要在此示例的原始版本中进行角度计算,因为我必须朝鼠标旋转。我忘了改变那条线。 – skrx

+0

@skrx我收到一个错误,说没有pygame.math模块。我在哪里可以找到它?我使用Windows btw。 – rebawan