Write the game "Alien Invasion" in python: limit the scope of the spaceship and add shooting functions to the spaceship

Limit the range of motion of the spaceship

Currently, if the player holds down the arrow keys long enough, the ship will move offscreen and disappear without a trace. Let's fix this problem and let the spaceship stop moving after reaching the edge of the screen. To do this, we will modify the method update() of the ship class:

def update(self):
		#根据移动标志调整飞船的位置
			#更新飞船的centerx值,而不是rect
		if self.moving_right and self.rect.right < self.screen_rect.right:
			self.center += self.ai_settings.ship_speed_factor	
		if self.moving_left and self.rect.left > 0:
			self.center -= self.ai_settings.ship_speed_factor	
			
		#根据self.center更新rect值
		self.rect.centerx = self.center

The above code checks the position of the spaceship before modifying the value of self.center. self.rect.right returns the x-coordinate of the bounding rectangle of the spaceship, if this value is less than the value of self.screen_rect.right, it means the spaceship has not touched the right edge of the screen. The situation is similar for the left edge: if the x-coordinate of the left edge of the rect is greater than zero, the spaceship is not touching the left edge of the screen. This ensures that the value of self.center is only adjusted when the ship is onscreen.

If alien_invasion.py is at this time, the spaceship will stop moving after touching the left or right edge of the screen.

Refactor check_events()

As the game development progresses, the function check_events() will become longer and longer, and we put part of its code in two functions:
one is responsible for handling the KEYDOWN event, and the other is responsible for handling the KEYUP event:

import sys
import pygame

def check_keydown_events(event,ship):
	if event.key == pygame.K_RIGHT:
		ship.moving_right = True
	elif event.key == pygame.K_LEFT:
		ship.moving_left = True

def check_keyup_events(event,ship):
	if event.key == pygame.K_RIGHT:
		ship.moving_right = False
	elif event.key == pygame.K_LEFT:
		ship.moving_left = False
				
def check_events(ship):
	for event in pygame.event.get():
		
		if event.type == pygame.QUIT:
			sys.exit()
			
		elif event.type == pygame.KEYDOWN:
			check_keydown_events(event,ship)
		elif event.type == pygame.KEYUP:
			check_keyup_events(event,ship)
			

 def update_screen(ai_settings,screen,ship):
		#每次循环时都重绘屏幕
		screen.fill(ai_settings.bg_color)
		ship.blitme()
		
		#让最近绘制的屏幕可见
		pygame.display.flip()

summary

At this point in the project, we have finished drawing the window, drawing the spaceship and setting the position. The movement speed of the spaceship is also set to speed up the pace of the game when it reaches a certain level. Of course, there is also the left and right movement of the spaceship. We created four modules in total. Let's summarize the respective functions of the four modules:

alien_invasion.py

The main file creates a series of objects that are used throughout the game: settings stored in ai_settings, surfaces stored in screen,
and a ship instance. The file alien_invasion also contains the game's main loop, which is a
while loop that calls check_events(), ship.update(), and update_screen().
To start Alien Invasion, simply run the file alien_invasion.py. Code contained in other files (settings.py, game_functions.py, ship.py) is imported directly or indirectly into this file.

settings.py

The file settings.py contains the Settings class, which simply contains __init__(), which is responsible for initializing the properties that control the game's appearance and the speed of the spaceship.

game_funcations.py

This file contains a series of functions that do most of the work of the game. The function check_events() detects related events, such as key up or key down, and uses the helper functions
check_keydown_events() and check_keyup_events() to handle the events. As it stands, these functions manage the movement of the spaceship. The module game_functions also contains
update_screen(), which is used to redraw the screen every time the main loop executes.

ship.py


This file contains the Ship class, which contains the method __init__(), the method update() to manage the position of the ship, and the method blitme() to draw the ship on the screen. The image representing the ship is stored in ship.bmp under the folder images.


shooting

Next, let's add shooting function to the spaceship. We will write the code that fires bullets (small rectangles) when the player presses the space bar. Bullets will travel upwards through the screen and disappear when they reach the edge of the screen.

Add bullet settings

First, update settings.py to store the values ​​needed for the new class Bullet at the end of its method __init__() :

		self.bullet_speed_factor = 1
		self.bullet_width = 3
		self.bullet_height = 15
		self.bullet_color = 60,60,60
Create the Bullet class

Here we create the file bullet.py that stores the Bullet class, the first half of which is as follows:

import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
	#一个对飞船发射子弹管理的类
	def __init__(self,ai_settings,screen,ship):
		#在飞船所处位置创建一个子弹对象
		super(Bullet,self).__init__()
		self.screen = screen
		
		#在(0,0)处创建一个表示子弹的矩形,再设置正确的位置
		self.rect = pygame.Rect(0,0,ai_settings.bullet_width,
		ai_settings.bullet_height)
		self.rect.centerx = ship.rect.centerx
		self.rect.top = ship.rect.top
		
		#存储用小数表示的子弹位置
		self.y = float(self.rect.y)
		
		self.color = ai_settings.bullet_color
		self.speed_factor = ai_settings.bullet_speed_factor

The Bullet class inherits the Sprite class we imported from the module pygame.sprite. By using sprites, related elements in the game can be grouped, and then all elements in the group can be operated simultaneously. To create a bullet instance, you need to pass ai_settings, screen and ship instances to __init__(), and call super() to inherit Sprite

code description

self.rect = pygame.Rect(0,0,ai_settings.bullet_width,ai_settings.bullet_height)

Create the attribute rect of the bullet. Bullets are not image based, so we have to use the pygame.Rect() class to create a rectangle from blank. When creating an instance of this class, you must provide the x- and y-coordinates of the upper-left corner of the rectangle, as well as the width and height of the rectangle. We created this rectangle at (0,0), but the next two lines of code move it to the correct position, since the initial position of the bullet depends on the current position of the spaceship. The distance and height of the bullet are obtained from ai_settings.

self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top

We set the centerx of the bullet to the rect.centerx of the spaceship. The bullet should not shoot from the top of the spaceship, so we set the top property of the bullet's rect to the top property of the spaceship's rect, so that the bullet looks like it is shot from the spaceship.

#Store the bullet position expressed as a decimal
self.y = float(self.rect.y)

self.color = ai_settings.bullet_color

self.speed_factor = at_settings.bullet_speed_factor

We store the bullet's y coordinate as a fractional value to be able to fine-tune the bullet's velocity. Then store the color and speed of the bullet into self.color and self.speed_factor respectively.


The following is the second part of bullet.py - methods update() and draw_bullet():

	def update(self):
		#向上移动子弹
		#更新表示子弹位置的小数值
		self.y -= self.speed_factor
		self.rect.y = self.y
		
	def draw_bullet(self):
		#在屏幕上绘制子弹
		pygame.draw.rect(self.screen,self.color,self.rect)

The method update() manages the position of the bullet. After firing, the bullet moves up the screen, which means that the y coordinate will keep decreasing, so to update the bullet's position, we subtract the value of self.speed_factor from self.y. Next, we set self.rect.y to the value of self.y. The attribute speed_factor allows us to adjust the behavior of the game by increasing the speed of the bullets as the game progresses or as needed. After the bullet is fired, its x coordinate is always the same, so the bullet will travel vertically upwards in a straight line.

When we need to draw a bullet, we call draw_bullet(). The function draw.rect() fills the portion of the screen represented by the bullet's rect with the color stored in self.color.

Store bullets into groups

After defining the Bullet class and the necessary settings, we can write the code.

Fires a bullet every time the player presses the space bar.

First, we will create a group in alien_invasion.py that will store all the active bullets so that we can manage all the bullets fired. This group will be an instance of the pygame.sprite.Group class; the pygame.sprite.Group class is like a list, but provides additional functionality that helps in developing games. In the main loop, we'll use this group to draw the bullets on the screen, and update the position of each bullet:

from pygame.sprite import Group
#创建一个用于存储子弹的编组
	bullets = Group()
     
# while 循环来控制游戏
while True:
		
	gf.check_events(ai_settings,screen,ship,bullets)
	ship.update()
	bullets.update()	
	gf.update_screen(ai_settings,screen,ship,bullets)

Here we import the Group class from pygame.sprite.

This group is created outside the while loop so that a new bullet group does not need to be created each time the loop is run.

The bullets are passed to check_events() and update_screen(). In check_events(), the bullets need to be processed when the player presses the space bar; and in update_screen(), the bullets to be drawn on the screen need to be updated.

When you call update() on a group, the group will automatically call update() on each sprite in it, so the line bullest.update() will call bullet.update() for each bullet in the group bullest

open fire

In game_functions.py:

We need to modify check_keydown_events() so that it fires a bullet when the player presses the space bar. We don't need to modify check_keyup_events() because nothing happens when the player releases the space bar. We also need to modify update_screen() to ensure that each bullet is redrawn on the screen before flip() is called.

from bullet import Bullet

def check_keydown_events(event,ai_settings,screen,ship,bullets):
	if event.key == pygame.K_RIGHT:
		ship.moving_right = True
	elif event.key == pygame.K_LEFT:
		ship.moving_left = True
	elif event.key == pygame.K_SPACE:
       #创建一颗子弹,并将其加入到编组bullets中
		new_bullet = Bullet(ai_settings,screen,ship)
		bullets.add(new_bullet)

def check_events(ai_settings,screen,ship,bullets):
	for event in pygame.event.get():

		if event.type == pygame.QUIT:
			sys.exit()

		elif event.type == pygame.KEYDOWN:
			check_keydown_events(event,ai_settings,screen,ship,bullets)
		elif event.type == pygame.KEYUP:
			check_keyup_events(event,ship)

def update_screen(ai_settings,screen,ship,bullets):
		#每次循环时都重绘屏幕
		screen.fill(ai_settings.bg_color)
		#在飞船和外星人后面重绘所有子弹
		for bullet in bullets.sprites():
			bullet.draw_bullet()
		ship.blitme()

		#让最近绘制的屏幕可见
		pygame.display.flip()

Marshaling bullets is passed to check_keydown_events():

When the player presses the space bar, create a new bullet: the Bullet instance of new_bullet, and use the method add() to add it to the group bullets; the code bullet.add(new_bullet) stores the new bullet into the group bullets.

In the definition of check_events(), we need to add the formal parameter bullets; when calling check_keydown_events(), we also need to pass bullets to it as an actual parameter.

def update_screen(ai_settings,screen,ship,bullets):

We added the bullets parameter to update_screen() which draws bullets on the screen. The method bullets.sprites() returns a list containing all sprites in the grouped bullets. To draw all the fired bullets on the screen, we iterate through all the sprites in the group bullets and call draw_bullet() on each sprite

If you run alien_invasion.py at this point, you will be able to move the ship left and right, and fire any number of bullets. The bullet will travel upwards in the screen and disappear after reaching the top of the screen

insert image description here

We can modify the size, color and speed of the bullet in settings.py.

remove missing bullets

Currently, the bullets disappear after reaching the top of the screen, simply because pygame cannot draw them outside the screen. The bullets actually still exist, their y coordinates are negative, and they get smaller and smaller. This is a problem because they will continue to consume memory and processing power.
So we need to delete these bullets that have disappeared, otherwise the game will do more and more unnecessary work, and then it will become slower and slower.

Check for the condition that the bottom property of the bullet's rect is zero, which indicates that the bullet has passed the top of the screen:
alien_invasion.py

#在while循环中,删除已消失的子弹
for bullet in bullets.copy():
	if bullet.rect.bottom <= 0:
		bullets.remove(bullet)
	print(len(bullets))	

In a for loop, entries should not be removed from the list or marshall, so a copy of the marshall must be traversed.
We set up the for loop with the method copy(), which allows us to modify the bullets within the for loop. We check each bullet to see if it has disappeared from the top of the screen. If so, remove it from bullets.
The print() statement is used to display how many bullets are left in the current bullet, so as to verify that the bullet that has disappeared is indeed deleted.
.If
there is no problem at the end of the code, confirm that the bullet can be deleted normally, we will delete the print statement, if this statement is left, the speed of the game will be greatly reduced, because it takes more time to write the output to the terminal than to draw the graphics to the game The window takes more time.

Limit the number of bullets

Many shooters place a limit on the number of bullets that can appear on the screen at one time to encourage players to shoot with purpose. Below we also add the same restriction.

First, store the maximum number of bullets allowed in settings.py:

self.bullets_allowed = 3

Limit undisappeared bullets to 3.
In check_keydown_events() of game_functions.py, we check whether the number of undisappeared bullets is less than this setting before creating new bullets:

def check_keydown_events(event,ai_settings,screen,ship,bullets):
	if event.key == pygame.K_RIGHT:
		ship.moving_right = True
	elif event.key == pygame.K_LEFT:
		ship.moving_left = True
	elif event.key == pygame.K_SPACE:
		#创建一颗子弹,并将其加入到编组bullets中
		if len(bullets) <ai_settings.bullets_allowed:
			new_bullet = Bullet(ai_settings,screen,ship)
			bullets.add(new_bullet)

When the player presses space, we check the length of bullets, and if len(bullets) is less than 3, we create a new bullet; but if there are already 3 undisappeared bullets, nothing happens when the player presses space. If you run the game now, there will only be a maximum of 3 bullets on the screen.

Create function update_bullets()

Once the bullet management code is written and checked, it can be moved
into the module game_functions to keep the main program file alien_invasion.py as simple as possible, we create a
new function called update_bullets() and add it to the end of game_functions.py .

def update_bullets(bullets):
	#更新子弹的位置,并删除已消失的子弹
	#更新子弹的位置
	bullets.update()
	
	#删除已消失的子弹
	for bullet in bullets.copy():
		if bullet.rect.bottom <= 0:
			bullets.remove(bullet)

The code in this function is cut and pasted from alien_invasion.py and it only takes one parameter, the marshalling bullets.

The while loop in alien_invasion.py becomes very simple again:

# while 循环来控制游戏
	while True:
		
		gf.check_events(ai_settings,screen,ship,bullets)
		ship.update()
		bullets.update()
		gf.update_bullets(bullets)	
		gf.update_screen(ai_settings,screen,ship,bullets)

We made the main loop contain as little code as possible, so that we can quickly understand what is happening in the game just by looking at the function name. The main loop checks for player input, then updates the position of the spaceship and the positions of any bullets that have not disappeared. Next, we use the updated position to draw the new screen.

Create function fire_bullet()

Let's move the code for firing the bullet to a separate function, so that
only one line of code is used in check_keydown_events() to fire the bullet, making the elif code simple.

def check_keydown_events(event,ai_settings,screen,ship,bullets):
	if event.key == pygame.K_RIGHT:
		ship.moving_right = True
	elif event.key == pygame.K_LEFT:
		ship.moving_left = True
	elif event.key == pygame.K_SPACE:
		#创建一颗子弹,并将其加入到编组bullets中
		fire_bullet(ai_settings,screen,ship,bullets)
			
def fire_bullet(ai_settings,screen,ship,bullets):
	#如果还没有达到限制,就发射一颗子弹
	#创建新子弹,并将其加入到编组 bullets中
	if len(bullets) <ai_settings.bullets_allowed:
			new_bullet = Bullet(ai_settings,screen,ship)
			bullets.add(new_bullet)

The function fire_bullet() just contains the code to fire the bullet when the player presses the spacebar;
in check_keydown_events() we call fire_bullet() when the player presses the spacebar.

Guess you like

Origin blog.csdn.net/tianlei_/article/details/129600940