pygame write basketball game - match man dribble to avoid defense and jump to shoot

Posted by someguyhere on Tue, 01 Feb 2022 23:49:18 +0100

There are many basketball games on the Internet. Here we use pygame to write one. There is a shooter and a defender in the game. The shooter dribbled the ball to avoid defense, jumped up and shot, and scored a point. The closer the shooter is to the basket, the higher the shooting accuracy, but the closer he is to the basket, the more likely he is to meet the defender. If he does, the game is over. Below is the effect picture of the game.

The background of the game is the basketball court, as shown in the figure above. There are three roles, one for shooter, one for defender and one for basketball, which are respectively defined by class. The character animation modeling adopts match man, because the match man modeling is easy to find, even if it is not difficult to draw one by yourself. Unfortunately, my drawing ability is too poor. I can only pull out the shape frame by frame from a video. All graphic backgrounds should be set to transparent. All figures of the three roles are as follows:

The shooter dribbles with 4 black shapes, frame number 0-3. When you press the space bar, the jump shot will be started, and the frame number will be changed to 4. The jump shot has 12 black shapes, frame number 4-15. All shapes are used to realize the animation of shooter dribble and jump shot. In the previous blog post "dribbling in basketball game save pictures of each frame with a list", it can be seen that under the condition of four graphics per second, four shapes can complete the dribbling animation. Note that the ball is also a part of the shape. However, in the case of 4 graphics per second, it is impossible to use only 4 graphics for jump shot animation. After a try, 4 graphics per second, I feel that 12 graphics should be used, that is, it takes 12 * \ 0.25 seconds = 3 seconds for jump shot from take-off to fall, so as to have a satisfactory animation effect. The 12 figures of the jump shot are a bit of a makeshift, but you can still see the animation effect. If you draw the 12 figures carefully, the effect may be better. Note that there is basketball in frames 4, 5 and 6 of the jump shot, and there is no basketball in the back frame. At an appropriate time (in this example, in frame 8), the basketball character will appear above the shooter and move to the backboard from this as the starting point. He will touch the backboard in frame 13, and the ball will move downward in frames 14 and 15. If he hits the shot, he will directly fall into the blue, and if he doesn't hit the shot, he will fall from the side. Basketball has only one figure.
The first problem to be solved is when you can shoot and when you can't shoot. There are various rules for shooting in the online shooting game. The rules of the game are: the farther away from the basket, the more inaccurate the shooting is. When shooting at a certain point, the shooting is irregular, or random, but the probability of hitting is a fixed value. Set the distance from the shooting point to the basket as l, let n=(L//100) take an integer, and use the random number generator to generate a random integer between 1 and n+1. If the specified random number is 1, it will be hit, and other random integers will not be hit. If the distance from the point to the basket frame is y < 100, n = 0, the hit rate is 100%; If 200 > L > 99, n = 1, the hit rate is 50%; For example, 300 > L > 199, n = 2, the hit rate is 33%, and so on.
The defender has only two yellow shapes, which seems to be a little less, which is barely enough in this case. When the distance between the defender and the shooter is greater than 200, the defender retreats to the initial position of defense. Less than 200, and the shooter is dribbling, and the defender approaches the defense, that is, the defender moves a certain distance towards the shooter (should be an integer). If he meets the shooter, the game ends. The so-called face, that is, the defender moves along the line between the defender and the shooter. In this way, the line L and the difference DX and Dy between the defender and the shooter along the X and Y axes form a right triangle. If the defender moves dx1 along the x-axis and dy1 along the y-axis every frame, only dx1/dy1=dx/dy, the defender can move towards the shooter. If dy1=7, then dx1=7dx/dy. However, if dy is 0, an error divided by 0 will be generated. To avoid this situation, judge the size of DX and Dy first. If DX < Dy, use the above formula; otherwise, dx1=7, dy1=7dy/dx. Note that DX and Dy cannot be 0 at the same time, because before it is 0 at the same time, the shooter and the defender will approach and collide, leading to the end of the game. In this way, the moving distance in different directions may be different. In the above example, the maximum value is that both right angle sides are 7 and the distance is 9.899. Only the integer can be taken as 9. If the smaller right angle side is 0, the distance is 7. In addition, the increment along the X and Y axes can only be integers, so there is also an error in the direction.
The following is the whole program of the game, with detailed comments. It should be easier to understand the program. The level is limited, and there is inevitably some thoughtlessness. Criticism and correction are welcome. Only the copy shell source program can not run correctly. You need to make all the shapes of shooters and defenders, ball shapes and background basketball court, and put them in the folder where the source program is located. The source program and all images will be packaged and uploaded, which can be downloaded by readers in need. When playing the game, because the shooter moves with the mouse, when opening the program or playing the game again, you must move the mouse to the outer boundary of the program window to avoid the collision between the program and the defender at the beginning, resulting in the end of the game.

import pygame
import math
import random
import os
class Ball():                       #Basketball
    def __init__(self,screen):      #screen is the main form of the game and an instance of Surface class
        self.screen=screen
        b=pygame.image.load('b.png').convert_alpha()                #Get basketball graphics
        r=b.get_rect()
        self.p=pygame.transform.scale(b,(r.width//2. R.height / / 2)) # zoom out
        self.x,self.y,self.xi,self.yi=0,0,0,0#(x,y) basketball coordinates, (xi,yi) is the increment between two basketball positions
        self.frameNum=9         #Basketball frame number (1-8), = 9, basketball is not visible
        self.mark=0             #Whether the shooting is successful or not, = 0 is not successful, = 1 is successful
        self.score=0            #Number of shots (score)        
    def draw(self):             #Call the main program to realize basketball animation
        if self.frameNum==9:    #Basketball frame number = 9, basketball is not visible           
            return
        if self.frameNum==1:    #The first frame calculates the necessary data. The coordinates of the next sentence (self.x,self.y) are the starting point of the ball
            dx,dy=(400-self.x),(40-self.y)  #The coordinate (400530) point is the point where the ball hits the backboard
            self.xi=dx//6 # basketball increment along the x-axis from the starting point to the backboard
            self.yi=dy//6 # basketball increment along the y-axis from the starting point to the backboard
            dist=math.sqrt((dx**2)+(dy**2))     #Distance from shooting point to backboard
            n=int(dist//The smaller the 100) # divisor, the lower the total hit rate
            if random.randint(1,n+1)==1:        #The random number is 1, and n+1 avoids dist < 100 as 0
                self.mark=1                     #Hit marked as 1           
            else:
                self.mark=0                     #If it is not in the middle, it is 0
        if self.frameNum>=1 and self.frameNum<6:    #From frame 1 to frame 5, the ball advances in this way
            self.x+=self.xi                         #Basketball increases by 1 increment along the x axis every frame
            self.y+=self.yi                         #Basketball increases by 1 increment along the y-axis every frame
            self.frameNum+=1
        elif self.frameNum==6:      #In this frame, the ball will hit the backboard. The landing point of hitting the backboard should be accurately controlled
            self.x=400              #x coordinate of the ball hitting the backboard    
            self.frameNum+=1
            if self.mark==1:        #Hit, the basketball landing point is close to the basket in the y-axis direction
                self.y=90
            else:                   #If you miss the shot, the y-axis direction of the basketball landing point is far from the basket
                self.y=70
        else:                       #The two points where the basketball falls, frames 7 and 8
            if self.mark==0:        #When the ball is not hit, the ball moves along the x-axis in addition to falling, and the ball falls from both sides of the basket
                if self.xi>=0:      #If the ball moves from left to right, in the last two frames, the ball continues to move from left to right along the x-axis
                    self.x+=30
                else:
                    self.x-=30      #Otherwise, in the last two frames, the ball continues to move from right to left along the x-axis
            self.y+=25              #If the x coordinate of the shot remains unchanged, that is, the ball falls directly through the basket
            self.frameNum+=1
        self.screen.blit(self.p, (self.x, self.y)) #Draw basketball at the specified position on the screen
        if self.frameNum==9 and self.mark==1:      #When all actions of the ball are completed, judge whether the score is increased by 1 
            self.score+=1
class Guard():                      #Defenders
    def __init__(self,screen):      ##screen is the main form of the game and an instance of Surface class
        self.screen=screen
        self.images=[]
        for n in range(2):          #Save 2 images to the list
            p = pygame.image.load(str(n+16)+'.png').convert_alpha()#The file name is 16 png,17. png
            r=p.get_rect()      
            p = pygame.transform.scale(p, (r.width//6. R.height / / 6)) # resize the image
            self.images.append(p)
        self.frameNum=0                 #Frame number, 0-1
        self.x,self.y=400,300           #The initial coordinates of the defender in the form
        self.PlayerX,self.PlayerY=0,0   #At this time, the shooter coordinates
        self.PlayerFrameNum=0           #The shooter's frame number
        self.rect=None#Call blit to draw the graph and return rect to record the graph in screen coordinates and graph width and height to detect collision
    def draw(self):                     #Call the main program to realize the defender animation
        p=self.images[self.frameNum]                #Take out the current frame graph
        if self.PlayerX-self.x<0:                   #Facing the shooter
            p=pygame.transform.flip(p,True,False)
        dx,dy=self.PlayerX-self.x,self.PlayerY-self.y #The difference dx and dy between the defender and the shooter along the x and y axes
        dist=math.sqrt((dx**2)+(dy**2))               #Calculate the distance between the shooter and the defender
        dx1,dy1=0,0                 #The defender moves dx1 along the x axis and dy1 along the y axis every frame
        if dist>200:                #If the distance from the shooter is > 200, return to the initial point
            self.x,self.y=400,300
        elif self.PlayerFrameNum<4: #If the shooter does not shoot, approach the shooter, if the shooter shoots, the position of the defender remains unchanged
            if abs(dx)<abs(dy):     #Ensure that abs(dy) is not 0, so that the next sentence dx/dy will not be divided by 0
                d=abs(dx/dy)
                dy1=7                #If the long side of the rectangle is 7,
                dx1=int((dy1*d)//1) #dx1 may be: 0, 1, 2, 3, 4, 5, 6, 7
            else:                    #Ensure that abs(dx) is not 0, so that the next sentence dy/dx will not be divided by 0
                d=abs(dy/dx)
                dx1=7                #If the long side of the rectangle is 7,
                dy1=int((dx1*d)//1) #dy1 may be: 0, 1, 2, 3, 4, 5, 6, 7
        if dx<0:                     #Get the sign of dx
            dx1=-dx1
        if dy<0:                     #Get the sign of dy
            dy1=-dy1
        self.x+=dx1                  #Defender movement
        self.y+=dy1#The following sentence returns rect to detect collision. Its attributes X and y are the coordinates of the figure in the game window, and width and height are the width and height of the figure
        self.rect=self.screen.blit(p,(self.x,self.y))   #Draw the defender at the specified position on the screen
        self.frameNum+=1        
        if self.frameNum==2:
            self.frameNum=0
class Player():                 #Shooting hand
    def __init__(self,screen):  #screen is the main form of the game and an instance of Surface class
        self.screen=screen
        self.images=[]
        for n in range(16):      #Save 16 frame images (including dribble and jump shot images) to the list
            p = pygame.image.load(str(n)+'.png').convert_alpha()#The file name is 1 png,2. png...
            r=p.get_rect()      
            p = pygame.transform.scale(p, (r.width//6. R.height / / 6)) # resize the image
            self.images.append(p)
        self.frameNum=0                 #Frame number, dribble 0 to 3, jump shot 4 to 15
        self.x,self.y=0,0               #Coordinates of the image on the form
        self.mouseX,self.mouseY=0,0     #The mouse coordinate value
        self.jumpUpOrDown=-10   #After pressing the space bar, the shooter jumps up, and the initial value is negative. Fall after reaching the highest point, which is a positive number
        self.rect=None#Call blit to draw the graph and return rect to record the graph in screen coordinates and graph width and height to detect collision
    def dribble(self):          #Dribble animation
        p=self.images[self.frameNum]
        if self.mouseX-self.x<0:        #Mouse oriented
            p=pygame.transform.flip(p,True,False)
        self.x,self.y=self.mouseX,self.mouseY   #Shooter coordinates = mouse coordinates
        if self.x<1:                            #The control shooter must be on the basketball court
            self.x=1
        if self.x+90>width:
            self.x=width-90
        if self.y<230:
            self.y=230
        if self.y+120>height:
            self.y=height-120
        self.rect=self.screen.blit(p,(self.x,self.y)) #Draw a graph at the specified position and return rect
        self.frameNum+=1
        if self.frameNum==4:
            self.frameNum=0
    def jumpShot(self):                             #Jump shot animation
        p=self.images[self.frameNum]
        if self.x>width/2:                          #Facing the backboard
            p=pygame.transform.flip(p,True,False)
        self.screen.blit(p, (self.x, self.y))   #The initial position of jump shot is the position when the dribble turns to jump shot
        self.y+=self.jumpUpOrDown               #After that, it goes up first (the y value decreases), and then goes down to the highest point
        self.frameNum+=1        
        if self.frameNum==9:    #Start falling, and the falling value is positive
            self.jumpUpOrDown=10           
        if self.frameNum==16:   #=16, jump shot end, transfer ball
            self.frameNum=0
            self.jumpUpOrDown=-10
pygame.init()
os.environ['SDL_VIDEO_WINDOW_POS']="%d,%d"%(200,40) #The number of points from the left and top of the game window is 200,40
size = width, height = 800,600                      #Create game window size
screen = pygame.display.set_mode(size)  
pygame.display.set_caption("Pitchers dribble and jump shot")        #Set window title
bg_img = pygame.image.load("Basketball court 1.png").convert() #Background basketball court image

fclock = pygame.time.Clock()                        #Create a clock to control the frequency
fps = 4                                             #Define refresh rate
player=Player(screen)                               #Examples of Shooters
ball=Ball(screen)                                   #Basketball examples
guard=Guard(screen)                                 #Defender class instance
font1 = pygame.font.SysFont('Song style', 50, True)       #Create font
gameOver=False                                      #Whether the game is over, not at the beginning
running = True                                      #Whether the program ends and runs initially
while running:    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:               #Handle exit events
            running = False                         #Program end
        if event.type == pygame.MOUSEMOTION:        #Mouse movement event
            player.mouseX,player.mouseY=event.pos   #Pass the mouse position to the shooter for dribbling
        if event.type == pygame.KEYUP:              #Lift the event after pressing the key to avoid not lifting the long key
            if event.key == pygame.K_SPACE:         #Press the space bar and lift it up
                if player.frameNum<4:               #If in dribbling state, turn to shooting state
                    player.frameNum=4               #It is already in shooting status and will not be processed
            if event.key == pygame.K_r and gameOver==True:     #Press the r key and lift it up to play the game again
                gameOver=False
                ball.score=0
    screen.blit(bg_img, (0, 0))             #Draw basketball court background
    surface1=font1.render('score:'+str(ball.score),True,[255,0,0])  #Cannot display Chinese
    screen.blit(surface1, (20, 20))         #Display the number of goals (score)
    if gameOver==True:                      #If the game is over, the subsequent program will no longer be executed
        fclock.tick(fps)   #fps is the number of frames per second, minus the program running time. In order to realize fps, it also needs to delay time
        continue
    if player.frameNum>=4:              #If the shooter frame number > = 4, the shooter is making a jump shot
        player.jumpShot()
        if player.frameNum==8:          #Frame 8 jump up without a ball in hand, the basketball will appear and start to rebound
            ball.frameNum=1             #The ball moves to the backboard frame 1
            ball.x=player.x             #The starting position of the ball moving towards the backboard
            ball.y=player.y
    else:                                   #If the shooter's frame number is < 4, the shooter is dribbling
        player.dribble()
    ball.draw()                             #Basketball animation
    guard.PlayerX,guard.PlayerY=player.x,player.y   #Pass the shooter's position to the defender
    guard.PlayerFrameNum=player.frameNum            #Pass the shooter's frame number to the defender
    guard.draw()                            #Defensive player animation        
    if player.frameNum<4:               #Judge whether there is a collision with the defender only when the shooter dribbles the ball
        if player.rect.colliderect(guard.rect):     #Detect whether there is a collision between the shooter and the defender
            gameOver=True                           #There is a collision and the game is over
            surface2=font1.render('if play again,press key r',True,[255,0,0])
            screen.blit(surface2, (20, 100))        #If you continue to play, press r
    pygame.display.flip()                           #Refresh the game scene
    fclock.tick(fps)        #fps is the number of frames per second, minus the program running time. In order to realize fps, it also needs to delay time
pygame.quit()

Topics: Python Game Development pygame