问题描述
对于我正在创建的游戏,我的敌舰会向玩家精灵发射激光。 这些敌人旋转以面向玩家,然后发射旨在与玩家飞船相撞的射弹。
然而,这些激光精灵当前生成在一个固定位置,因此当玩家四处移动时,它们会与敌舰碰撞箱重叠,如您所见in this gif。
如何让生成位置围绕敌方精灵的边缘移动,以便在敌方精灵之外创建激光?
相关部分在用于生成位置的 DroneArc 类在第 146 行 的 Shoot 方法内,以及 第 44 行的激光类。
这里是简化的代码,如果需要可以复制:
import pygame
import sys
import time
import math
pygame.init()
WIDTH = 750
HEIGHT = 750
SIZE = WIDTH,HEIGHT
CLOCK = pygame.time.Clock()
screen = pygame.display.set_mode(SIZE)
FPS = 120
def in_game():
run = True
global player
player = MainShip(310,600)
global enemies
enemies = [Drone_Arc(50,50,(50,50),0),Drone_Arc(50,650,0)]
while run:
screen.fill(0)
player.x,player.y = pygame.mouse.get_pos()
player.draw()
for enemy in enemies[:]:
angle =90-math.degrees(math.atan2((player.y+player.get_height()/2 - (enemy.y+enemy.get_height()/2)),(player.x+player.get_width()/2- (enemy.x+enemy.get_width()/2))))
enemy.rotate_ship(angle)
enemy.draw()
enemy.shoot(angle)
enemy.move_lasers(player)
if collide(enemy,player):
enemies.remove(enemy)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
CLOCK.tick(FPS)
def collide(obj1,obj2):
offset_x = obj2.x - obj1.x
offset_y = obj2.y - obj1.y
return obj1.mask.overlap(obj2.mask,((int(offset_x)),((int(offset_y)))))
class Laser(pygame.sprite.Sprite):
def __init__(self,x,y,w,h,dx,dy):
pygame.sprite.Sprite.__init__(self)
self.original_image = pygame.Surface([w,h],pygame.SRCALPHA)
self.color = (255,0)
self.original_image.fill(self.color)
self.image = self.original_image
self.mask = pygame.mask.from_surface(self.image)
self.dx = dx
self.dy = dy
self.x = x
self.y = y
self.rect = self.image.get_rect(center = (self.x,self.y))
def draw(self,screen):
screen.blit(self.image,self.rect)
def rotate_laser(self,angle):
self.image = pygame.transform.rotate(self.original_image,angle)
self.rect = self.image.get_rect(center = (self.x,self.y))
self.mask = pygame.mask.from_surface(self.image)
def move(self,speed):
self.x += self.dx*speed
self.y += self.dy*speed
self.rect = self.image.get_rect(center = (self.x,self.y))
def collision(self,obj):
return collide(self,obj)
class Ship(pygame.sprite.Sprite):
def __init__(self,y):
self.x = x
self.y = y
self.ship_img = None
self.laser_img = None
self.lasers = []
self.cooldown_counter = 0
def cooldown(self):
if self.cooldown_counter >= self.COOLDOWN:
self.cooldown_counter = 0
elif self.cooldown_counter > 0:
self.cooldown_counter += 1
def get_width(self):
return self.image.get_width()
def get_height(self):
return self.image.get_height()
def rotate_ship(self,angle):
w,h = self.original_image.get_size()
Box = [pygame.math.Vector2(p) for p in [(0,(w,-h),(0,-h)]]
Box_rotate = [p.rotate(angle) for p in Box]
min_Box = (min(Box_rotate,key=lambda p: p[0])[0],min(Box_rotate,key=lambda p: p[1])[1])
max_Box = (max(Box_rotate,max(Box_rotate,key=lambda p: p[1])[1])
pivot = pygame.math.Vector2(w/2,-h/2)
pivot_rotate = pivot.rotate(angle)
pivot_move = pivot_rotate - pivot
origin = (self.x - w/2 + min_Box[0] - pivot_move[0],self.y - h/2 - max_Box[1] + pivot_move[1])
self.image = pygame.transform.rotate(self.original_image,angle)
self.mask = pygame.mask.from_surface(self.image)
class MainShip(Ship):
COOLDOWN = 15
def __init__(self,y):
super().__init__(x,y)
self.original_image = pygame.Surface([36,62],255,255)
self.original_image.fill(self.color)
self.image = self.original_image
self.mask = pygame.mask.from_surface(self.image)
def draw(self):
screen.blit(self.image,(self.x,self.y))
class Drone_Arc(Ship):
COOLDOWN = 50
def __init__(self,path,speed):
super().__init__(x,36],255)
self.original_image.fill(self.color)
self.image = self.original_image
self.mask = pygame.mask.from_surface(self.image)
self.laser_speed = 3
self.angle = 0
def shoot(self,angle):
if self.cooldown_counter == 0:
radians = math.atan2((player.y+player.get_height()/2 - (self.y+self.get_height()/2)),(player.x+player.get_width()/2- (self.x+self.get_width()/2)))
dx = math.cos(radians)
dy = math.sin(radians)
cx,cy = self.x + self.get_height() / 2,self.y + self.get_width()/2
lx,ly = cx + dx * (self.get_height()/2 + 11),cy + dy * (self.get_height()/2 + 11)
print(dx,dy)
laser_1 = Laser(lx,ly,81,22,dy)
laser_1.rotate_laser(angle)
self.lasers.append(laser_1)
self.cooldown_counter = 1
def move_lasers(self,obj):
self.cooldown()
for laser in self.lasers:
laser.move(self.laser_speed)
if laser.collision(obj):
self.lasers.remove(laser)
def draw(self):
screen.blit(self.image,self.y))
pygame.draw.circle(screen,(255,(player.x + player.get_width()/2,player.y + player.get_height()/2),3)
pygame.draw.circle(screen,(self.x+self.get_width()/2,self.y + self.get_height()/2),((((player.x + player.get_height()/2)+(self.x-self.get_width()/2))/2),(((player.y + player.get_height()/2)+(self.y+self.get_height()/2))/2)),3)
for laser in self.lasers:
laser.draw(screen)
in_game()
解决方法
阅读 How do I rotate an image around its center using PyGame? 并将建议应用于 Laser
类。这大大简化了您的代码:
class Laser(pygame.sprite.Sprite):
def __init__(self,x,y,w,h,dx,dy):
pygame.sprite.Sprite.__init__(self)
self.original_image = pygame.Surface([w,h],pygame.SRCALPHA)
self.color = (255,0)
self.original_image.fill(self.color)
self.image = self.original_image
self.mask = pygame.mask.from_surface(self.image)
self.dx = dx
self.dy = dy
self.x = x
self.y = y
self.rect = self.image.get_rect(center = (self.x,self.y))
def draw(self,screen):
screen.blit(self.image,self.rect)
def rotate_laser(self,angle):
self.image = pygame.transform.rotate(self.original_image,angle)
self.rect = self.image.get_rect(center = (self.x,self.y))
self.mask = pygame.mask.from_surface(self.image)
def move(self,speed):
self.x += self.dx*speed
self.y += self.dy*speed
self.rect = self.image.get_rect(center = (self.x,self.y))
在新代码中,属性 x
和 y
指定了激光的中心点。当激光发射时,您所要做的就是计算激光的中心 (lx
,ly
),具体取决于船的中心 (cy
,cy
) 和射击方向 (dx
,dy
):
class Drone_Arc(Ship):
# [...]
def shoot(self,angle):
if self.cooldown_counter == 0:
radians = math.atan2((player.y+player.get_height()/2 - (self.y+self.get_height()/2)),(player.x+player.get_width()/2- (self.x+self.get_width()/2)))
dx = math.cos(radians)
dy = math.sin(radians)
cx,cy = self.x + self.get_height() / 2,self.y + self.get_width()/2
lx,ly = cx + dx * (self.get_height()/2 + 11),cy + dy * (self.get_height()/2 + 11)
laser_1 = Laser(lx,ly,81,22,dy)
laser_1.rotate_laser(angle)
self.lasers.append(laser_1)
self.cooldown_counter = 1
碰撞检测不再起作用,因为 pygame.mask.Mask.overlap
的第二个参数是掩码左上边缘之间的偏移:
def collide(obj1,obj2):
offset_x = obj2.x - obj1.x
offset_y = obj2.y - obj1.y
return obj1.mask.overlap(obj2.mask,((int(offset_x)),((int(offset_y)))))
由于激光的属性x
和y
不存储左上边缘而是存储枢轴点(中心点),因此偏移量的计算是错误的。
然而,这可以轻松解决。只需修复偏移量的计算:
class Laser(pygame.sprite.Sprite):
# [...]
def collision(self,obj):
offset_x = obj.x - self.rect.left
offset_y = obj.y - self.rect.top
return self.mask.overlap(obj.mask,((int(offset_y)))))