There are a lot of basketball games on the Internet. Here is one using PyGame. There’s a shooter, there’s a defender. The shooter dribbles the ball around the defense, takes a jump shot, and scores a point. The closer the shooting hand is to the basket, the higher the accuracy of the shot, but the closer it is to the basket, the more likely it is to touch the defender, if touched, the game is over. Below is a rendering of the game.

The game is set on a basketball court, as shown above. There are three roles, one each for shooter, defender and basketball, each defined by class. The character animation uses matchstick man because it is easy to find matchstick man, even if you draw one yourself. Unfortunately, my drawing ability is poor, I can only pick out the shape from a video frame by frame. All graphics backgrounds should be set to transparent. All graphics of the 3 characters are as follows:

Shooting hand dribble has 4 black shapes (frame 0-3). When the space bar is pressed to start the jump shot, the frame number is changed to 4. The jump shot has 12 black shapes (frame 4-15), all of which are used to realize shooting hand dribble and jump shot animation. In the previous post “Dribbling in Basketball games _ Save every frame with a list”, you can see that at 4 frames per second, dribbling can be animated with 4 shapes. Note that the ball is also part of the shape. However, under the condition of 4 frames per second, it is impossible to use only 4 frames for jump-shot animation. I tried it, 4 frames per second, and felt that 12 frames should be used, that is, it takes 12*\0.25 seconds =3 seconds for jump-shot animation to get satisfactory animation effect.

Jump shot 12 graphics have a bit of a thing, but still can see the animation effect, such as the 12 graphics carefully drawn, the effect may be better. Note that jumper 4, 5 and 6 frames in the graphics are basketball, no basketball frame, back at the appropriate time (in this case) in 8 frames, basketball character will appear in the player’s upper part, as a starting point to a rebound movement, in the frame met 13 rebounds, 14 and 15 frames the ball downward movement, such as shooting, directly into the blue, if not hit, falling from the side direction. A basketball has only one shape.

The first thing to figure out is which case you can make a shot and which case you can’t. The rules of online shooting game are varied. The rules of the game is: the farther the distance from the basket, the more forbidden shooting, shooting at a certain point, that hit, that hit irregular, or random, but the probability is fixed value. Set the distance from the shooting point to the basket as L, set n=(L//100) as an integer, and use a random number generator to generate a random integer between 1 and n+1. Set random number as 1, make, other random integer make miss. Such as point to the basket frame distance Y<100,n=0, the shooting rate is 100%; If 200>L>99,n=1, the shooting rate is 50%; For example, 300>L>199,n=2, 33% shooting rate, and so on.

The defender has only two yellow shapes, which seems a little low, but barely enough for this example. When the distance between the defender and the shooter is greater than 200, the defender falls back to the starting position. Less than 200, and the shooter is dribbling the ball, the defender close to the defense, that is, the defender towards the shooter to move a number of distances (should be integer), such as touching the shooter, the game is over. Facing is when the defender moves along the line between the defender and the shooter.

Thus, the line L, and the difference between the defender and the shooter along the x and y axes, dx and dy, 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, then the defender is guaranteed to move toward the shooter. So dy1 is equal to 7, so dx1 is equal to 7dx dy. But dy is 0, so we’re going to divide by 0. Dx1 =7, dy1=7dy/dx.

Note that dx and dy cannot both be 0 at the same time, because before both are 0, the shooter and the defender will approach each other and collide, causing the game to end. In the example above, the maximum is 9.899 for both sides of the right Angle, which can only be integer: 9. If the smaller side is 0, the distance is 7. In addition, the increments along the x and y axes can only be integers, so there is also an error in direction.

The following is the full program of the game, with detailed annotations, should be easier to read the program. The level is limited, unavoidably have the place that considers not week, welcome the criticism to point out. Just copying the source program will not work correctly, you need to create all the shots and defenders, ball modeling, background basketball court, in the folder where the source program is located. The source program and all images will be packaged and uploaded for readers who need them to download. When playing the game, because the shooter moves with the mouse, when opening the program or replaying the game, the mouse must be moved 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():                       #篮球类
    def __init__(self,screen):      #screen是游戏主窗体,Surface类实例
        self.screen=screen
        b=pygame.image.load('b.png').convert_alpha()                #得到篮球图形
        r=b.get_rect()
        self.p=pygame.transform.scale(b,(r.width//2,r.height//2))   #缩小图形
        self.x,self.y,self.xi,self.yi=0,0,0,0#(x,y)篮球坐标,(xi,yi)是篮球两个位置间增量
        self.frameNum=9         #篮球帧编号(1-8),=9,篮球不可见
        self.mark=0             #此次投篮中否,=0不中,=1中
        self.score=0            #投篮投中次数(得分)        
    def draw(self):             #主程序调用,实现篮球动画
        if self.frameNum==9:    #篮球帧编号=9,篮球不可见           
            return
        if self.frameNum==1:    #第1帧计算必要数据,下句坐标(self.x,self.y)是球运行起点
            dx,dy=(400-self.x),(40-self.y)  #坐标(400,530)点是球碰到篮板上的点
            self.xi=dx//6                   #篮球从起始点到篮板每帧沿x轴前进的增量
            self.yi=dy//6                   #篮球从起始点到篮板每帧沿y轴前进的增量
            dist=math.sqrt((dx**2)+(dy**2))     #投篮点距离篮板距离
            n=int(dist//100)                    #除数越小,总投中率越低
            if random.randint(1,n+1)==1:        #随机数为1投中,n+1避免dist<100为0
                self.mark=1                     #投中标记为1           
            else:
                self.mark=0                     #投不中为0
        if self.frameNum>=1 and self.frameNum<6:    #从第1帧到第5帧,球按此法前进
            self.x+=self.xi                         #篮球每帧沿x轴增加1个增量值
            self.y+=self.yi                         #篮球每帧沿y轴增加1个增量值
            self.frameNum+=1
        elif self.frameNum==6:      #此帧球将碰到篮板,要准确控制碰到篮板的落点
            self.x=400              #球碰到篮板的x坐标    
            self.frameNum+=1
            if self.mark==1:        #投中,篮球落点y轴方向靠近篮筐
                self.y=90
            else:                   #投不中,篮球落点y轴方向离篮筐较远
                self.y=70
        else:                       #篮球下落的两个点,即第7,8帧
            if self.mark==0:        #球未投中,球除下落,还沿x轴方向移动,球从篮筐两侧落下
                if self.xi>=0:      #如球从左到右,最后两帧,球沿x轴方向继续从左向右移动
                    self.x+=30
                else:
                    self.x-=30      #否则最后两帧,球沿x轴方向继续从右向左移动
            self.y+=25              #如投中x坐标不变,即球直接下落穿过篮筐
            self.frameNum+=1
        self.screen.blit(self.p, (self.x, self.y)) #在屏幕指定位置绘制篮球
        if self.frameNum==9 and self.mark==1:      #球所有动作完成,判断得分是否加1 
            self.score+=1
class Guard():                      #防守者类
    def __init__(self,screen):      ##screen是游戏主窗体,Surface类实例
        self.screen=screen
        self.images=[]
        for n in range(2):          #将2帧图像保存到列表中
            p = pygame.image.load(str(n+16)+'.png').convert_alpha()#文件名为16.png,17.png
            r=p.get_rect()      
            p = pygame.transform.scale(p, (r.width//6, r.height//6))    #调整图像的大小
            self.images.append(p)
        self.frameNum=0                 #帧编号,0-1
        self.x,self.y=400,300           #防守运动员在窗体的初始坐标
        self.PlayerX,self.PlayerY=0,0   #此时投篮手坐标
        self.PlayerFrameNum=0           #此时投篮手帧号
        self.rect=None#调用blit绘制图形,返回rect记录图形在screen坐标和图形宽和高,用来检测碰撞
    def draw(self):                     #主程序调用,实现防守者动画
        p=self.images[self.frameNum]                #取出当前帧图形
        if self.PlayerX-self.x<0:                   #面向投篮手
            p=pygame.transform.flip(p,True,False)
        dx,dy=self.PlayerX-self.x,self.PlayerY-self.y #防守者和投篮手两点沿x、y轴的差值dx和dy
        dist=math.sqrt((dx**2)+(dy**2))               #计算投篮手和防守者距离
        dx1,dy1=0,0                 #防守者每帧沿x轴移动dx1,沿y轴移动dy1
        if dist>200:                #如距投篮者>200,返回初始点
            self.x,self.y=400,300
        elif self.PlayerFrameNum<4: #如投篮者未投篮,逼近投篮者,如投篮者投篮,防守者位置不变
            if abs(dx)<abs(dy):     #保证abs(dy)不为0,使下句dx/dy一定不会被0除
                d=abs(dx/dy)
                dy1=7                #如矩形长边为7,
                dx1=int((dy1*d)//1)  #dx1可能是:0,1,2,3,4,5,6,7
            else:                    #保证abs(dx)不为0,使下句dy/dx一定不会被0除
                d=abs(dy/dx)
                dx1=7                #如矩形长边为7,
                dy1=int((dx1*d)//1)  #dy1可能是:0,1,2,3,4,5,6,7
        if dx<0:                     #得到dx的正负号
            dx1=-dx1
        if dy<0:                     #得到dy的正负号
            dy1=-dy1
        self.x+=dx1                  #防守者移动
        self.y+=dy1#下句返回rect用来检测碰撞,其属性x,y是图形在游戏窗口坐标,width,hight是图形宽和高
        self.rect=self.screen.blit(p,(self.x,self.y))   #在屏幕指定位置绘制防守者
        self.frameNum+=1        
        if self.frameNum==2:
            self.frameNum=0
class Player():                 #投篮手类
    def __init__(self,screen):  #screen是游戏主窗体,Surface类实例
        self.screen=screen
        self.images=[]
        for n in range(16):      #将16帧图像(包括运球和跳投图像)保存到列表中
            p = pygame.image.load(str(n)+'.png').convert_alpha()#文件名为1.png,2.png...
            r=p.get_rect()      
            p = pygame.transform.scale(p, (r.width//6, r.height//6))    #调整图像的大小
            self.images.append(p)
        self.frameNum=0                 #帧编号,运球为0到3,跳投为4到15
        self.x,self.y=0,0               #图像在窗体的坐标
        self.mouseX,self.mouseY=0,0     #此时鼠标坐标值
        self.jumpUpOrDown=-10   #按空格键后投篮手向上跳,初始值为负数。到最高点后下落,为正数
        self.rect=None#调用blit绘制图形,返回rect记录图形在screen坐标和图形宽和高,用来检测碰撞
    def dribble(self):          #运球动画
        p=self.images[self.frameNum]
        if self.mouseX-self.x<0:        #面向鼠标
            p=pygame.transform.flip(p,True,False)
        self.x,self.y=self.mouseX,self.mouseY   #投篮手坐标=鼠标坐标
        if self.x<1:                            #控制投篮手必须在篮球场中
            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)) #在指定位置绘制图形,返回rect
        self.frameNum+=1
        if self.frameNum==4:
            self.frameNum=0
    def jumpShot(self):                             #跳投动画
        p=self.images[self.frameNum]
        if self.x>width/2:                          #面向篮板
            p=pygame.transform.flip(p,True,False)
        self.screen.blit(p, (self.x, self.y))   #跳投初始位置是运球转跳投时位置
        self.y+=self.jumpUpOrDown               #以后先向上(y值减少),到最高点后下降
        self.frameNum+=1        
        if self.frameNum==9:    #开始下落,下落值为正
            self.jumpUpOrDown=10           
        if self.frameNum==16:   #=16,跳起投篮结束,转运球
            self.frameNum=0
            self.jumpUpOrDown=-10
pygame.init()
os.environ['SDL_VIDEO_WINDOW_POS']="%d,%d"%(200,40) #游戏窗口距左侧和顶部点数为200,40
size = width, height = 800,600                      #创建游戏窗口大小
screen = pygame.display.set_mode(size)  
pygame.display.set_caption("投手运球和跳投")        #设置窗口标题
bg_img = pygame.image.load("篮球场1.png").convert() #背景篮球场图像

fclock = pygame.time.Clock()                        #创建控制频率的clock
fps = 4                                             #定义刷新频率
player=Player(screen)                               #投篮手类实例
ball=Ball(screen)                                   #篮球类实例
guard=Guard(screen)                                 #防守者类实例
font1 = pygame.font.SysFont('宋体', 50, True)       #创建字体
gameOver=False                                      #该次游戏是否结束,初始不结束
running = True                                      #程序是否结束,初始运行
while running:    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:               #处理退出事件
            running = False                         #程序结束
        if event.type == pygame.MOUSEMOTION:        #鼠标移动事件
            player.mouseX,player.mouseY=event.pos   #将鼠标位置传递给投篮手用于运球
        if event.type == pygame.KEYUP:              #按键后抬起事件,避免长按键不抬起
            if event.key == pygame.K_SPACE:         #按空格键后抬起
                if player.frameNum<4:               #如在运球状态,转投篮状态
                    player.frameNum=4               #已在投篮状态不处理
            if event.key == pygame.K_r and gameOver==True:     #按r键后抬起,重玩游戏
                gameOver=False
                ball.score=0
    screen.blit(bg_img, (0, 0))             #绘制篮球场背景
    surface1=font1.render('score:'+str(ball.score),True,[255,0,0])  #不能显示中文
    screen.blit(surface1, (20, 20))         #显示进球数(得分)
    if gameOver==True:                      #如果该次游戏结束,后边程序不再执行
        fclock.tick(fps)   #fps是每秒多少帧,减去程序运行时间,为实现fps,还需延迟时间
        continue
    if player.frameNum>=4:              #如果投篮手帧号>=4,投篮手正在跳投
        player.jumpShot()
        if player.frameNum==8:          #第8帧跳起手中无球,篮球要出现并开始向篮板运动
            ball.frameNum=1             #球向篮板运动第1帧
            ball.x=player.x             #球向篮板运动的起始位置
            ball.y=player.y
    else:                                   #如果投篮手帧号<4,投篮手正在运球
        player.dribble()
    ball.draw()                             #篮球动画
    guard.PlayerX,guard.PlayerY=player.x,player.y   #将投篮手位置传递给防守者
    guard.PlayerFrameNum=player.frameNum            #将投篮手帧号传递给防守者
    guard.draw()                            #防守运动员动画        
    if player.frameNum<4:               #仅在投篮手运球时,判断和防守者是否发生碰撞
        if player.rect.colliderect(guard.rect):     #检测投篮者和防守者是否发生碰撞
            gameOver=True                           #发生碰撞,游戏结束
            surface2=font1.render('if play again,press key r',True,[255,0,0])
            screen.blit(surface2, (20, 100))        #显示如继续玩,按r键
    pygame.display.flip()                           #刷新游戏场景
    fclock.tick(fps)        #fps是每秒多少帧,减去程序运行时间,为实现fps,还需延迟时间
pygame.quit()

Copy the code