如何在pygame中使球从三角形反弹?

2024-06-18

你好,我是一名相当新的程序员,我正在尝试让球从 45 度三角形反弹。这是我的代码:

该程序使球在撞击窗户两侧时弹起,但我不知道如何使其从三角形弹起。

import pygame # importing the pygame
import sys # importing the system libraries
import time # importing timer
import random
from pygame.locals import * # importing the locals functions from the pygame library set
pygame.init() # the function from pygame that initializes all relevant variable

# setting length and width
width = 500
length = 300

# colour variables
WHITE = (255,255,255)
BLUE = (0,0,255)

# importing ball image
ball = pygame.image.load('ball.png')
ballRect = ball.get_rect()
ballRect.left = 300 
ballRect.right = 300

# setting speed
x_speed = 2
y_speed = 2

# setting window size
WINDOW = pygame.display.set_mode((width, length))# setting the size of the window
pygame.display.update()

# loop
while True:
 for event in pygame.event.get():
     if event.type == QUIT:
         pygame.quit()
         sys.exit()

 ballRect = ballRect.move(x_speed,y_speed)
 WINDOW.fill(WHITE) # changing screen colour to white
 WINDOW.blit(ball,ballRect) # printing the ball to screen
 pygame.display.update()
 pygame.display.flip()
 time.sleep(0.002) # to slow down the speed of bouncing
 pygame.display.update()

 # if the left side of ballRect is in a position less than 0, or the right side of ballRect is greater than 500
 if ballRect.left < 0 or ballRect.right > (width):
     x_speed = x_speed * -1

 # if the top of ballRect is in a position less than 0, or the bottom of ballRect is greater than the length
 elif ballRect.top < 0 or ballRect.bottom > (length):
     y_speed = y_speed * -1

 pygame.display.update()

我没有在三角形中画画,因为我不知道在哪里画,但我希望球会像撞到窗户两侧时一样从三角形上弹开。任何帮助都会很棒!


有趣的任务。三角形可以通过一个简单的列表来定义:

triangle = [(250, 220), (400, 300), (100, 300)]

三角形可以通过以下方式绘制pygame.draw.polygon() https://www.pygame.org/docs/ref/draw.html#pygame.draw.polygon

pygame.draw.polygon(WINDOW, RED, triangle, 0)

Use pygame.math.Vector2 https://www.pygame.org/docs/ref/math.html#pygame.math.Vector2定义球的位置和运动矢量:

ballvec = pygame.math.Vector2(1, 1)
ballpos = pygame.math.Vector2(150, 250)
balldiameter = 64

创建一个函数来进行碰撞检测。该函数必须检测球是否击中一条线。如果击中该线,则球的运动矢量会反映在线上。
该线由 2 个点 (lp0, lp1), 哪个是pygame.math.Vector2 https://www.pygame.org/docs/ref/math.html#pygame.math.Vector2对象。
球的位置(pt)和运动矢量(dir) are pygame.math.Vector2 https://www.pygame.org/docs/ref/math.html#pygame.math.Vector2对象也是:

def isect(lp0, lp1, pt, dir, radius):
    # direction vector of the line
    l_dir = (lp1 - lp0).normalize()
    # normal vector to the line
    nv = pygame.math.Vector2(-l_dir[1], l_dir[0])
    # distance to line
    d = (lp0-pt).dot(nv)
    # intersection point on endless line
    ptX = pt + nv * d
    # test if the ball hits the line
    if abs(d) > radius or dir.dot(ptX-pt) <= 0:
        return dir
    if (ptX-lp0).dot(l_dir) < 0 or (ptX-lp1).dot(l_dir) > 0:
        return dir 
    # reflect the direction vector on the line (like a billiard ball)
    r_dir = dir.reflect(nv)
    return r_dir

将窗口矩形和三角形附加到行列表中。每行由 2 的元组表示pygame.math.Vector2 https://www.pygame.org/docs/ref/math.html#pygame.math.Vector2对象:

# add screen rect
screen_rect = [(0, 0), (0, 300), (500, 300), (500, 0)]
for i in range(len(screen_rect)):
    p0, p1 = screen_rect[i], screen_rect[(i+1) % len(screen_rect)]
    line_list.append((pygame.math.Vector2(p0[0], p0[1]), pygame.math.Vector2(p1[0], p1[1])))

# add red trianlge
triangle = [(250, 220), (400, 300), (100, 300)]
for i in range(len(triangle)):
    p0, p1 = triangle[i], triangle[(i+1) % len(triangle)]
    line_list.append((pygame.math.Vector2(p0[0], p0[1]), pygame.math.Vector2(p1[0], p1[1])))

在遍历线路的循环​​中进行碰撞检测。如果球击中一条线,则运动矢量将被反射的运动矢量替换:

for line in line_list:
    ballvec = isect(*line, ballpos, ballvec, balldiameter/2)

最后更新球的位置和球矩形:

ballpos = ballpos + ballvec
ballRect.x, ballRect.y = ballpos[0]-ballRect.width/2, ballpos[1]-ballRect.height/2

请参阅示例代码,其中我将建议的更改应用于您的原始代码。我的球图像的尺寸为 64x64。球直径必须设置为该尺寸(balldiameter = 64):


最小的例子

import pygame

pygame.init()
window = pygame.display.set_mode((500, 300))

try:
    ball = pygame.image.load("Ball64.png")
except:
    ball = pygame.Surface((64, 64), pygame.SRCALPHA)
    pygame.draw.circle(ball, (255, 255, 0), (32, 32), 32)
ballvec = pygame.math.Vector2(1.5, 1.5)
ballpos = pygame.math.Vector2(150, 250)
balldiameter = ball.get_width()

def reflect_circle_on_line(lp0, lp1, pt, dir, radius):
    l_dir = (lp1 - lp0).normalize()                 # direction vector of the line
    nv = pygame.math.Vector2(-l_dir[1], l_dir[0])   # normal vector to the line
    d = (lp0-pt).dot(nv)                            # distance to line
    ptX = pt + nv * d                               # intersection point on endless line
    if (abs(d) > radius or dir.dot(ptX-pt) <= 0 or  # test if the ball hits the line   
        (ptX-lp0).dot(l_dir) < 0 or (ptX-lp1).dot(l_dir) > 0):
        return dir 
    r_dir = dir.reflect(nv)                         # reflect the direction vector on the line (like a billiard ball)                       
    return r_dir
      
triangle1 = [(250, 220), (400, 300), (100, 300)]
triangle2 = [(250, 80), (400, 0), (100, 0)]
screen_rect = [(0, 0), (0, window.get_height()), window.get_size(), (window.get_width(), 0)]

line_list = []
for p0, p1 in zip(triangle1, triangle1[1:] + triangle1[:1]):
    line_list.append((pygame.math.Vector2(p0), pygame.math.Vector2(p1)))
for p0, p1 in zip(triangle2, triangle2[1:] + triangle2[:1]):
    line_list.append((pygame.math.Vector2(p0), pygame.math.Vector2(p1)))
for p0, p1 in zip(screen_rect, screen_rect[1:] + screen_rect[:1]):
    line_list.append((pygame.math.Vector2(p0), pygame.math.Vector2(p1)))

clock = pygame.time.Clock()
run = True
while run:
    clock.tick(250)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    for line in line_list:
        ballvec = reflect_circle_on_line(*line, ballpos, ballvec, balldiameter/2)
    ballpos = ballpos + ballvec
    
    window.fill((64, 64, 64))
    pygame.draw.polygon(window, (255, 0, 0), triangle1, 0)
    pygame.draw.polygon(window, (0, 0, 255), triangle2, 0)
    window.blit(ball, (round(ballpos[0]-balldiameter/2), round(ballpos[1]-balldiameter/2)))
    pygame.display.flip()

pygame.quit()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在pygame中使球从三角形反弹? 的相关文章

随机推荐