Как заставить шар отскочить от треугольника в пигме? - PullRequest
0 голосов
/ 18 января 2019

Здравствуйте, я довольно новый программист и пытаюсь заставить мяч отскочить от треугольника 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()

Я не рисовал в треугольнике, потому что я не знаю, куда, но я ожидаю, что шар отскочит от треугольника, как это происходит, когда он касается боковых сторон окна. Любая помощь будет отличной!

1 Ответ

0 голосов
/ 20 января 2019

Интересное задание. Треугольник можно определить простым списком:

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

Треугольник можно нарисовать с помощью pygame.draw.polygon()

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

Используйте pygame.math.Vector2, чтобы определить положение и вектор движения мяча:

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

Создать функцию, которая выполняет обнаружение столкновений. Функция должна определять, попадает ли мяч в линию. Если линия нажата, то вектор движения мяча отражается на линии.
Линия представлена ​​двумя точками (lp0, lp1), которые являются pygame.math.Vector2 объектами.
Положение шара (pt) и вектор движения (dir) также являются 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

Добавьте прямоугольник окна и треугольник к списку линий. Линия Ech представлена ​​кортежем из 2 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

См. Пример кода, где я применил предложенные изменения к вашему исходному коду. Изображение моего мяча имеет размер 64х64. Диаметр шарика должен быть установлен на этот размер (balldiameter = 64):

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)
RED = (255,0,0)
GRAY = (128,128,128)

# importing ball image
ball = pygame.image.load("ball.png")
ballRect = ball.get_rect()

# setting ball data
ballvec = pygame.math.Vector2(1.5, 1.5)
ballpos = pygame.math.Vector2(150, 250)
balldiameter = 64

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

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

line_list = []

# 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])))

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

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

    WINDOW.fill(GRAY)
    pygame.draw.polygon(WINDOW, RED, triangle, 0)
    pygame.draw.polygon(WINDOW, BLUE, triangle2, 0)
    WINDOW.blit(ball,ballRect)

    pygame.display.update()
    pygame.display.flip()
    time.sleep(0.002) # to slow down the speed of bouncing
    pygame.display.update()

    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

    pygame.display.update()
...