问题描述
大约 500 行代码,所以为了更容易阅读,我相信这个错误来自 _update_asteroids(self) 函数。简单地说,当用户处于暂停游戏状态并且必须按下播放键时,如果玩家决定等待让我们说一分钟。一分钟的小行星将在同一时间产生。我相信这是因为 pygame.time.get_ticks() 函数在游戏暂停时继续获得滴答声。有没有办法重置刻度或使其在用户决定在按下播放按钮之前等待时不会产生 100 颗小行星?
import pygame
import sys
from pygame.sprite import Sprite
import random
from time import sleep
import pygame.font
class Game(Sprite):
""" a class the creates a window with a blue screen """
def __init__(self):
pygame.init()
super().__init__()
#--------------------------------------------------------------------------------------------
#screen size,color,caption
self.screen = pygame.display.set_mode((1200,800)) #create attribute to hold display settings
self.bg_color = (0,255) #create attribute to hold RGB color (blue)
pygame.display.set_caption("Blue Screen")
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#tank drawing
self.screen_rect = self.screen.get_rect() #get the screen rect dim
self.image = pygame.image.load('images/tank.gif') #load the image from directory
self.rect = self.image.get_rect() #get the image rect dim
self.rect.center = self.screen_rect.center #store the screens center x/y coord
self.x = float(self.rect.x)
self.y = float(self.rect.y)
#tank movement
self.tank_moving_left = False
self.tank_moving_right = False
self.tank_moving_up = False
self.tank_moving_down = False
self.tank_speed = 0.5 #tank pixels
self.direction_right = self.image #holds right image
self.direction_left = pygame.transform.flip(self.image,True,False) #holds left image
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#UI
self.health = 100
self.visable = True
self.sb = scoreboard(self)
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#bullet
self.bullets = pygame.sprite.Group()
self.current_direction = self.direction_right
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#asteroids
self.asteroids = pygame.sprite.Group()
self.next_object_time = 0
self.time_interval = 250
#self._create_asteroids()
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#button
self.game_active = False
self.play_button = Button(self,"Play")
#--------------------------------------------------------------------------------------------
def _create_asteroids(self):
""" create the asteroid shower """
#create a asteroid
number_of_aliens = 1
for asteroid_num in range(number_of_aliens):
self._create_asteroid()
def _create_asteroid(self):
asteroid = Asteroid(self)
if asteroid.x <= 0:
asteroid.direction = 1
elif asteroid.x >= 1160:
asteroid.direction = -1
self.asteroids.add(asteroid)
def move(self):
""" move tnak tank_speed based on direction of movement (key pressed)
also detect collision """
if self.tank_moving_right and self.rect.right < self.screen_rect.right:
self.x += self.tank_speed
self.rect.x = self.x
if self.tank_moving_left and self.rect.left > self.screen_rect.left:
self.x -= self.tank_speed
self.rect.x = self.x
if self.tank_moving_down and self.rect.bottom < self.screen_rect.bottom:
self.y += self.tank_speed
self.rect.y = self.y
if self.tank_moving_up and self.rect.top > self.screen_rect.top:
self.y -= self.tank_speed
self.rect.y = self.y
def draw_healthbar(self):
pygame.draw.rect(self.screen,(255,0),(self.rect.x,self.rect.y - 20,100,10))
pygame.draw.rect(self.screen,(0,255,100 - (100 - self.health),10))
def blitme(self):
""" draw the image of the tank """
self.screen.blit(self.image,self.rect)
def tank_asteroid_collision(self):
#get rid of asteroids that collide with tank
for asteroid in self.asteroids.copy():
if pygame.Rect.colliderect(self.rect,asteroid.rect):
if self.health > 25:
self.health -= 25
self.asteroids.remove(asteroid)
print(self.health)
else:
self._tank_death()
self.sb.reset_score()
self.game_active = False
pygame.mouse.set_visible(True)
def _update_screen(self):
""" update screen """
self.screen.fill(self.bg_color)
self.blitme()
for bullet in self.bullets.sprites():
self.bullets.draw(self.screen)
collisions = pygame.sprite.groupcollide(self.bullets,self.asteroids,True)
if collisions:
for asteroids in collisions.values():
self.sb.score += 100 * len(asteroids)
self.sb.prep_score()
self.sb.check_high_score()
#draw healthbar if game active
if self.game_active:
self.draw_healthbar()
self.asteroids.draw(self.screen)
#draw the play button and other buttons if game is paused
if not self.game_active:
self.play_button.draw_button()
self.sb.show_score()
pygame.display.flip()
def _tank_death(self):
sleep(0.5)
self.bullets.empty()
self.asteroids.empty()
self.center_ship()
self.health = 100
def _check_KEYDOWN(self,event):
""" when key is press either quit,or move direction of arrow pressed and flip image """
if event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_RIGHT:
self.tank_moving_right = True
self.image = self.direction_right
self.current_direction = self.direction_right
elif event.key == pygame.K_LEFT:
self.tank_moving_left = True
self.image = self.direction_left
self.current_direction = self.direction_left
elif event.key == pygame.K_UP:
self.tank_moving_up = True
elif event.key == pygame.K_DOWN:
self.tank_moving_down = True
elif event.key == pygame.K_SPACE:
self._fire_bullet()
def _check_KEYUP(self,event):
""" when key is let go stop moving """
if event.key == pygame.K_RIGHT:
self.tank_moving_right = False
elif event.key == pygame.K_LEFT:
self.tank_moving_left = False
elif event.key == pygame.K_UP:
self.tank_moving_up = False
elif event.key == pygame.K_DOWN:
self.tank_moving_down = False
def _fire_bullet(self):
""" create a bullet and add it to the bullets group """
if self.current_direction == self.direction_left:
#create new bullet and set bullet path
new_bullet = Bullet(self)
new_bullet.bullet_shootRight = False
new_bullet.bullet_shootLeft = True
#change direction of bullet starting point
new_bullet.x -= 100
#add bullet to sprite list
self.bullets.add(new_bullet)
elif self.current_direction == self.direction_right:
#create new bullet and set bullet path
new_bullet = Bullet(self)
new_bullet.bullet_shootRight = True
new_bullet.bullet_shootLeft = False
#add bullet to sprite list
self.bullets.add(new_bullet)
def center_ship(self):
""" centers the ship in middle of screen """
self.rect.center = self.screen_rect.center
self.x = float(self.rect.x)
self.y = float(self.rect.y)
def _update_tank(self):
""" move tank or quit game based on keypress """
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_KEYDOWN(event)
elif event.type == pygame.KEYUP:
self._check_KEYUP(event)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
self._check_play_button(mouse_pos)
def _check_play_button(self,mouse_pos):
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked and self.play_button.rect.collidepoint(mouse_pos):
self.game_active = True
pygame.mouse.set_visible(False)
def _update_asteroids(self):
""" afer 'x' milliseconds create a new asteroid """
current_time = pygame.time.get_ticks()
if current_time > self.next_object_time:
self.next_object_time += self.time_interval
self._create_asteroids()
def run_game(self):
""" loops the game/ updates screen/ checks for key clicks"""
while True:
self._update_tank()
if self.game_active:
self.move()
self.bullets.update()
self.tank_asteroid_collision()
#after certain number of time create a asteroid
self._update_asteroids()
self.asteroids.update()
#get rid of asteroids that have disapeared
for asteroid in self.asteroids.copy():
if asteroid.x >= 1200 or asteroid.x <= 0:
self.asteroids.remove(asteroid)
#get rid of bullets that have disapeared
for bullet in self.bullets.copy():
if bullet.rect.left >= 1200 or bullet.rect.right <= 0:
self.bullets.remove(bullet)
self._update_screen()
class Bullet(Sprite):
""" A class to manage bullets fired from the ship """
def __init__(self,game):
""" create a bullet object at the ships current position """
super().__init__()
self.screen = game.screen
self.bullet_speed = 2.0
self.bullet_width = 20
self.bullet_height = 5
self.bullet_color = (0,200,200)
self.bullet_shootRight = False
self.bullet_shootLeft = False
self.image = pygame.Surface((self.bullet_width,self.bullet_height))
self.image.fill(self.bullet_color)
self.rect = self.image.get_rect()
self.rect.midright = game.rect.midright
self.rect.y -= 5 #the tanks barrel is 5 pixels above center
self.rect.x += 15
#store the bullets position as a decimal value
self.y = float(self.rect.y)
self.x = float(self.rect.x)
def update(self):
""" move the bullet up the screen """
#update the decimal position of the bullet.
if self.bullet_shootRight:
self.x += self.bullet_speed
self.rect.x = self.x
elif self.bullet_shootLeft:
self.x -= self.bullet_speed
self.rect.x = self.x
class Asteroid(Sprite):
""" a class that represents a single asteroid """
def __init__(self,game):
""" initialize the asteroid and set its starting position """
super().__init__()
self.screen = game.screen
self.speed = 0.1
self.direction = 1
#load the asteroid image onto the screen
self.image = pygame.image.load('images/asteroid.gif')
self.rect = self.image.get_rect()
#start each new asteroid in a random part just outside the map on either the left or right
self.rect.x = random.randint(*random.choice([(0,(1160,1160)]))
self.rect.y = random.randint(0,760)
#store the asteroid's exact horizontal positon
self.x = float(self.rect.x)
self.y = float(self.rect.y)
def update(self):
""" move the asteroid to the right or left """
self.x += self.speed * self.direction
self.rect.x = self.x
self.y += (self.speed / 3) * self.direction
self.rect.y = self.y
class Button:
def __init__(self,game,msg):
""" Initialize button """
self.screen = game.screen
self.screen_rect = self.screen.get_rect()
#set the dimensions and properties of the button
self.width,self.height = 200,50
self.button_color = (0,0)
self.text_color = (255,255)
self.font = pygame.font.SysFont(None,48)
#Build the button's rect object and center it.
self.rect = pygame.Rect(0,self.width,self.height)
self.rect.center = self.screen_rect.center
self._prep_msg(msg)
def _prep_msg(self,msg):
""" Turn msg into a rendered image and center text on the button """
self.msg_image = self.font.render(msg,self.text_color,self.button_color)
self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
def draw_button(self):
""" draw blank button then draw message """
self.screen.fill(self.button_color,self.rect)
self.screen.blit(self.msg_image,self.msg_image_rect)
class scoreboard:
""" A class to report scoring information """
def __init__(self,game):
""" initialize scorekeeping attributes """
self.screen = game.screen
self.screen_rect = self.screen.get_rect()
self.score = 0
self.highscore = 0
# Font settings for scoring information
self.text_color = (30,30,30)
self.font = pygame.font.SysFont(None,48)
self.prep_score()
self.prep_highscore()
def reset_score(self):
self.score = 0
self.prep_score()
def prep_highscore(self):
""" Turn the high score into a rendered image. """
high_score = round(self.highscore,-1)
high_score_str = "{:,}".format(high_score)
self.high_score_image = self.font.render(high_score_str,255))
#center the high score at the top of the screen
self.high_score_rect = self.high_score_image.get_rect()
self.high_score_rect.centerx = self.screen_rect.centerx
self.high_score_rect.top = self.score_rect.top
def check_high_score(self):
""" check to see if there's a new high score """
if self.score > self.highscore:
self.highscore = self.score
self.prep_highscore()
def prep_score(self):
""" Turn the score into a renered image """
rounded_score = round(self.score,-1)
score_str = "{:,}".format(rounded_score)
self.score_image = self.font.render(score_str,255))
#display the score at the top of the screen
self.score_rect = self.score_image.get_rect()
self.score_rect.right = self.screen_rect.right
self.score_rect.top = 0
def show_score(self):
""" draw the score to the screen """
self.screen.blit(self.score_image,self.score_rect)
self.screen.blit(self.high_score_image,self.high_score_rect)
if __name__ == '__main__':
a = Game()
a.run_game()
解决方法
小行星的生成取决于 next_object_time
。 next_object_time
初始化为 0。您需要在按下播放按钮时设置 self.next_object_time
:
class Game(Sprite):
# [...]
def _check_play_button(self,mouse_pos):
if self.play_button.rect.collidepoint(mouse_pos):
self.game_active = True
pygame.mouse.set_visible(False)
self.next_object_time = pygame.time.get_ticks() + self.time_interval
为了使算法更加稳健,您可以根据小行星产生的当前时间设置 next_object_time
(这是可选的):
class Game(Sprite):
# [...]
def _update_asteroids(self):
""" afer 'x' milliseconds create a new asteroid """
current_time = pygame.time.get_ticks()
if current_time > self.next_object_time:
# self.next_object_time += self.time_interval
self.next_object_time = current_time + self.time_interval
self._create_asteroids()