如何让Python模拟的物体实现弹跳而非仅自由下落?
质点弹跳的碰撞分辨率实现指导
初始下落质点实现代码
我尝试制作软体物体,先从实现下落质点入手,编写了以下Python代码:
import numpy as np import tkinter as tk import math root=tk.Tk() root.geometry("700x350") c=tk.Canvas(root, width=700, height=350, background="white") c.pack() class point(): def __init__(self,pos,vel): self.position=np.array(pos) self.velocity=np.array(vel) def show(self): x=self.position[0] y=self.position[1] c.delete("all") c.create_oval(x-4,y-4,x+4,y+4,fill='black') class runner(): def __init__(self,points,force): self.ps=points self.grav=force def update(self,dt): for p in self.ps: p.velocity=p.velocity+np.array(self.grav)*dt p.position=p.position+p.velocity*dt p.show() ps=[point([350,350//2],[-2,-2])] x=runner(ps,[0,0.1]) while True: x.update(1) root.update()
问题说明
我已经掌握Python中的碰撞检测,但对**collision resolution(碰撞分辨率)**的实现方式存疑,想请教如何让该质点触地后实现弹跳,明确其碰撞后的运动方向与受力计算方法。
尝试的碰撞实现代码
我尝试编写了一段代码,但仍需指导完善:
import numpy as np import tkinter as tk import math import warnings warnings.filterwarnings("error") root=tk.Tk() root.geometry("700x350") c=tk.Canvas(root, width=700, height=350, background="white") c.pack() class Collision: def __init__(self, normal=[0.0, 0.0], depth=float('-inf')): self.normal = np.array(normal) self.depth = np.clip(depth,-1000,100) def find_collision(position): return Collision([0.0, 1.0], -(300-position[1])) class point(): def __init__(self,pos,vel): self.position=np.array(pos) self.velocity=np.array(vel) def show(self): x=self.position[0] y=self.position[1] c.delete("all") c.create_oval(x-4,y-4,x+4,y+4,fill='black') class runner(): def __init__(self,points,force): self.ps=points self.grav=force self.friction=10 self.elasticity=1 def update(self,dt): for p in self.ps: p.velocity=p.velocity+np.array(self.grav)*dt c = find_collision(p.position) if c.depth>=0: try: p.position += c.normal * c.depth p.position=np.clip(p.position,-345,345) vn = c.normal * np.dot(c.normal, p.velocity) vt = p.velocity - vn vn = -self.elasticity * vn vt *= math.exp(-self.friction * dt) p.velocity = vn + vt except RuntimeWarning: print(c.normal,c.depth) p.position=p.position+p.velocity*dt p.show() ps=[point([350,350//2],[0,0])] x=runner(ps,[0,0.1]) while True: x.update(1) root.update()
代码优化指导
1. 碰撞检测逻辑修正
当前find_collision函数的深度计算未考虑质点半径,导致质点视觉上会穿入地面。Canvas高度为350,y轴向下,质点半径4像素,所以地面碰撞阈值应为350 - 4 = 346,修正后的函数:
def find_collision(position): # 地面位于y=346,避免质点视觉穿入地面 ground_y = 346 depth = ground_y - position[1] if depth < 0: # 未碰撞,返回无效碰撞 return Collision([0.0, 1.0], float('-inf')) return Collision([0.0, 1.0], depth)
2. 碰撞分辨率顺序修正
原代码的更新顺序错误,导致碰撞修正后的位置被速度更新覆盖。正确顺序应为:检测碰撞→修正位置和速度→应用重力→更新位置。修正后的update方法:
def update(self,dt): for p in self.ps: c = find_collision(p.position) if c.depth >= 0: # 1. 将质点移出碰撞区域,避免持续碰撞 p.position += c.normal * c.depth # 2. 分解速度为法向(垂直地面)和切向(平行地面)分量 vn = np.dot(c.normal, p.velocity) * c.normal vt = p.velocity - vn # 3. 法向弹跳:弹性系数反转法向速度,1为完全弹性,0为完全非弹性 vn = -self.elasticity * vn # 4. 切向摩擦:指数衰减切向速度,模拟地面摩擦 vt *= math.exp(-self.friction * dt) # 5. 合并速度分量 p.velocity = vn + vt # 6. 应用重力更新速度 p.velocity += np.array(self.grav) * dt # 7. 更新位置,并限制x轴范围避免移出画布 p.position += p.velocity * dt p.position[0] = np.clip(p.position[0], 4, 696) p.show()
3. 参数调整建议
elasticity(弹性系数):建议设为0.8,更符合现实中的弹跳效果(完全弹性1会导致质点永远弹跳)。friction(摩擦系数):建议设为3,避免切向速度衰减过快,导致质点很快停止横向运动。
4. 移除不必要的警告处理
逻辑修正后不会出现数值异常,可删除warnings.filterwarnings("error")及try-except块。
内容的提问来源于stack exchange,提问作者Ishwari Katariya
相关产品推荐
相关产品推荐

