下面是我写的一段小游戏,但是有两个问题我没有达到预期
1 是判断小球与挡板撞击时有Bug,当与挡板侧面撞击时,球不是反弹而是不规则移动
2 希望设计关卡,当撞击挡板次数达到要求,游戏自动进入下一关,但是没有实现
请学霸们帮我修正下这段程序
#反弹球游戏设计
from tkinter import *
import time
import random
top=Tk()
top.title("反弹球游戏")
cvsWidth=600
cvsHeight=600
var=StringVar()
Label(top,textvariable=var).pack()
canvas=Canvas(top,width=cvsWidth,height=cvsHeight,bg="lightgreen")
canvas.pack()
BoardWidth=80
BoardHeight=10
class Board():
def __init__(self):
self.canvas=canvas
self.bdwidth=BoardWidth
self.bdheight=BoardHeight
self.stepx=3
self.Board=self.canvas.create_rectangle(cvsWidth//2-self.bdwidth//2,\
cvsHeight-40-self.bdheight//2,\
cvsWidth//2+self.bdwidth//2,\
cvsHeight-40+self.bdheight//2,fill="black")
self.canvas.bind_all("<KeyPress-Left>",self.__moveLeft)
self.canvas.bind_all("<KeyPress-Right>",self.__moveRight)
def __moveLeft(self,event):
self.canvas.move(self.Board,-self.stepx,0)
if self.canvas.coords(self.Board)[0]<=0:
self.canvas.moveto(self.Board,0,cvsHeight-40-self.bdheight//2)
def __moveRight(self,event):
self.canvas.move(self.Board,self.stepx,0)
if self.canvas.coords(self.Board)[2]>=cvsWidth:
self.canvas.moveto(self.Board,cvsWidth-self.bdwidth,cvsHeight-40-self.bdheight//2)
playBoard=Board()
class Ball():
def __init__(self,r,board):
self.hitCount=0
self.canvas=canvas
self.r=r
self.board=board
self.vx=random.choice([-4,-3,-2,-1,1,2,3,4])
self.vy=3
self.hitBottom=False
self.ball=self.canvas.create_oval(cvsWidth//2-self.r,50-self.r,\
cvsWidth//2+self.r,50+self.r,fill="red")
def __isHitBottom(self):
if self.canvas.coords(self.ball)[3]>=cvsHeight:
self.hitBottom=True
def getHitCount(self):
return self.hitCount
def __hitWall(self):
if self.canvas.coords(self.ball)[0]<=0:
self.vx=-self.vx
if self.canvas.coords(self.ball)[2]>=cvsWidth:
self.vx=-self.vx
if self.canvas.coords(self.ball)[1]<=0:
self.vy=-self.vy
if self.__isHitBoard():
self.vy=-self.vy
def __isHitBoard(self):
if self.canvas.coords(self.ball)[0]+self.r>=self.canvas.coords(self.board.Board)[0] \
and self.canvas.coords(self.ball)[0]+self.r<=self.canvas.coords(self.board.Board)[2]:
if self.canvas.coords(self.ball)[3]>=self.canvas.coords(self.board.Board)[1] \
and self.canvas.coords(self.ball)[3]<=self.canvas.coords(self.board.Board)[3]:
self.hitCount+=1
var.set(f"Win:{self.hitCount}")
return True
return False
def ballMove(self):
while not self.hitBottom:
self.canvas.move(self.ball,self.vx,self.vy)
self.__hitWall()
self.__isHitBottom()
top.update()
time.sleep(0.03)
第几关=1
iscontinue=True
while iscontinue:
Ball.hitCount=0
playBall=Ball(10,playBoard)
playBall.ballMove()
if playBall.hitBottom:
canvas.delete(playBall.ball)
print("Game Over")
iscontinue=False
if playBall.getHitCount()>=1+(第几关-1)*1:
print(f"通过第{第几关}关")
canvas.delete(playBall.ball)
continue
第几关+=1
top.mainloop()
第一步:查看错误页面
第二步:看错误的行号
第三步:根据具体的错误,具体分析
我可以尝试解决这个问题。
第一个问题,即小球与挡板侧面撞击时不规则移动,可以通过修改小球碰到挡板侧面时的反弹方式解决。具体来说,可以通过判断小球与挡板碰撞时的相对位置,来决定小球的反弹方式。如果小球在挡板的左边或右边撞上,应该让小球在水平方向上反弹,即x方向的速度取相反数,y方向速度不变;如果小球在挡板的上方或下方撞上,则应该让小球在垂直方向上反弹,即y方向速度取相反数,x方向速度不变。
代码如下:
if ball.rect.right >= paddle.rect.left and ball.rect.left <= paddle.rect.right:
# 球与挡板左右边缘相交,进行水平反弹
ball.y_speed = -ball.y_speed
if ball.rect.centerx < paddle.rect.centerx:
ball.x_speed = -abs(ball.x_speed)
else:
ball.x_speed = abs(ball.x_speed)
elif ball.rect.bottom >= paddle.rect.top and ball.rect.top <= paddle.rect.bottom:
# 球与挡板上下边缘相交,进行垂直反弹
ball.x_speed = -ball.x_speed
第二个问题,即如何实现自动进入下一关,可以通过设立计数器,统计小球与挡板撞击的次数,从而确定是否进入下一关。具体来说,当小球与挡板撞击时,计数器加1;当计数器的值达到某个阈值时,即小球与挡板撞击的次数达到规定的次数时,自动进入下一关。
代码如下:
# 在主循环里添加以下代码
if pygame.sprite.collide_rect(ball, paddle):
# 球与挡板相撞,计数器加1
hit_count += 1
if hit_count >= MAX_HIT:
# 进入下一关
level += 1
hit_count = 0
reset_ball_and_paddle()
其中,MAX_HIT为小球与挡板碰撞的次数阈值,level为当前游戏的关卡数,reset_ball_and_paddle()函数用于初始化小球和挡板的位置和速度。
完整代码如下:
import pygame
import random
# 常量定义
SCREEN_SIZE = (640, 480)
BALL_SIZE = (20, 20)
PADDLE_SIZE = (100, 20)
BALL_SPEED = [5, 5]
PADDLE_SPEED = 10
MAX_HIT = 5 # 撞击阈值,达到此值自动进入下一关
FONT_SIZE = 32
FONT_COLOR = (255, 255, 255)
# 初始化游戏
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption("Paddle Ball Game")
font = pygame.font.Font(None, FONT_SIZE)
# 定义小球类
class Ball(pygame.sprite.Sprite):
def __init__(self, x, y, x_speed, y_speed, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill((255, 255, 255))
pygame.draw.ellipse(self.image, (255, 0, 0), [0, 0, width, height])
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = y
self.x_speed = x_speed
self.y_speed = y_speed
def update(self):
self.rect.move_ip(self.x_speed, self.y_speed)
if self.rect.left < 0 or self.rect.right > SCREEN_SIZE[0]:
self.x_speed = -self.x_speed
if self.rect.top < 0:
self.y_speed = -self.y_speed
if self.rect.bottom >= SCREEN_SIZE[1]:
return True
return False
def reset(self, x, y, x_speed, y_speed):
self.rect.centerx = x
self.rect.centery = y
self.x_speed = x_speed
self.y_speed = y_speed
# 定义挡板类
class Paddle(pygame.sprite.Sprite):
def __init__(self, x, y, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill((255, 255, 255))
pygame.draw.rect(self.image, (0, 0, 255), [0, 0, width, height])
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = y
def update(self):
pass
def reset(self, x, y):
self.rect.centerx = x
self.rect.centery = y
# 定义游戏初始化函数
def init_game():
global ball, paddle, level, hit_count
ball = Ball(SCREEN_SIZE[0] // 2, SCREEN_SIZE[1] // 2, *BALL_SPEED, *BALL_SIZE)
paddle = Paddle(SCREEN_SIZE[0] // 2, SCREEN_SIZE[1] - PADDLE_SIZE[1] - 10, *PADDLE_SIZE)
level = 1
hit_count = 0
# 定义重新开始游戏的函数
def restart_game():
global ball, paddle, level, hit_count
init_game()
ball.reset(SCREEN_SIZE[0] // 2, SCREEN_SIZE[1] // 2, *BALL_SPEED)
paddle.reset(SCREEN_SIZE[0] // 2, SCREEN_SIZE[1] - PADDLE_SIZE[1] - 10)
level = 1
hit_count = 0
# 定义重置小球和挡板的函数
def reset_ball_and_paddle():
global ball, paddle
ball.reset(SCREEN_SIZE[0] // 2, SCREEN_SIZE[1] // 2, *BALL_SPEED)
paddle.reset(SCREEN_SIZE[0] // 2, SCREEN_SIZE[1] - PADDLE_SIZE[1] - 10)
# 定义主循环
def main_loop():
global level, hit_count
clock = pygame.time.Clock()
running = True
while running:
# 处理事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
restart_game()
# 绘制画面
screen.fill((0, 0, 0))
screen.blit(font.render("Level: {}".format(level), True, FONT_COLOR), (10, 10))
screen.blit(font.render("Hits: {}".format(hit_count), True, FONT_COLOR), (10, 40))
ball.update()
paddle.update()
screen.blit(ball.image, ball.rect)
screen.blit(paddle.image, paddle.rect)
# 碰撞检测
if pygame.sprite.collide_rect(ball, paddle):
# 球与挡板相撞,计数器加1
hit_count += 1
if hit_count >= MAX_HIT:
# 进入下一关
level += 1
hit_count = 0
reset_ball_and_paddle()
# 更新画面
pygame.display.flip()
clock.tick(60)
if __name__ == "__main__":
init_game()
main_loop()
pygame.quit()
注意:上述代码只是对小球与挡板的碰撞进行了检测,如果游戏中还有其他需要碰撞检测的对象,需要自行添加碰撞检测的代码。
1.仔细检查__isHitBoard里的逻辑,反正我是没怎么看懂你if里到底在判断什么
2.hitCount只是在自加,根本没有任何地方去判断它的大小呀
# 反弹球游戏设计
from tkinter import *
import time
import random
top = Tk()
top.title("反弹球游戏")
cvsWidth = 600
cvsHeight = 600
var = StringVar()
Label(top, textvariable=var).pack()
canvas = Canvas(top, width=cvsWidth, height=cvsHeight, bg="lightgreen")
canvas.pack()
BoardWidth = 80
BoardHeight = 10
class Board():
def __init__(self):
self.canvas = canvas
self.bdwidth = BoardWidth
self.bdheight = BoardHeight
self.stepx = 3
self.Board = self.canvas.create_rectangle(
cvsWidth//2-self.bdwidth//2,
cvsHeight-40-self.bdheight//2,
cvsWidth//2+self.bdwidth//2,
cvsHeight-40+self.bdheight//2,
fill="black"
)
self.canvas.bind_all("<KeyPress-Left>", self.__moveLeft)
self.canvas.bind_all("<KeyPress-Right>", self.__moveRight)
def __moveLeft(self, event):
self.canvas.move(self.Board, -self.stepx, 0)
if self.canvas.coords(self.Board)[0] <= 0:
self.canvas.moveto(self.Board, 0, cvsHeight-40-self.bdheight//2)
def __moveRight(self, event):
self.canvas.move(self.Board, self.stepx, 0)
if self.canvas.coords(self.Board)[2] >= cvsWidth:
self.canvas.moveto(self.Board, cvsWidth-self.bdwidth, cvsHeight-40-self.bdheight//2)
playBoard = Board()
class Ball():
def __init__(self, r, board):
self.hitCount = 0
self.canvas = canvas
self.r = r
self.board = board
self.vx = random.choice([-4, -3, -2, -1, 1, 2, 3, 4])
self.vy = 3
self.hitBottom = False
self.ball = self.canvas.create_oval(
cvsWidth//2-self.r, 50-self.r,
cvsWidth//2+self.r, 50+self.r,
fill="red"
)
def __isHitBottom(self):
if self.canvas.coords(self.ball)[3] >= cvsHeight:
self.hitBottom = True
def __hitWall(self):
if self.canvas.coords(self.ball)[0] <= 0 or self.canvas.coords(self.ball)[2] >= cvsWidth:
self.vx = -self.vx
if self.canvas.coords(self.ball)[1] <= 0:
self.vy = -self.vy
if self.__isHitBoard():
self.vy = -self.vy
def __isHitBoard(self):
ball_coords = self.canvas.coords(self.ball)
board_coords = self.canvas.coords(self.board.Board)
if (
ball_coords[2] >= board_coords[0] and
ball_coords[0] <= board_coords[2] and
ball_coords[3] >= board_coords[1] and
ball_coords[3] <= board_coords[3]
):
self.hitCount += 1
var.set(f"Win:{self.hitCount}")
return True
return False
def ballMove(self):
while not self.hitBottom:
self.canvas.move(self.ball, self.vx, self.vy)
self.__hitWall()
self.__isHitBottom()
top.update()
time.sleep(0.03)
def start_next_level():
global playBall, 第几关
canvas.delete(playBall.ball)
第几关 += 1
playBall = Ball(10, playBoard)
playBall.ballMove()
if playBall.hitBottom:
canvas.delete(playBall.ball)
print("Game Over")
第几关 = 1
iscontinue = True
playBall = None
while iscontinue:
playBall = Ball(10, playBoard)
playBall.hitCount = 0
playBall.ballMove()
if playBall.hitBottom:
canvas.delete(playBall.ball)
print("Game Over")
iscontinue = False
if playBall.getHitCount() >= 1 + (第几关-1) * 1:
print(f"通过第{第几关}关")
start_next_level()
top.mainloop()