Python 设计真实反弹球算法及原理分析 (使用物理定律)

文章简单地使用物理定律, 编写程序模拟真实世界中的碰撞。
在开始正式讲解之前, 先看这两个代码:

# 把球掉头
ball.speed[0] = -ball.speed[0]
ball.speed[1] = -ball.speed[1]

可以看到, 这个代码直接把球的速度反了一下, 比较粗糙。
这是提升的版本 (真实世界中两个球质量相同时,是这个样子):

# 这里用 speed表示速度
self.speed,other.speed = other.speed,self.speed # 把两个球进行速度交换

运行效果:
Python 设计真实反弹球算法及原理分析 (使用物理定律)_第1张图片

更精确的算法

前面两种算法, 都还不能很好地模拟真实世界中的碰撞。
由于我们模拟的是弹性碰撞, 碰撞前后两球的总动量守恒,总动能也守恒。
根据动量p=mv, 动能E=mv2 / 2,
可以推导出以下公式:(v1, v2是两小球的速度, m1,m2是质量, v1’ , v2’ 是碰撞后的速度)


根据这个公式, 就可以设计相应的程序。
使用pygame设计的完整代码如下:

import sys, pygame,math,time
from random import *

__version__='1.1'
class Ball(pygame.sprite.Sprite):
    def __init__(self, image, location, speed,mass=1):
        pygame.sprite.Sprite.__init__(self)
        self.image = image
        self.rect = self.image.get_rect()
        self.rect.left, self.rect.top = location
        self.pos = location
        self.speed = speed
        self.m = mass

    def move(self):
        self.pos = (self.pos[0]+self.speed[0],self.pos[1]+self.speed[1])
        self.rect.left,self.rect.top = self.pos

        # 检查有无撞到窗口
        if self.rect.left <= 0:
            self.speed[0] = abs(self.speed[0])
        elif self.rect.right >= width:
            self.speed[0] = -abs(self.speed[0])
        if self.rect.top <= 0:
            self.speed[1] = abs(self.speed[1])
        elif self.rect.bottom >= height:
            self.speed[1] = -abs(self.speed[1])
    def distance(self,other):
        dx=other.rect.left-self.rect.left
        dy=other.rect.top-self.rect.top
        return math.hypot(dx,dy)
    def collide(self,other):
        # 反弹球算法部分
        m1=self.m;m2=other.m
        dx1 = (m1-m2)/(m1+m2)*self.speed[0] + 2*m2/(m2+m1)*other.speed[0]
        dy1 = (m1-m2)/(m1+m2)*self.speed[1] + 2*m2/(m2+m1)*other.speed[1]
        dx2 = (m2-m1)/(m1+m2)*other.speed[0] + 2*m1/(m2+m1)*self.speed[0]
        dy2 = (m2-m1)/(m1+m2)*other.speed[1] + 2*m1/(m2+m1)*self.speed[1]
        self.speed=[dx1, dy1]
        other.speed=[dx2, dy2]

animate()函数更新一次小球在屏幕上的位置,
state用于记录球的碰撞信息, 避免球重复碰撞 (也可以使用更好的代码)。

state = {}
def animate(group):
    rect=pygame.rect.Rect((0,0),(width,height))
    screen.fill((255,255,255),rect)
    global ball
    for ball in group:
        ball.move()
    for i in range(len(group)):
        ball=group[i]
        for j in range(i):
            other=group[j]
            collided = ball.distance(other) < ball.rect.width
            # 避免球重复碰撞
            if i!=j:
                if collided and not state.get((i,j),0):
                    ball.collide(other)
                    state[(i,j)] = 1
                elif not collided:
                    state[(i,j)] = 0

        screen.blit(ball.image, ball.rect)
    pygame.display.flip()

使用pygame的主程序的实现:

size = width,height = 640, 480
screen = pygame.display.set_mode(size,pygame.RESIZABLE)
screen.fill((255, 255, 255))
image = pygame.image.load("beach_ball.png")
clock = pygame.time.Clock()
group = []
for row in range(3):
    for column in range(3):
        location = [column * 180 + 50, row * 120 + 50]
        speed = [randrange(-4,5), randrange(-4,5)]
        ball = Ball(image,location, speed)
        group.append(ball)

主事件循环, 每循环一次就调用一次(也可以是多次) animate()函数, 刷新一次小球位置:

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            frame_rate = clock.get_fps()
            print("frame rate = ", frame_rate)
            running = False
        elif event.type == pygame.VIDEORESIZE:
            width,height = event.size
            screen = pygame.display.set_mode(event.size, pygame.RESIZABLE)
    animate(group)
    clock.tick(60)
pygame.quit()

程序运行效果:
Python 设计真实反弹球算法及原理分析 (使用物理定律)_第2张图片

你可能感兴趣的:(Python,算法,python,物理学,游戏开发,pygame)