向量使用1:pygame编写篮球游戏-火柴人运球避开防守跳起投篮(向量法处理防守者逼近投篮者前进数据)
Posted geng_zhaoying
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了向量使用1:pygame编写篮球游戏-火柴人运球避开防守跳起投篮(向量法处理防守者逼近投篮者前进数据)相关的知识,希望对你有一定的参考价值。
编写“火柴人运球避开防守跳起投篮”游戏时,未系统学习pygame,边看边编程,并不知道pygame中有向量。游戏中投篮者靠近篮板投篮,防守者逼近防守,向投篮者方向移动若干距离。原计算前进距离的代码没使用向量,因此逻辑关系较复杂,精度差,还花费较多时间。后发现pygame中有向量,改为向量法实现,可简化编程,提高了精度。程序中篮球从投篮处向篮板前进代码,也可使用向量,但改后和原代码没有优势,因此未改。向量是处理这类问题常用的方法,是编写游戏必须掌握的知识。读懂下边程序要有向量概念,可参见向量入门网页
https://blog.csdn.net/ttm2d/article/details/107798949。
文章中特别强调:关于向量的一个要点是,它们仅表示相对方向和大小,一个向量的位置是没有意义的。这是指数学意义上的向量。但实际上,在程序中处理的向量是在游戏坐标系中的有向线段,位置还是必要的。可以这样理解,有向线段是指定了位置的向量,因此,向量的许多概念和方法也适用于有向线段。另外,请仔细学习向量差的物理意义。
Guard类的方法draw(self)中被改写代码如下。请注意是投篮者向量减防守者向量,从而产生从防守者到投篮者的向量,防守者从自己所在位置出发沿向量方向逼近投篮者。这里防守者处是运动的出发点,投篮者处是目的地,为方便使用请记住:出发点到目的点向量,是目的地减出发点。很多游戏要用到这些概念,例如,投篮、踢球和射击等。
vGuard=pygame.math.Vector2(self.x,self.y) #防守者向量,参数是防守者坐标
vPlayer=pygame.math.Vector2(self.PlayerX,self.PlayerY) #投篮者向量,参数是投篮者坐标
vG_P=vPlayer-vGuard #注意投篮者向量减防守者向量,产生从防守者到投篮者的向量
dist=vG_P.length() #防守者到投篮者距离
#vG_P=vG_P.normalize() #可先变单位向量
#vG_P=vG_P*5 #向量长度变为5
vG_P.scale_to_length(5) #也可直接将向量长度变为5,这是防守者向投篮者方向1帧前进距离
if dist>200: #如距投篮者>200,返回初始点
self.x,self.y=400,300
elif self.PlayerFrameNum<4: #如投篮者未投篮,逼近投篮者
self.x+=int(vG_P.x) #防守者移动
self.y+=int(vG_P.y)
完整程序如下。所需图形和原来程序相同。
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)
vGuard=pygame.math.Vector2(self.x,self.y) #防守者向量,参数是防守者坐标
vPlayer=pygame.math.Vector2(self.PlayerX,self.PlayerY) #投篮者向量,参数是投篮者坐标
vG_P=vPlayer-vGuard #注意投篮者向量减防守者向量,产生从防守者到投篮者的向量
dist=vG_P.length() #防守者到投篮者距离
#vG_P=vG_P.normalize() #可先变单位向量
#vG_P=vG_P*5 #向量长度变为5
vG_P.scale_to_length(5) #也可直接将向量长度变为5,这是防守者向投篮者方向1帧前进距离
if dist>200: #如距投篮者>200,返回初始点
self.x,self.y=400,300
elif self.PlayerFrameNum<4: #如投篮者未投篮,逼近投篮者,如投篮者投篮,防守者位置不变
self.x+=int(vG_P.x) #防守者移动
self.y+=int(vG_P.y)
#下句返回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
player.mouseX,player.mouseY=pygame.mouse.get_pos()
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()
以上是关于向量使用1:pygame编写篮球游戏-火柴人运球避开防守跳起投篮(向量法处理防守者逼近投篮者前进数据)的主要内容,如果未能解决你的问题,请参考以下文章
向量使用1:pygame编写篮球游戏-火柴人运球避开防守跳起投篮(向量法处理防守者逼近投篮者前进数据)