PYGAME - 用矢量移动角色

问题描述

我正在自学 pygame,并希望让我的角色能够旋转,然后朝他们所面对的方向移动。

我可以进行旋转,但无法让角色朝图像所面对的方向移动。

代码在 Trinket HERE

class Bob(pygame.sprite.Sprite):
  def __init__(self,color,height,width):
    super().__init__()
    self.image = pygame.Surface([width,height])
    self.image.fill(BLACK)
    self.image.set_colorkey(BLACK)
    
    #Loading the image for the character
    self.img = pygame.image.load("char.jfif")
    #creating a copy of the image
    self.img_orig = self.img.copy()
    #defining the starting angle of the character image
    self.angle = 0
    
    self.veLocity = 5
    self.rect = self.img_orig.get_rect()
    
  def moveLeft(self):
    self.angle += 1
    self.img = pygame.transform.rotate(self.img_orig,self.angle)
      
  def moveRight(self):
    self.rect.x += self.veLocity
    if self.rect.x > 485:
      self.rect.x = 485
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
            
    keys = pygame.key.get_pressed()
    if keys[pygame.K_UP]:
      pSprite.moveForward()
    if keys[pygame.K_DOWN]:
      pSprite.moveDown()
    if keys[pygame.K_LEFT]:
      pSprite.moveLeft()
    if keys[pygame.K_RIGHT]:
      pSprite.moveRight()

    #---- Game Logic Here
    
      
    #--- Drawing Code Here
    #Reset the screen to blank
    screen.fill(BLUE)
    #Draw Play Area
    
    #Draw Sprites
    screen.blit(pSprite.img,(pSprite.rect.x,pSprite.rect.y))

解决方法

围绕其中心旋转播放器(参见 How do I rotate an image around its center using PyGame?):

self.angle += 1
self.img = pygame.transform.rotate(self.img_orig,self.angle)
self.rect = self.img.get_rect(center = self.rect.center)

使用属性 xy 以浮点精度存储玩家的位置。

class Bob(pygame.sprite.Sprite):
  def __init__(self,color,height,width):
    # [...]

    self.x,self.y = self.rect.center

使用三角函数 math.sinmath.cos 根据角度计算玩家的方向。更改位置属性并更新 rect 属性:

self.x += self.velocity * math.cos(math.radians(self.angle + 90))
self.y -= self.velocity * math.sin(math.radians(self.angle + 90))
self.rect.center = round(self.x),round(self.y)

y 轴需要反转 (-dy),因为 y 轴通常指向上方,但在 PyGame 坐标系中,y 轴指向下方。此外,必须扣除校正角 (+ 90)。由于精灵仰视时“角度”为0°,因此计算方向向量时需要将角度加上90°。

在 pygame 中用按键移动时也可以看到 te](How to turn the sprite in pygame while moving with the keys.


Bob 类:

import pygame
import math
BLACK = (0,0)

class Bob(pygame.sprite.Sprite):
  def __init__(self,width):
    super().__init__()
    self.image = pygame.Surface([width,height])
    self.image.fill(BLACK)
    self.image.set_colorkey(BLACK)
    
    #Loading the image for the character
    self.img = pygame.image.load("char.jfif")
    #creating a copy of the image
    self.img_orig = self.img.copy()
    #defining the starting angle of the character image
    self.angle = 0
    
    self.velocity = 5
    self.rect = self.img_orig.get_rect()
    self.x,self.y = self.rect.center
    
  def rotate(self,change_angle):
    self.angle += change_angle
    self.img = pygame.transform.rotate(self.img_orig,self.angle)
    self.rect = self.img.get_rect(center = self.rect.center)
    
  def move(self,distance):
    self.x += distance * math.cos(math.radians(self.angle + 90))
    self.y -= distance * math.sin(math.radians(self.angle + 90))
    self.rect.center = round(self.x),round(self.y)
    
  def moveLeft(self):
    self.rotate(1) 
  def moveRight(self):
    self.rotate(-1)
    
  def moveForward(self):
    self.move(self.velocity)
  def moveDown(self):
    self.move(-self.velocity)

设置播放器起始位置时,需要设置xyrect属性:

pSprite = Bob(WHITE,25,25)
pSprite.rect.x = 50
pSprite.rect.y = 50
pSprite.x,pSprite.y = pSprite.rect.center
,

您可以使用 pygame 的 Vector2 类而不是自己计算精灵的位置。

我还建议让精灵自己处理它的运动,而不是在主循环中这样做,并使用时钟来保持恒定的帧率并轻松控制精灵的速度。

您可能还想使用带有 Alpha 通道的图像格式(如 PNG)。

这是一个简单的例子:

import pygame

class Actor(pygame.sprite.Sprite):
    def __init__(self,pos,*grps):
        super().__init__(*grps)
        self.image = pygame.image.load('char.png').convert_alpha()
        self.image_org = self.image.copy()
        self.rect = self.image.get_rect(center=pos)
        self.pos = pygame.Vector2(pos)
        self.direction = pygame.Vector2((0,-1))
        
    def update(self,events,dt):
        pressed = pygame.key.get_pressed()
        
        # if a is pressed,rotate left with 360 degress per second
        if pressed[pygame.K_a]: self.direction.rotate_ip(dt * -360) 
        # if d is pressed,rotate right with 360 degress per second
        if pressed[pygame.K_d]: self.direction.rotate_ip(dt *  360)

        # check if should move forward or backward
        movement = 0
        if pressed[pygame.K_w]: movement =  1
        if pressed[pygame.K_s]: movement = -1
        movement_v = self.direction * movement
        if movement_v.length() > 0:
            movement_v.normalize_ip()
            # move 100px per second in the direction we're facing
            self.pos += movement_v * dt * 100
        
        # rotate the image
        self.image = pygame.transform.rotate(self.image_org,self.direction.angle_to((0,-1)))
        self.rect = self.image.get_rect(center=self.pos)
        

def main():
    pygame.init()
    screen = pygame.display.set_mode((600,480))
    sprites = pygame.sprite.Group()
    Actor((100,100),sprites)
    clock,dt = pygame.time.Clock(),0
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
        screen.fill('grey')
        sprites.draw(screen)
        sprites.update(events,dt)
        pygame.display.flip()
        dt = clock.tick(60) / 1000
main()

enter image description here

char.png

enter image description here