PlayTheBall游戏制作二

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 = 0

		elif self.rect.right < 0:
			self.rect.left = self.width

		elif self.rect.top > self.height:
			self.rect.bottom = 0

		elif 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 pygame
import sys
import math
from 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 = position
		self.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 = 0

		elif self.rect.right < 0:
			self.rect.left = self.width

		elif self.rect.top > self.height:
			self.rect.bottom = 0

		elif 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_balls

def main():

	# 初始化pygame
	pygame.init()

	# 设置窗口大小
	bg_size = width, height = 1024, 681
	screen = 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")

	# 定义循环标志s
	running = 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 pygame
import sys
from 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 = position
		self.width, self.height = bg_size[0], bg_size[1]
		self.radius = self.rect.width / 2
		self.speed = speed

	# 定义一个move方法
	def move(self):
		self.rect = self.rect.move(self.speed)

		if self.rect.left > self.width:
			self.rect.right = 0

		elif self.rect.right < 0:
			self.rect.left = self.width

		elif self.rect.top > self.height:
			self.rect.bottom = 0

		elif self.rect.bottom < 0:
			self.rect.top = self.height


def main():

	# 初始化pygame
	pygame.init()

	# 设置窗口大小
	bg_size = width, height = 1024, 681
	screen = 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")

	# 定义循环标志s
	running = 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)





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零涂

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值