pygame Learning Notes Add a Wizard: Tank Moving and Rotating

Posted by darkfreaks on Wed, 08 May 2019 13:15:03 +0200

Content of this article

  • Use of Elves
  • Use of Rect Class
  • Realize the movement and rotation of a tank

Effect demonstration

Add a static tank

We already know how to create a game window, set up background pictures, and let the game program respond to keyboards and mice. Then we need to add an elf to the game. Elves are a concept in the game development, such as a tank, a tree, a bullet can be an elf.
pgame.sprite provides a basic sprite class, which includes image and location rect. It can only move a picture. When we want more functions, such as animation, we need to inherit Sprite and write our own code. In this study, we prepared three pictures, one tank fuselage and two gun barrels.

First, we can add the prepared material to the game project by dragging, copying and pasting.

After importing the resources, we began to write the code formally. First, we create a Surface object to store our first resource file, the same operation as adding background, as follows:

tank_image = pygame.image.load('tank.png').convert()                            #Create tanks

Next, we inherit the Sprite class and create a HeroTank class, so that we can further expand the function, first look at the specific code:

class HeroTank(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)								#Initialize Sprite class constructor
        self.tank_image = tank_image								 	#Initialization of Tank Pictures
        self.tank_rect = self.tank_image.get_rect()            			#Getting fuselage rect object

    def display(self,screen):
        screen.blit(self.tank_image, self.tank_rect)					#screen draws pictures of tank fuselage on rect of tank fuselage

Now, it has only one constructor and one real function. Then we use the HeroTank class to create an object and call the displaying method of this class to display the tank fuselage. The complete program is as follows:

# Import module
import pygame
from pygame.locals import *
from sys import exit

# Initialization section
pygame.init()

# Setting up Game Window
screen = pygame.display.set_mode((640,480))
pygame.display.set_caption("My Game Window")
background = pygame.image.load("background_640x480.jpg").convert()

# Tank Elves
tank_image = pygame.image.load('tank.png').convert_alpha()		#Use convert_alpha() to retain transparent information, not convert()

class HeroTank(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.tank_image = tank_image
        self.tank_rect = self.tank_image.get_rect()

    def display(self,screen):
        screen.blit(self.tank_image, self.tank_rect)

my_tank = HeroTank()

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()

    screen.blit(background, (0, 0))
    my_tank.display(screen)
    pygame.display.update()

Because in the object, we did not specify the rect position of the generated tank fuselage image, so the default is (0, 0). When we fill the tank image on rect, it defaults to the top left corner of srceen. The program runs as follows:

We added the tank's fuselage smoothly, and then we added the barrel. Its logic is similar. The following is the specific code:

# Import module
import pygame
from pygame.locals import *
from sys import exit

# Initialization section
pygame.init()

# Setting up Game Window
screen = pygame.display.set_mode((640,480))
pygame.display.set_caption("My Game Window")
background = pygame.image.load("background_640x480.jpg").convert()

# Tank Elves
tank_image = pygame.image.load('tank.png').convert_alpha()
cannon1_image = pygame.image.load('cannon_1.png').convert_alpha()

class HeroTank(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.tank_image = tank_image
        self.cannon1_image = cannon1_image
        self.tank_rect = self.tank_image.get_rect()
        self.cannon1_rect = self.cannon1_image.get_rect()

    def display(self,screen):
        screen.blit(self.tank_image, self.tank_rect)
        self.cannon1_rect.center = self.tank_rect.center #The barrel center point is consistent with the tank pilot rect center point.
        screen.blit(self.cannon1_image, self.cannon1_rect)

my_tank = HeroTank()

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()

    screen.blit(background, (0, 0))
    my_tank.display(screen)
    pygame.display.update()

Here we add the assignment statement of rect central point in the display() function, because if not, the barrel painting will also be in the upper left corner of the program, so it looks like the barrel is out of place. The program runs as follows:

Mobile tank

We have created an elf class, and we have a preliminary understanding of surface and rect objects. Then we control the movement of tanks by manipulating rect objects. First, we add four methods to control the movement of tanks from top to bottom. The code of the specific class is as follows. (Here, we use different keyboard acquisition methods from the previous section, personal control of it.) Experience feels better:

# Import module
import pygame
from pygame.locals import *
from sys import exit

# Initialization section
pygame.init()

# Setting up Game Window
screen = pygame.display.set_mode((640,480))
pygame.display.set_caption("My Game Window")
background = pygame.image.load("background_640x480.jpg").convert()

# Tank Elves
tank_image = pygame.image.load('tank.png').convert_alpha()
cannon1_image = pygame.image.load('cannon_1.png').convert_alpha()

class HeroTank(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.tank_image = tank_image
        self.cannon1_image = cannon1_image
        self.tank_rect = self.tank_image.get_rect()
        self.cannon1_rect = self.cannon1_image.get_rect()
        self.speed = 1;												#Pixels moved at a time

    def moveLeft(self):
        if self.tank_rect.left > 0:									#Display the range of rect so that the player's tank does not move out of the screen
            self.tank_rect.x -= self.speed
    def moveRight(self):
        elif self.tank_rect.right < 640:
            self.tank_rect.x += self.speed
    def moveUp(self):
        elif self.tank_rect.top > 0:
            self.tank_rect.y -= self.speed
    def moveDown(self):
        elif self.tank_rect.bottom < 480:
            self.tank_rect.y += self.speed

    def display(self,screen):
        screen.blit(self.tank_image, self.tank_rect)
        self.cannon1_rect.center = self.tank_rect.center
        screen.blit(self.cannon1_image, self.cannon1_rect)

my_tank = HeroTank()

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
    key_press = pygame.key.get_pressed()
    if key_press[K_w]:
        my_tank.moveUp()
    elif key_press[K_s]:
        my_tank.moveDown()
    elif key_press[K_a]:
        my_tank.moveLeft()
    elif key_press[K_d]:
        my_tank.moveRight()

    screen.blit(background, (0, 0))
    my_tank.display(screen)
    pygame.display.update()

Controlling Game Rate-Setting Frame Rate

When we run the program, we will find that the tank can move, but the speed is too fast, because although we only move one pixel at a time, but we move too many times in a second, here we use frames to control the speed of the whole game. If we can control the program refresh 30 times in one second, then our tank will only move 30 pixels in one second. It's a bit slow, but it looks much more controllable. We implement it using the following functions

framerate = pygame.time.Clock()        #Instantiate a middle object
framerate .tick(30)								#The control cycle is 30 frames per second

The clock.tick(30) function works to control the game rate. When the time difference between this function and the last function is less than 1/30 seconds, we wait and then run. We need to put the function in the main loop. Then we adjusted the moving speed of the tank slightly, that is, the speed variable in the example, to achieve a good effect (the changes are small, so the complete code is posted together with the next part):
Maybe you think he's still a little fast. I set speed to 8, which is 8 pixels at a time. Thirty times a second, a map of 480 pixels wide can only be moved in less than two seconds (because our tanks have width). Of course, the speed adjustment is simple. Now there is another problem. The tank moves in translation like a McNam wheel, which is not in line with the actual situation. Next, we will solve this problem.

Rotary Tank Elves

We can do this by rotating Rect:

pygame.transform.rotate(image,angle)     

The first parameter of transform.rotate() is image, and the second parameter is angle. After selection, a new object is returned. In programming, we will use this new object. Because we now have tanks moving up and down, so our image has four angles, 0, 90, 180, 270. We have added a new function to our tank class so that we can better control the selection:

def rotate(self,angle):
    self.tank_image = pygame.transform.rotate(tank_image, angle)  # Select and generate a new object
    self.rect = self.image.get_rect(center=self.rect.center)      #Update rect, otherwise it wobbles when rotated

As explained here, when we rotate the image, its rect will change, so we need to read the rect of the rotated image, and we read it with the center of the original image as the current center, otherwise it will be generated by default in the upper left corner. This will cause if you update rect, the tank will reset the initial point as soon as you rotate.
For an understanding of rotation, you can refer to this blog: pygame Notes-9 Picture Rotation and Boundary Rebound Bloggers and good analysis, not to mention here. Now take a look at the complete procedure:

# Import module
import pygame
from pygame.locals import *
from sys import exit

# Initialization section
pygame.init()

# Setting up Game Window
screen = pygame.display.set_mode((640,480))
pygame.display.set_caption("My Game Window")
background = pygame.image.load("background_640x480.jpg").convert()

# Tank Elves
tank_image = pygame.image.load('tank.png').convert_alpha()
cannon1_image = pygame.image.load('cannon_1.png').convert_alpha()

class HeroTank(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.tank_image = tank_image
        self.cannon1_image = cannon1_image
        self.tank_rect = self.tank_image.get_rect()
        self.cannon1_rect = self.cannon1_image.get_rect()
        self.speed = 8

    def moveLeft(self):
        if self.tank_rect.left > 0:
            self.tank_rect.x -= self.speed
        self.rotate(270)

    def moveRight(self):
        if self.tank_rect.right < 640:
            self.tank_rect.x += self.speed
        self.rotate(90)

    def moveUp(self):
        if self.tank_rect.top > 0:
            self.tank_rect.y -= self.speed
        self.rotate(180)

    def moveDown(self):
        if self.tank_rect.bottom < 480:
            self.tank_rect.y += self.speed
        self.rotate(0)

    def rotate(self, angle):
        # Select fuselage
        self.tank_image = pygame.transform.rotate(tank_image, angle)
        self.tank_rect = self.tank_image.get_rect(center=self.tank_rect.center)
        # Revolving barrel
        self.cannon1_image = pygame.transform.rotate(cannon1_image, angle)
        self.cannon1_rect = self.cannon1_image.get_rect(center=self.cannon1_rect.center)

    def display(self, screen):
        screen.blit(self.tank_image, self.tank_rect)
        self.cannon1_rect.center = self.tank_rect.center
        screen.blit(self.cannon1_image, self.cannon1_rect)


my_tank = HeroTank()
framerate = pygame.time.Clock()

while True:
    framerate .tick(30)
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
    key_press = pygame.key.get_pressed()
    if key_press[K_w]:
        my_tank.moveUp()
    elif key_press[K_s]:
        my_tank.moveDown()
    elif key_press[K_a]:
        my_tank.moveLeft()
    elif key_press[K_d]:
        my_tank.moveRight()
    screen.blit(background, (0, 0))
    my_tank.display(screen)
    pygame.display.update()

Running the program, we get the effect of the beginning of the article. We also have a barrel material that is not used. Here we can switch the barrel as an additional exercise by pressing a keyboard button.
The following is the download address of the entire project, including the code and image resources:

Topics: less Mobile Programming