网上有很多篮球类游戏,这里应用pygame编写了一个。游戏中有一个投篮手,一个防守者。投篮手运球避开防守,跳起投篮,投中得一分。投篮手离篮筐越近,投篮准确率越高,但离篮筐越近,越可能碰到防守者,如碰到,游戏完结。下边是游戏的效果图。

游戏背景是篮球场,如上图。有3个角色,投篮手、防守者和篮球各1个,别离用类来定义。角色动画造型采纳火柴人,这是因为火柴人造型容易找到,即便本人画一个也不难。惋惜自己画图能力太差,只能从一段视频中一帧一帧抠出造型。所有图形背景都要设置为通明。3个角色所有图形如下:

投篮手运球有4个彩色造型,帧号0-3,当按空格键将启动跳投,将帧号批改为4,跳投有12个彩色造型,帧号4-15,所有造型用来实现投篮手运球和跳投动画。在前1篇博文“篮球游戏中的运球_用列表保留每帧图片”中,能够看到在每秒4帧图形状况下,4个造型就能够实现运球动画,留神球也是造型的一部分。但在每秒4帧图形状况下,跳投动画仅用4帧图形是不可能的,试了一下,每秒4帧,感觉要用12帧图形,即跳投从起跳到再落下需用12*\0.25秒=3秒工夫,才有称心的动画成果。跳投的12个图形有有点对付事,但还是能看到动画成果的,如把12个图形认真画一下,可能成果更好。留神,跳投的第4、5和6帧中图形是有篮球的,后边帧无篮球,在适当时候(本例在第8帧),篮球角色将呈现在投篮者上方,以此为终点向篮板静止,在第13帧碰到篮板,第14和15帧球向下静止,如投中,间接掉到蓝中,如未投中,从侧方向下掉。篮球只有一个图形。
首先要解决的问题是那种状况投篮能中,那种状况投篮不中。网上投篮游戏投中规定形形色色。本游戏规则是:间隔篮筐越远投篮越不准,在某一点投篮,那次投中,那次投不中无规律,或者说是随机的,但投中概率是定值。设从投篮点到篮筐间隔为L,令n=(L//100)取整数,应用随机数发生器产生1到n+1之间随机整数。规定随机数为1,投中,其它随机整数投不中。如点到篮筐架间隔Y<100,n=0,投中率为100%;如200>L>99,n=1,投中率为50%;如300>L>199,n=2,投中率为33%,等等。
防守者只有2个黄色造型,仿佛少了点,在本例勉强够用。当防守者和投篮手间隔大于200,防守者退到防守初始地位。小于200,而且投篮手正在运球,防守者迫近防守,即防守者面向投篮手挪动若干间隔(应为整数),如碰到投篮手,游戏完结。所谓面向,即防守者沿防守者和投篮手连线挪动。这样,这个连线L,以及防守者和投篮手两点沿x、y轴的差值dx和dy组成一个直角三角形。如防守者每帧沿x轴挪动dx1,沿y轴挪动dy1,只有dx1/dy1=dx/dy,就保障防守者面向投篮手挪动。可令dy1=7,则dx1=7dx/dy。但dy为0,要产生被0除谬误。为防止此种状况,可先判断dx和dy大小,如dx<dy,采纳上式,否则,dx1=7,dy1=7dy/dx。留神dx和dy是不可能同时为0的,因同时为0前,投篮手和防守者就会靠近,产生碰撞,导致游戏完结。这样做,不同方向挪动的间隔可能不同,上例中,最大值为两直角边都为7,间隔为9.899,只能取整数为:9,如较小直角边为0,则间隔为7。另外,沿x、y轴增量都只能是整数,因而方向也有误差。
以下是游戏全副程序,做了具体注解,应该较容易读懂程序。程度无限,未免有考虑不周之处,欢送批评指正。仅拷贝源程序不能正确运行,须要制作投篮手和防守者全副造型,球造型,背景篮球场,放到源程序所在文件夹中。将把源程序和所有图像打包上传,有须要的读者可下载。玩网络游戏时,因为投篮者随鼠标挪动,在关上程序或重玩游戏时,必须把鼠标移到程序窗口外边界,防止程序一开始就和防守者产生碰撞,导致游戏完结。

import pygameimport mathimport randomimport osclass 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+=1class 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=0class 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=-10pygame.init()os.environ['SDL_VIDEO_WINDOW_POS']="%d,%d"%(200,40) #游戏窗口距左侧和顶部点数为200,40size = 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()                        #创立管制频率的clockfps = 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()