可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Me and a few friends have been coding an intresting new shooting mechanic. For It to work, we need to shoot in the direction the player is facing. The Sprite is being rotated with Pygame.Transform.Rotate. How can we get an angle, and shoot our bullet in that direction?
Here is Our Code for rotation of the Sprite
char_image_number = pygame.mouse.get_pressed()
if char_image_number == (0, 0, 0):
char_image = pygame.image.load(player_none).convert_alpha()
char_image = pygame.transform.rotate(char_image, angle)
screen.blit(background, (0,0))
screen.blit(char_image,(540, 260))
pygame.display.update()
elif char_image_number == (1, 0, 0):
print (angle)
char_image = pygame.image.load(player_left).convert_alpha()
angle+=1
char_image = pygame.transform.rotate(char_image, angle)
screen.blit(background, (0,0))
screen.blit(char_image,(540, 260))
pygame.display.update()
elif char_image_number == (0, 0, 1):
print (angle)
angle-=1
char_image = pygame.image.load(player_right).convert_alpha()
char_image=pygame.transform.rotate(char_image, angle)
screen.blit(background, (0,0))
screen.blit(char_image,(540, 260))
pygame.display.update()
elif char_image_number == (1, 0, 1):
char_image = pygame.image.load(player_both).convert_alpha()
char_image = pygame.transform.rotate(char_image, angle)
screen.blit(background, (0,0))
screen.blit(char_image,(540, 260))
pygame.display.update()
How can we shoot at the angle the player is facing?
回答1:
Here's a quick'n'dirty executable example of how to use vector math to do what you want.
import pygame
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill((0, 0, 0))
self.image.set_colorkey((0, 0, 0))
pygame.draw.polygon(self.image, pygame.Color('dodgerblue'), ((0, 0), (32, 16), (0, 32)))
self.org_image = self.image.copy()
self.angle = 0
self.direction = pygame.Vector2(1, 0)
self.rect = self.image.get_rect(center=(200, 200))
self.pos = pygame.Vector2(self.rect.center)
def update(self, events, dt):
for e in events:
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_SPACE:
self.groups()[0].add(Projectile(self.rect.center, self.direction.normalize()))
pressed = pygame.key.get_pressed()
if pressed[pygame.K_a]:
self.angle += 3
if pressed[pygame.K_d]:
self.angle -= 3
self.direction = pygame.Vector2(1, 0).rotate(-self.angle)
self.image = pygame.transform.rotate(self.org_image, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
class Projectile(pygame.sprite.Sprite):
def __init__(self, pos, direction):
super().__init__()
self.image = pygame.Surface((8, 8))
self.image.fill((0, 0, 0))
self.image.set_colorkey((0, 0, 0))
pygame.draw.circle(self.image, pygame.Color('orange'), (4, 4), 4)
self.rect = self.image.get_rect(center=pos)
self.direction = direction
self.pos = pygame.Vector2(self.rect.center)
def update(self, events, dt):
self.pos += self.direction * dt
self.rect.center = self.pos
if not pygame.display.get_surface().get_rect().contains(self.rect):
self.kill()
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
sprites = pygame.sprite.Group(Player())
clock = pygame.time.Clock()
dt = 0
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
sprites.update(events, dt)
screen.fill((30, 30, 30))
sprites.draw(screen)
pygame.display.update()
dt = clock.tick(60)
if __name__ == '__main__':
main()
Note: it seems that you're loading the image of your player every frame. You should not do that, as this is very slow. Just load each image once. Also, it's better to have only one spot in your code that calls pygame.display.update()
, as you should make sure it's called only once each frame.
回答2:
You have angle
in degrees so try this:
import math
# angle examples
for angle in [0, 30, 45, 60, 0+90, 30+90, 45+90, 60+90, 0+180, 30+180, 45+180, 60+180, 0+270, 30+270, 45+270, 60+270]:
bullet_speed = 10
bullet_move_x = bullet_speed * math.cos( math.radians(angle) )
bullet_move_y = -bullet_speed * math.sin( math.radians(angle) )
print angle, bullet_move_x, bullet_move_y
Now you can add bullet_move_x
, bullet_move_y
to bullet positon.
For better results keep bullet position as float
and convert to int
when you blit bullet
回答3:
This example demonstrates how vectors can be utilized to control the position, velocity and also the start offset of the bullets.
In the while loop we use math.atan2
to calculate the angle to the target which is then used to rotate the cannon image and passed to the Bullet
instances. In the __init__
method of the Bullet
s we rotate the offset vector, velocity vector and the sprite image by the angle.
To move the bullet, we add the (rotated) velocity vector to the position vector and set the rect position to the pos
vector. The rect has to be moved, because it holds the blit position and is used for collision detection.
import math
import pygame as pg
from pygame.math import Vector2
pg.init()
screen = pg.display.set_mode((640, 480))
FONT = pg.font.Font(None, 24)
BLACK = pg.Color('black')
BULLET_IMAGE = pg.Surface((20, 11), pg.SRCALPHA)
pg.draw.polygon(BULLET_IMAGE, pg.Color('grey11'), [(0, 0), (20, 5), (0, 11)])
class Bullet(pg.sprite.Sprite):
def __init__(self, pos, angle):
super(Bullet, self).__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)
# Add the offset vector to the position vector.
self.pos = Vector2(pos) + offset # Center of the sprite.
# Rotate the velocity vector (9, 0) by the angle.
self.velocity = Vector2(9, 0).rotate(angle)
def update(self):
# Add velocity to pos to move the sprite.
self.pos += self.velocity
self.rect.center = self.pos
def main():
clock = pg.time.Clock()
# The cannon image and rect.
cannon_img = pg.Surface((60, 22), pg.SRCALPHA)
pg.draw.rect(cannon_img, pg.Color('grey19'), [0, 0, 35, 22])
pg.draw.rect(cannon_img, pg.Color('grey19'), [35, 6, 35, 10])
orig_cannon_img = cannon_img # Store orig image to preserve quality.
cannon = cannon_img.get_rect(center=(320, 240))
angle = 0
# Add bullets to this group.
bullet_group = pg.sprite.Group()
playing = True
while playing:
for event in pg.event.get():
if event.type == pg.QUIT:
playing = False
elif event.type == pg.MOUSEBUTTONDOWN:
# Left button fires a bullet from cannon center with
# current angle. Add the bullet to the bullet_group.
if event.button == 1:
bullet_group.add(Bullet(cannon.center, angle))
bullet_group.update()
# Find angle to target (mouse pos).
x, y = Vector2(pg.mouse.get_pos()) - cannon.center
angle = math.degrees(math.atan2(y, x))
# Rotate the cannon image.
cannon_img = pg.transform.rotate(orig_cannon_img, -angle)
cannon = cannon_img.get_rect(center=cannon.center)
# Draw
screen.fill(pg.Color('darkseagreen4'))
bullet_group.draw(screen)
screen.blit(cannon_img, cannon)
txt = FONT.render('angle {:.1f}'.format(angle), True, BLACK)
screen.blit(txt, (10, 10))
pg.draw.line(
screen, pg.Color(150, 60, 20),
cannon.center, pg.mouse.get_pos(), 2)
pg.display.update()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()