PlayTheBall游戏制作二

来源:互联网 发布:js中时间格式化字符串 编辑:程序博客网 时间:2024/09/21 06:33

PlayTheBall游戏制作二

经过一天的学习,增加了球能移动功能。能达到按指定的速度和方向移动,并且当球出边框后,在相对的方向移动进入游戏界面,当在界面中有两个球相互撞击时,两球会按照相反的方向移动。移动功能实现步骤: 能移动小球 -> 小球出边界时,处理小球从反方向进入界面 -> 碰撞检测及运动方向的改变
一, 移动小球及出边界判断,反向:在子类Ball中定义一个move方法,再调用rect对象中的move方法对小球进行移动。移动后对小球进行判断。代码如下:
# 定义一个move方法def move(self):self.rect = self.rect.move(self.speed)if self.rect.left > self.width:self.rect.right = 0elif self.rect.right < 0:self.rect.left = self.widthelif self.rect.top > self.height:self.rect.bottom = 0elif self.rect.bottom < 0:self.rect.top = self.height
二,碰撞检测
判断方法有两种,一种是利用两圆心距大于半径的方法,一种是利用pygame提供的库进行碰撞检测。
1.利用两圆心距大于半径的方法: 利用初中知识,两圆心之间的距离大于半径。利用python中自带的math库中的sqrt和pow函数进行开方和幂的运算。
实现算法如下:
先定义一个储存碰撞小球的列表,然后逐次将目标球对象与传入的列表中其他球对象的圆心利用pow和sqrt函数进行求距离运算,如果两圆心距大于半径,则将该目标球对象加入到新定义的列表中,并结束循环,跳出碰撞检测函数。代码如下:
# 定义碰撞检测方法def collide_check(item, target):# 定义碰撞的球的列表col_balls = []# 逐个寻找碰撞的球for each in target:# 计算两个球的距离distance = math.sqrt(\math.pow((item.rect.center[0] - each.rect.center[0]), 2) +math.pow((item.rect.center[1] - each.rect.center[1]), 2))# 两个球碰撞了if distance <= (item.rect.width + each.rect.width) / 2:col_balls.append(item)return col_balls
              然后在主函数中进行依次检测碰撞小球,在检测过程中一定要把目标对象利用pop方法从列表中删除,并且查找目标碰撞小球后,要把小球对象重新放回列表中。如果检测到小球确实碰撞了,则改变小球的运动方向,代码如下:
# 碰撞检测for i in range(BALL_NUM):# 在列表中去除目标本身item = balls.pop(i)# 检测目标是否碰撞if collide_check(item, balls):# 碰撞时改变运动方向item.speed[0] = - item.speed[0]item.speed[1] = - item.speed[1]# 在列表中加入目标balls.insert(i, item)
完整代码如下:
# -*- coding: utf-8 -*-# @Author: 四叶草# @Date:   2017-11-08 20:15:55# @Last Modified by:   Administrator# @Last Modified time: 2017-11-10 15:15:46# 导入相应的模块import pygameimport sysimport mathfrom pygame.locals import *from random import *# 定义一个继承Sprite类的球类class Ball(pygame.sprite.Sprite):def __init__(self, image, position, speed, bg_size):pygame.sprite.Sprite.__init__(self)self.image = pygame.image.load(image).convert_alpha()self.rect = self.image.get_rect()self.rect.left, self.rect.top = positionself.width, self.height = bg_size[0], bg_size[1]self.speed = speed# 定义一个move方法def move(self):self.rect = self.rect.move(self.speed)if self.rect.left > self.width:self.rect.right = 0elif self.rect.right < 0:self.rect.left = self.widthelif self.rect.top > self.height:self.rect.bottom = 0elif self.rect.bottom < 0:self.rect.top = self.height# 定义碰撞检测方法def collide_check(item, target):# 定义碰撞的球的列表col_balls = []# 逐个寻找碰撞的球for each in target:# 计算两个球的距离distance = math.sqrt(\math.pow((item.rect.center[0] - each.rect.center[0]), 2) +math.pow((item.rect.center[1] - each.rect.center[1]), 2))# 两个球碰撞了if distance <= (item.rect.width + each.rect.width) / 2:col_balls.append(item)return col_ballsdef main():# 初始化pygamepygame.init()# 设置窗口大小bg_size = width, height = 1024, 681screen = pygame.display.set_mode(bg_size)# 载入图片ball_image = "gray_ball.png"bg_image = "background.png"background = pygame.image.load(bg_image).convert_alpha()# 设置界面主题pygame.display.set_caption("Play The Ball - Four Leaf Clover")# 定义循环标志srunning = True# 定义球的链表balls = []# 创建球的个数BALL_NUM = 5# 创建频率对象clock = pygame.time.Clock()# 随机生成球的位置及速度,并调用Ball方法,添加各个球对象到链表中for i in range(5):position = randint(0, width - 100), randint(0, height - 100)speed = [randint(-10, 10), randint(-10, 10)]ball = Ball(ball_image, position, speed, bg_size)# 防止一开始生成球时。两球就碰撞在一起while collide_check(ball, balls):ball.rect.left, ball.rect.top = randint(0, width - 100), randint(0, height - 100)balls.append(ball)# 主循环while running:# 寻找关闭窗口事件for event in pygame.event.get():if event.type == QUIT:sys.exit()# 显示背景screen.blit(background, (0, 0))# 显示五个球for each in balls:each.move()screen.blit(each.image, each.rect)# 碰撞检测for i in range(BALL_NUM):# 在列表中去除目标本身item = balls.pop(i)# 检测目标是否碰撞if collide_check(item, balls):# 碰撞时改变运动方向item.speed[0] = - item.speed[0]item.speed[1] = - item.speed[1]# 在列表中加入目标balls.insert(i, item)# 刷新缓冲区图像pygame.display.flip()# 控制帧率为30帧clock.tick(60)# 在本文件中调用main函数if __name__ == "__main__":main()


2. 利用pygame提供的pygame.sprite.spritecollide(sprite, group, dokill, collided = None)返回Sprite_list方法,官方文档介绍:http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollide 其中sprit对象是待检测目标对象,group是sprit对象组,dokill值是bool类型,表示是否把与目标对象碰撞的对象从组中删除,若值为正,则删除对象,反之,不删除。collided参数表示是否注册回调函数,这个参数可以不需要,在需要的时候再调用回调函数。
算法实现:
先定义sprite组。利用sprite方法中的Group()方法,代码如下:group = pygame.sprite.Group()
然后再判断检测,记得检测前先把目标对象从列表中删除,检测完后在添加到组里面。代码如下:
# 检测两球是否碰撞for each in group:# 移除目标精灵group.remove(each)# 检测是否碰撞,碰撞了则改变运动方向if pygame.sprite.spritecollide(each, group, False):each.speed[0] = -each.speed[0]each.speed[1] = -each.speed[1]# 添加目标精灵到组中group.add(each)
在检测碰撞时发现一个问题,当两个小球未碰到一起时,小球就弹开了,去找原因,在于判断是否碰撞,是根据小球占的矩形框是否碰撞来检测的。因此对这个问题改进,利用sprite给定的方法来判断圆类碰撞的方法,利用pygame.sprite.collide_circle(left, right)方法,官方文档介绍:http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.collide_circle ,用来判断两个球精灵是否发生碰撞,前提是球对象要有半径属性。所以我们在定义球类的时候就应该定义半径属性,并且计算出来。上述代码改为如下:
# 检测两球是否碰撞for each in group:# 移除目标精灵group.remove(each)# 检测是否碰撞,碰撞了则改变运动方向if pygame.sprite.spritecollide(each, group, False, pygame.sprite.collide_circle):each.speed[0] = -each.speed[0]each.speed[1] = -each.speed[1]# 添加目标精灵到组中group.add(each)
完整代码:
# -*- coding: utf-8 -*-# @Author: 四叶草# @Date:   2017-11-08 20:15:55# @Last Modified by:   Administrator# @Last Modified time: 2017-11-10 20:33:45# 导入相应的模块import pygameimport sysfrom pygame.locals import *from random import *# 定义一个继承Sprite类的球类class Ball(pygame.sprite.Sprite):def __init__(self, image, position, speed, bg_size):pygame.sprite.Sprite.__init__(self)self.image = pygame.image.load(image).convert_alpha()self.rect = self.image.get_rect()self.rect.left, self.rect.top = positionself.width, self.height = bg_size[0], bg_size[1]self.radius = self.rect.width / 2self.speed = speed# 定义一个move方法def move(self):self.rect = self.rect.move(self.speed)if self.rect.left > self.width:self.rect.right = 0elif self.rect.right < 0:self.rect.left = self.widthelif self.rect.top > self.height:self.rect.bottom = 0elif self.rect.bottom < 0:self.rect.top = self.heightdef main():# 初始化pygamepygame.init()# 设置窗口大小bg_size = width, height = 1024, 681screen = pygame.display.set_mode(bg_size)# 载入图片ball_image = "gray_ball.png"bg_image = "background.png"background = pygame.image.load(bg_image).convert_alpha()# 设置界面主题pygame.display.set_caption("Play The Ball - Four Leaf Clover")# 定义循环标志srunning = True# 定义球的链表balls = []group = pygame.sprite.Group()# 创建频率对象clock = pygame.time.Clock()# 随机生成球的位置及速度,并调用Ball方法,添加各个球对象到链表中for i in range(5):position = randint(0, width - 100), randint(0, height - 100)speed = [randint(-10, 10), randint(-10, 10)]ball = Ball(ball_image, position, speed, bg_size)# 防止一生成球时两者就碰撞while pygame.sprite.spritecollide(ball, group, False, pygame.sprite.collide_circle):ball.rect.left, ball.rect.top = randint(0, width-100), randint(0, height-100)balls.append(ball)group.add(ball)# 主循环while running:# 寻找关闭窗口事件for event in pygame.event.get():if event.type == QUIT:sys.exit()# 显示背景screen.blit(background, (0, 0))# 显示五个球for each in balls:each.move()screen.blit(each.image, each.rect)# 检测两球是否碰撞for each in group:# 移除目标精灵group.remove(each)# 检测是否碰撞,碰撞了则改变运动方向if pygame.sprite.spritecollide(each, group, False, pygame.sprite.collide_circle):each.speed[0] = -each.speed[0]each.speed[1] = -each.speed[1]# 添加目标精灵到组中group.add(each)# 刷新缓冲区图像pygame.display.flip()# 控制帧率为30帧clock.tick(60)# 在本文件中调用main函数if __name__ == "__main__":main()
3.运行完代码后,发现一个问题,当初始化小球属性的时候,若两个小球已经碰撞了,会出现一直碰撞,分离不开的情况,原因是,当两个小球碰撞时,会发生运动方向改变,运动方向改变,每次只移动一个像素,不足以把两个小球分开,下次判断时又会认为是碰撞了,运动方向又发生改变,周而复始,一直脱离不开。解决算法,在产生小球对象时,对每个对象进行碰撞检测判断,如果碰撞了,则再次随机产生位置属性,直到两球不碰撞为止。实现代码如下:
# 防止一生成球时两者就碰撞while pygame.sprite.spritecollide(ball, group, False, pygame.sprite.collide_circle):ball.rect.left, ball.rect.top = randint(0, width-100), randint(0, height-100)
# 防止一开始生成球时。两球就碰撞在一起while collide_check(ball, balls):ball.rect.left, ball.rect.top = randint(0, width - 100), randint(0, height - 100)