如何在 Pygame 中根据角度移动精灵

问题描述 投票:0回答:4

我在移动精灵时遇到问题。我可以毫无问题地在 x 轴和 y 轴上移动它们。我不知道如何才能按照某个角度移动它们。我的意思是,我正在尝试创建一个函数,其中包括我尝试移动的对象、其速度和方向(应以度为单位测量)。比如:

MovingObject(obj,speed,direction) #this is the function I'm trying to define

它更像是一个“生成函数”而不仅仅是运动......举个例子,我想创建一个“Bullet”类的对象,并希望它遵循一定的方向(与 x 和 y 轴不同,课程) 实际上我不清楚如何做这样的事情,我想要一些建议才能实现这一目标。 感谢您阅读本文!

编辑: @Joran Beasley 我尝试按照你告诉我的去做...但我想我做错了...

import pygame, math, time
screen=pygame.display.set_mode((320,240))
clock=pygame.time.Clock()
pygame.init()
def calculate_new_xy(old_xy,speed,angle_in_radians):
    new_x = old_xy.x + (speed*math.cos(angle_in_radians))
    new_y = old_xy.y + (speed*math.sin(angle_in_radians))
    return new_x, new_y
class Bullet(pygame.sprite.Sprite):
    def __init__(self,x,y,direction,speed):        
            pygame.sprite.Sprite.__init__(self)
            self.image=pygame.Surface((16, 16))
            self.image.fill((255,0,0))
            self.rect=self.image.get_rect()
            self.rect.center=(x,y)
            self.direction=math.radians(direction)
            self.speed=speed
    def update(self):
            self.rect.center=calculate_new_xy(self.rect,self.speed,self.direction)
spr=pygame.sprite.Group()
bullet=Bullet(160,120,45,1); spr.add(bullet)
play=True
while play:
    clock.tick(60)
    for ev in pygame.event.get():
        if ev.type == pygame.QUIT:
            play=False
    screen.fill((0,0,0))
    spr.update()
    spr.draw(screen)
    pygame.display.flip()
pygame.quit()

物体移动,但...不在指定方向...

python python-3.x pygame sprite
4个回答
6
投票

你只需要一点基本的三角

def calculat_new_xy(old_xy,speed,angle_in_radians):
    new_x = old_xy.X + (speed*math.cos(angle_in_radians))
    new_y = old_xy.Y + (speed*math.sin(angle_in_radians))
    return new_x, new_y

--- 编辑 ---

这是您上面编辑后可以工作的代码

import pygame, math, time
screen=pygame.display.set_mode((320,240))
clock=pygame.time.Clock()
pygame.init()
def calculate_new_xy(old_xy,speed,angle_in_radians):
    new_x = old_xy[0] + (speed*math.cos(angle_in_radians))
    new_y = old_xy[1] + (speed*math.sin(angle_in_radians))
    return new_x, new_y
class Bullet(pygame.sprite.Sprite):
    def __init__(self,x,y,direction,speed):
            pygame.sprite.Sprite.__init__(self)
            self.image=pygame.Surface((16, 16))
            self.image.fill((255,0,0))
            self.rect=self.image.get_rect()
            self.rect.center=(x,y)
            self.direction=math.radians(direction)
            self.speed=speed
    def update(self):
            self.rect.center=calculate_new_xy(self.rect.center,self.speed,self.direction)
spr=pygame.sprite.Group()
bullet=Bullet(160,120,45,2); spr.add(bullet)
play=True
while play:
    clock.tick(60)
    for ev in pygame.event.get():
        if ev.type == pygame.QUIT:
            play=False
    screen.fill((0,0,0))
    spr.update()
    spr.draw(screen)
    pygame.display.flip()
pygame.quit()

2
投票

如果您使用 Pygame,我建议使用

pygame.math.Vector2
来完成此任务。使用 speed
 从其 
极坐标
(angle_in_degrees
, 
from_polar()
) 设置方向向量。将此向量添加到项目符号的当前位置。这比使用
sin
 cos
更容易理解:

def calculate_new_xy(old_xy, speed, angle_in_degrees):
    move_vec = pygame.math.Vector2()
    move_vec.from_polar((speed, angle_in_degrees))
    return old_xy + move_vec

请注意,

pygame.Rect
只能存储整数坐标。这是因为
pygame.Rect
应该代表屏幕上的一个区域:

矩形对象的坐标都是整数。 [...]

如果希望子弹以不能被 45° 整除的角度沿某个方向直线前进,则必须以浮点精度存储对象位置。您必须将对象的位置存储在单独的变量和属性中,并同步

pygame.Rect
对象。
round
坐标并将其分配给矩形的位置(例如
.topleft
):

class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y, direction, speed):
            # [...]

            self.rect = self.image.get_rect(center = (x, y))
            self.pos = (x, y)

    def update(self, screen):
            self.pos = calculate_new_xy(self.pos, self.speed, -self.direction)
            self.rect.center = round(self.pos[0]), round(self.pos[1])

另请参阅朝目标移动


最小示例

import pygame

def calculate_new_xy(old_xy, speed, angle_in_degrees):
    move_vec = pygame.math.Vector2()
    move_vec.from_polar((speed, angle_in_degrees))
    return old_xy + move_vec

class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y, direction, speed):
            pygame.sprite.Sprite.__init__(self)
            self.image = pygame.Surface((16, 8), pygame.SRCALPHA)
            self.image.fill((255, 0, 0))
            self.image = pygame.transform.rotate(self.image, direction)
            self.rect = self.image.get_rect(center = (x, y))
            self.pos = (x, y)
            self.direction = direction
            self.speed = speed
    def update(self, screen):
            self.pos = calculate_new_xy(self.pos, self.speed, -self.direction)
            self.rect.center = round(self.pos[0]), round(self.pos[1])
            if not screen.get_rect().colliderect(self.rect):
                self.kill()

pygame.init()
screen = pygame.display.set_mode((320,240))
clock = pygame.time.Clock()
spr = pygame.sprite.Group()
play = True
frame_count = 0
while play:
    clock.tick(60)
    for ev in pygame.event.get():
        if ev.type == pygame.QUIT:
            play = False
    
    spr.update(screen)
    if (frame_count % 10) == 0:
        spr.add(Bullet(*screen.get_rect().center, frame_count, 2))
    frame_count += 1

    screen.fill((0,0,0))
    spr.draw(screen)
    pygame.draw.circle(screen, (64, 128, 255), screen.get_rect().center, 10)
    pygame.display.flip()

pygame.quit()
exit()

1
投票

我建议使用向量。要获得速度,请将起始方向矢量

Vector2(1, 0)
旋转一定角度,然后乘以所需的速度。然后只需将此速度向量添加到
update
方法中的位置向量,并更新
rect
位置即可移动精灵。 (按“a”或“d”旋转,鼠标1或空格键射击。)

import pygame as pg
from pygame.math import Vector2


pg.init()
screen = pg.display.set_mode((640, 480))
screen_rect = screen.get_rect()

FONT = pg.font.Font(None, 24)
BULLET_IMAGE = pg.Surface((20, 11), pg.SRCALPHA)
pg.draw.polygon(BULLET_IMAGE, pg.Color('aquamarine1'),
                [(0, 0), (20, 5), (0, 11)])


class Bullet(pg.sprite.Sprite):

    def __init__(self, pos, angle):
        super().__init__()
        self.image = pg.transform.rotate(BULLET_IMAGE, -angle)
        self.rect = self.image.get_rect(center=pos)
        # To apply an offset to the start position,
        # create another vector and rotate it as well.
        offset = Vector2(40, 0).rotate(angle)
        # Then add the offset vector to the position vector.
        self.pos = Vector2(pos) + offset  # Center of the sprite.
        # Rotate the direction vector (1, 0) by the angle.
        # Multiply by desired speed.
        self.velocity = Vector2(1, 0).rotate(angle) * 9

    def update(self):
        self.pos += self.velocity  # Add velocity to pos to move the sprite.
        self.rect.center = self.pos  # Update rect coords.

        if not screen_rect.contains(self.rect):
            self.kill()


def main():
    clock = pg.time.Clock()
    cannon_img = pg.Surface((40, 20), pg.SRCALPHA)
    cannon_img.fill(pg.Color('aquamarine3'))
    cannon = cannon_img.get_rect(center=(320, 240))
    angle = 0
    bullet_group = pg.sprite.Group()  # Add bullets to this group.

    while True:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                return
            elif event.type == pg.MOUSEBUTTONDOWN:
                # Left button fires a bullet from center with
                # current angle. Add the bullet to the bullet_group.
                if event.button == 1:
                    bullet_group.add(Bullet(cannon.center, angle))

        keys = pg.key.get_pressed()
        if keys[pg.K_a]:
            angle -= 3
        elif keys[pg.K_d]:
            angle += 3
        if keys[pg.K_SPACE]:
            bullet_group.add(Bullet(cannon.center, angle))

        # Rotate the cannon image.
        rotated_cannon_img = pg.transform.rotate(cannon_img, -angle)
        cannon = rotated_cannon_img.get_rect(center=cannon.center)

        bullet_group.update()

        # Draw
        screen.fill((30, 40, 50))
        screen.blit(rotated_cannon_img, cannon)
        bullet_group.draw(screen)
        txt = FONT.render('angle {:.1f}'.format(angle), True, (150, 150, 170))
        screen.blit(txt, (10, 10))
        pg.display.update()

        clock.tick(30)

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

关于添加示例中的代码,最简单的解决方案是在

speed_x
方法中计算
speed_y
__init__
(“速度”会更合适),然后只需更新
self.rect.x
y
update
方法中的属性。

import math
import pygame


pygame.init()

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

BULLET_IMAGE = pygame.Surface((20, 11), pygame.SRCALPHA)
pygame.draw.polygon(BULLET_IMAGE, pygame.Color('aquamarine1'),
                [(0, 0), (20, 5), (0, 11)])


class Bullet(pygame.sprite.Sprite):

    def __init__(self, x, y, angle, speed):
        pygame.sprite.Sprite.__init__(self)
        # Rotate the bullet image (negative angle because y-axis is flipped).
        self.image = pygame.transform.rotate(BULLET_IMAGE, -angle)
        self.rect = self.image.get_rect(center=(x, y))
        angle = math.radians(angle)
        self.speed_x = speed * math.cos(angle)
        self.speed_y = speed * math.sin(angle)

    def update(self):
        self.rect.x += self.speed_x
        self.rect.y += self.speed_y

spr = pygame.sprite.Group()
bullet = Bullet(10, 10, 60, 3)
bullet2 = Bullet(10, 10, 30, 3)
spr.add(bullet, bullet2)

play = True
while play:
    clock.tick(60)
    for ev in pygame.event.get():
        if ev.type == pygame.QUIT:
            play = False
    screen.fill((30,30,40))
    spr.update()
    spr.draw(screen)
    pygame.display.flip()

pygame.quit()

有一个问题,因为

pygame.Rect
s 的 x 和 y 属性只能是整数,所以移动不会 100% 正确。要解决这个问题,您需要将精灵的坐标/位置存储在单独的变量中,添加速度,然后更新矩形:

    # In `__init__`.
    self.pos_x = x
    self.pos_y = y

def update(self):
    self.pos_x += self.speed_x
    self.pos_y += self.speed_y
    self.rect.center = (self.pos_x, self.pos_y)

0
投票

我不知道………………………… ......................

© www.soinside.com 2019 - 2024. All rights reserved.