资讯详情

Python 物理引擎pymunk最完整教程(下)

📅 2026/10/26 17:51:55 | 华诺云谱 👁 阅读
Python 物理引擎pymunk最完整教程(下)
上一篇文章Python 物理引擎pymunk最完整教程中总目录https://gitcode.com/qq_48979387/csdn-articles/blob/master/pymunk/README.md8 空间管理8.1 管理身体、形状、约束身体、形状、以及约束对象都要被添加到空间中才能起作用。添加的方法是Space.add方法。有一些功能只有在物体被加入空间后才能使用例如查询。space.add(body1,shape2,constraint1,constraint2,body2,...)身体、形状、约束被加入空间后以列表形式被储存在Space.bodies, shapes, constraints几个属性中。与add相反Space.remove方法可以用于删除被加入空间的身体、形状、或约束。但是需要注意如果要删除一个物体那么需要同时删除这个物体的Body, Shape, 以及附加的Constraint对象。只删除一个Body对象或是只删除一个Shape都无法彻底地删除这个物体。Space.remove方法主要用于删除“飞出边界”的物体。例如一个小球从一个斜坡上落下去如果不加以处理它会一直下坠而不会自动删除拖慢了程序的速度。forshapeinspace.shapes:# 遍历空间内所有形状ifshape.body.position.y...:# 如果飞出了边界space.remove(shape,shape.body)# 删除形状和对应身体8.2 static_bodySpace对象在创建时自带一个static_body属性表示一个静态的Body对象位于(0,0)。通常不需要直接将这个属性和Shape绑定也不需要将它添加到空间但是使用它能方便一些操作比如添加固定支点的约束。importpymunk spacepymunk.Space()space.gravity(0,500)bpymunk.Body()b.position(100,100)spymunk.Segment(b,(0,0),(50,50),5)s.mass1space.add(b,s,pymunk.PivotJoint(b,space.static_body,b.position))if__name____main__:importutil util.run(space)这段代码实现了一个钟摆的效果。前文已经介绍过PivotJoint创建一个单点作为约束点的关节使用世界坐标表示约束点。这段代码直接将static_body和线段的body进行了约束。8.3 复制和转储空间pymunk.Space对象可以被复制或用pickle转储。通过Space.copy方法可以复制Space对象得到一个新的空间。spacepymunk.Space()space2space.copy()Space也可以被pickle模块进行转储和载入。pickle模块可以直接将一些python对象转换为字符串一样的数据可以将pickle后的数据转换为python对象。关于pickle详细用法参见https://blog.csdn.net/Hardworking666/article/details/112754839importpymunkimportrandom# 随机数模块importpickle# python对象序列化与反序列化模块defadd_circle():bpymunk.Body()b.position(random.randint(100,900),100)# 随机位置spymunk.Circle(b,random.randint(1,50))# 创建半径随机大小的圆形s.mass1s.elasticity0.8# 设置弹性系数space.add(b,s)try:spacepickle.load(open(savefile.p,rb))# 载入空间以二进制形式读取exceptFileNotFoundError:# 没找到储存的空间spacepymunk.Space()# 创建新的空间space.gravity(0,500)bodypymunk.Body(body_typepymunk.Body.STATIC)# 创建静态地面body.position(0,550)shapepymunk.Segment(body,(0,10),(1000,10),3)shape.elasticity0.8space.add(body,shape)foriinrange(random.randint(2,6)):# 添加随机2-6个圆形add_circle()count0# 计数器defupdate():globalcount count1ifcount120:# 每过2秒保存一次pymunk空间count0pickle.dump(space,open(savefile.p,wb))# 以二进制形式将pymunk空间储存到文件中print(Saved!)if__name____main__:importutil util.run(space,update)这段代码首先在空间中添加随机数量和随机大小的几个圆形每过2s就会储存一次空间。打印出Saved!后重新运行程序发现运行结果和上一次储存时的空间内容一样说明pickle成功转储了空间。目录下有一个savefile.p文件表示的就是pickle储存空间的内容。8.4 Space对象属性参考classpymunk.Space(threaded: bool False)创建一个pymunk空间。所有身体、形状、约束都必须被添加到一个正确的空间中才能起作用。threaded表示是否使用线程来运行物理求解器这在空间数量较多时可能有用Windows系统不支持。空间可以被复制或用pickle转储。add(*objs: Body | Shape | Constraint) → None将身体、形状、约束加入空间add_collision_handler(collision_type_a: int, collision_type_b: int) → CollisionHandler添加碰撞处理器处理collison_type_a和collision_type_b发生的碰撞详见7.1add_default_collision_handler() → CollisionHandler添加默认碰撞处理器用于处理那些没有通过普通碰撞处理器和通配符碰撞处理器进行处理的碰撞详见7.5add_wildcard_collision_handler(collision_type_a: int) → CollisionHandler添加通配符碰撞处理器用于处理所有与collision_type_a发生的碰撞详见7.5add_post_step_callback(callback_function: Callable[[…], None], key: Hashable, *args: Any, **kwargs: Any) → bool注册一个回调函数将在下一次Space.step结束后调用。func(space : Space, key, *args, **kwargs)bb_query(bb: BB, shape_filter: ShapeFilter) → List[Shape]查询边界框附近的形状列表详见7.6propertybodies: List[Body]所有加入空间的身体对象propertyconstraints: List[Constraint]所有加入空间的约束对象propertyshapes: List[Shape]所有加入空间的形状对象propertycollision_bias: float决定空间以多快的速度将两个重叠的物体推开值在0-1之间但一般不需要改变这个值。pymunk允许快速移动的物体重叠然后随着时间的推移修复重叠把重叠的物体推开例如两个小球相撞小球的形状之间会发生重叠然后通过修复而弹开。默认值约为0.2%表示经过1s后剩余0.2%的重叠没有修复。propertycollision_persistence: float空间保持碰撞解决方案的帧数一般不用改变这个值。propertycollision_slop: float允许的形状之间的重叠量。为了提高稳定性将这个值设置得尽可能高就不会出现明显的重叠。默认值为0.1。copy() → T复制空间深层复制propertycurrent_time_step: float上一次调用或当前调用Space.step所指定的时间步长。propertydamping: float空间的阻尼系数详见2.5propertygravity: Vec2d空间的重力加速度详见2.5debug_draw(options: SpaceDebugDrawOptions) → None绘制当前pymunk空间中的内容options是绘制选项详见后文propertyidle_speed_threshold: float一个物体被认为是进入了“空闲状态”的速度。如果一个物体的速度很小基本可以被认为是静止了这个速度小于idle_speed_threshold就会认为这个物体进入了空闲状态。默认值为0表示根据重力加速度估计一个合适的空闲速度。“空闲”和“睡眠”不是一个概念空闲是指这个身体静止或几乎静止了而睡眠是指让这个物体的各属性不再刷新。propertyiterations: int物理求解器的迭代数量默认为10。这个值设置的越大物理效果越真实但是也会增加CPU占用。point_query(point: Tuple[float, float], max_distance: float, shape_filter: ShapeFilter) → List[PointQueryInfo]查询与point这个点距离为max_distance以内的所有形状包含传感器详见7.6point_query_nearest(point: Tuple[float, float], max_distance: float, shape_filter: ShapeFilter) → PointQueryInfo | None查询与point这个点距离为max_distance以内的最近的形状不包含传感器详见7.6reindex_shape(shape: Shape) → None当更改了某个身体、形状的一些属性如位置后如果立即进行形状查询可能不准。此时可以调用这个方法来刷新给定shape的碰撞信息来避免这一点。reindex_shapes_for_body(body: Body) → None刷新给定身体对应的所有形状的碰撞信息。reindex_static() → None刷新空间中静态形状的碰撞检测信息。只有在移动静态形状时才需要调用这个函数。remove(*objs: Body | Shape | Constraint) → None从空间中删除身体、形状、约束。segment_query(start: Tuple[float, float], end: Tuple[float, float], radius: float, shape_filter: ShapeFilter) → List[SegmentQueryInfo]沿着start到end以radius为线段半径查询与此线段相交的所有形状包含传感器详见7.6segment_query_first(start: Tuple[float, float], end: Tuple[float, float], radius: float, shape_filter: ShapeFilter) → SegmentQueryInfo | None沿着start到end以radius为线段半径查询与此线段相交的所有形状不包含传感器详见7.6shape_query(shape: Shape) → List[ShapeQueryInfo]查询与给定形状相交的所有形状包含传感器详见7.6。propertysleep_time_threshold: float物体进入空闲状态多少秒后进入睡眠状态。默认为float(“inf”)表示禁用睡眠。如果不设置这个值那么无法使用Body.sleep方法。详见3.3propertystatic_body: Body表示一个位于(0,0)的静态身体对象详见8.2。step(dt: float) → None使物理空间执行一段时间propertythreads: int如果创建空间时启用了线程则表示用于通过线程运行step的数量最大数量为2默认为1。Windows系统不支持use_spatial_hash(dim: float, count: int) → None将空间切换为使用空间散列而不是边界框树。dim是哈希单元的大小count是哈希表中建议的最小单元格数9 其他功能9.1 自动几何图形转换pymunk.autogeometry模块用于将图像类的信息转换成几何图形从而创建符合形状的多边形。pymunk.autogeometry.march_hard(bb:BB,x_samples:int,y_samples:int,threshold:float,sample_func:Callable[[Tuple[float,float]],float])→ PolylineSetautogeometry.march_hard方法对需要转换为几何图形的内容进行采样并返回多边形点集对象autogeometry.PolylineSet。首先需要提供几何图形的边界框作为参数然后决定x方向上的采样和y方向上的采样数量还需要提供threshold表示算法的容错率。采样时pymunk会按照采样数量遍历边界框内的点的坐标然后将坐标传递给sample_func回调函数。sample_func回调函数接受一个2D向量作为参数需要返回一个布尔值决定是否对当前坐标的点进行标记。march_hard会将所有标记的点储存到PolylineSet对象中并进行一些删减、分类之类的处理从而就有了创建多边形的每一个点。示例如下importpymunkfrompymunkimportautogeometryfrompprintimportpprint# 漂亮打印只是为了输出的结果更好看与pymunk无关defsample_func(point):xint(point[0])yint(point[1])# 采样点不是整数需要转换为整数后进行列表切片markedimg[y][x]ifmarkedx:# 判断当前采样点是否被标记returnTrue# 被标记的采样点会被储存到PolylineSet中else:returnFalseimg[...xx...,...xx...,...xx...,...xxxx.]# .表示空像素点区域x表示这个位置有像素wlen(img[0])# 图像的宽度(x方向上的长度)hlen(img)# 图像的高度(y方向上的长度)pl_setautogeometry.march_hard(pymunk.BB(0,0,w-1,h-1),w,h,0.1,sample_func)pprint(list(pl_set))# 打印出采样的结果运行后输出[[Vec2d(2.5, 3.0), Vec2d(2.5, 2.0), Vec2d(2.5, 1.0), Vec2d(2.5, 0.0)], [Vec2d(5.5, 0.0), Vec2d(5.5, 1.0), Vec2d(5.5, 2.0), Vec2d(5.5, 2.5), Vec2d(6.0, 2.5), Vec2d(6.5, 2.5), Vec2d(6.5, 3.0)]]这个示例中设置了采样范围为(0,0,w-1,h-1)在x方向上有w个采样点。因此pymunk在x方向上会从0遍历到w-1步长为1。采样过程的python代码表示如下实际算法肯定有所不同只是下面的写法比文字表述容易理解#bb: BB, x_samples: int, y_samples: int, sample_func: Callable[[Tuple[float, float]], float]defmarch_hard(...):forxinrange(bb.left,bb.right1,int(bb.right-bb.left)//x_samples):foryinrange(bb.bottom,bb.top1,int(bb.top-bb.bottom)//y_samples):ifsample_func((x,y)):...# 对这个点进行标记else:...# 舍弃除了march_hard之外autogeometry还有一个march_soft方法用法和march_hard完全一样只是适用范围不同。march_hard适合那种线条横平竖直的图像march_soft则对采样点进行了抗锯齿的处理使得采样线条更加平滑。通过march_hard方法进行采样后需要对采样后的数据进行处理。需要注意的是march_hard所返回的PolylineSet对象转换为列表后里面可能有多个小列表表示一条条折线。如果要创建多边形Poly的话首先需要把这些列表拼接起来。直接遍历列表进行拼接效果有时候不是特别理想如果想要完美复现形状可以将这些小列表中的折线用一条条Segment创建出来。importpymunkfrompymunkimportautogeometrydefsample_func(point):xint(point[0])yint(point[1])# 采样点不是整数需要转换为整数后进行列表切片markedimg[y][x]ifmarkedx:# 判断当前采样点是否被标记returnTrue# 被标记的采样点会被储存到PolylineSet中else:returnFalseimg[...xx...,...xx...,...xx...,...xxxx.]# .表示空像素点区域x表示这个位置有像素wlen(img[0])# 图像的宽度hlen(img)# 图像的高度pl_setautogeometry.march_hard(pymunk.BB(0,0,w-1,h-1),w,h,0.1,sample_func)pl[pointforainpl_setforpointinautogeometry.simplify_vertexes(a,1)]# 将列表拼接起来调用simplify_curves是为了优化折线效果spacepymunk.Space()bpymunk.Body()b.position(100,100)shapepymunk.Poly(b,pl,pymunk.Transform.scaling(10),radius1)# 创建多边形放大10倍shape.mass1space.add(b,shape)if__name____main__:importutil util.run(space)运行效果如下效果并不是很理想因为拼接列表并不是按照点的顺序将折线连接起来的需要其他算法进行处理。代码中还使用了autogeometry.simplify_curves函数。这个函数接受一个折线的点列表它会根据算法优化这个列表。这个函数还需要一个参数表示算法容错根据实际情况设定即可。autogeometry.simplify_vertexes([(0,0),(10,0),(20,0),(20,20),(10,20),(0,20)],0)[Vec2d(0.0,0.0),Vec2d(20.0,0.0),Vec2d(20.0,20.0),Vec2d(0.0,20.0)]上面这个示例中需要优化的点列表表示一个长方形但是其中包含了长方形的非支点。经过优化后只保留了长方形的四个支点坐标。与这个方法类似simplify_curves方法也用于优化点的列表但是这个函数比较适合平滑或者轻微弯曲的图形而simplify_vertexes适合边缘直或有角度的图形。下面这个示例展示了使用Segment连接图形的轮廓创建出一个和pymunk logo一样的图形。importpymunkimportpygameaspgfrompymunkimportautogeometry spacepymunk.Space()space.gravity(0,500)surfpg.image.load(pymunk_logo.png)# 导入图像w,hsurf.get_size()# 获取图像的宽和高defsample_func(point):colorsurf.get_at(point)# 获取位于point的像素颜色return1ifcolor[3]else0# 像素是否为不透明的pl_setautogeometry.march_soft(pymunk.BB(0,0,w-1,h-1),100,100,0.1,sample_func)# 抗锯齿取样polyline[bforainlist(pl_set)forbina]linepymunk.autogeometry.simplify_curves(polyline,1.0)# 优化多边形各点列表bpymunk.Body()b.position(100,100)space.add(b)d1foriinrange(len(line)-1):p1line[i]*d# 线段的起始点p2line[i1]*d# 线段的终点shapepymunk.Segment(b,p1,p2,1)shape.mass1shape.elasticity0.8space.add(shape)bpymunk.Body(body_typepymunk.Body.STATIC)# 创建静态地面b.position(0,550)shapepymunk.Segment(b,(0,10),(1000,10),3)shape.elasticity0.8space.add(b,shape)if__name____main__:importutil util.run(space)-关于autogeometry更多方法可参考https://www.pymunk.org/en/latest/pymunk.autogeometry.html9.2 调试通常情况下pymunk不提供有关绘图的内容因此需要与pygame, pyglet, matplotlib等图形模块相结合才能将绘制的内容显示到窗口上。但是为了方便测试pymunk提供了调试绘图方法Space.debug_draw。Space.debug_draw方法需要一个参数表示绘制选项。一般的绘图选项类是pymunk.SpaceDebugDrawOptions但是这个选项并不创建窗口绘制内容而是输出绘制的内容。importpymunk spacepymunk.Space()bodypymunk.Body()body.position(100,100)shapepymunk.Circle(body,10)shape.mass1space.add(body,shape)optionspymunk.SpaceDebugDrawOptions()# 创建绘图选项对象whileTrue:space.step(0.01)# 运行空间一段时间(0.01s)space.debug_draw(options)# 绘制空间中的内容运行后程序不断输出draw_circle (Vec2d(100.0, 100.0), 0.0, 10.0, SpaceDebugColor(r44.0, g62.0, b80.0, a255.0), SpaceDebugColor(r52.0, g152.0, b219.0, a255.0)) draw_circle (Vec2d(100.0, 100.0), 0.0, 10.0, SpaceDebugColor(r44.0, g62.0, b80.0, a255.0), SpaceDebugColor(r52.0, g152.0, b219.0, a255.0)) ...除此之外pymunk.pygame_util, pyglet_util, matplotlib_util中各包含一个DrawOptions类表示绘制选项分别用于在pygame, pyglet, matplotlib模块中的调试绘图。以pygame为例importpygameaspgimportpymunkfrompymunk.pygame_utilimportDrawOptions# 导入pygame_util中的绘图选项spacepymunk.Space()bodypymunk.Body()body.position(100,100)shapepymunk.Circle(body,10)shape.mass1space.add(body,shape)pg.init()screenpg.display.set_mode((1000,600))clockpg.time.Clock()optionsDrawOptions(screen)# pygame的绘图选项需要提供pygame窗口表面作为参数whileTrue:screen.fill((255,255,255))foreventinpg.event.get():ifevent.typepg.QUIT:pg.quit()exit()space.step(1/60)# 运行pymunk空间1/60sspace.debug_draw(options)# 调试绘图pg.display.flip()clock.tick(60)事实上这正类似于前文用于测试代码的util.py所做的util.py中还包含了一些附加功能。DrawOptions有一个重要的功能就是整体变换。可以通过设置一个transform参数来对整个画面进行缩放、平移、旋转等操作。optionspymunk.SpaceDebugDrawOptions()options.transformpymunk.Transform.scaling(2) pymunk.Transform.translation(2,3)关于矩阵变换的使用已在前文https://blog.csdn.net/qq_48979387/article/details/140383410#t4中介绍这里不详述。还有很多别的绘图选项也不是一成不变的形状颜色绘制哪些东西怎么绘制这些都是可以进行修改的尽管一般不需要。详见https://www.pymunk.org/en/latest/pymunk.html#pymunk.SpaceDebugDrawOptions9.3 其他子模块batchbatch模块用于对一些操作进行批处理可以有效提高程序的运行效率。但是batch模块目前是实验性的模块且涉及到较多内存操作这里暂不详细介绍。关于batch模块可以参考pymunk.examples.planet。文档位于https://www.pymunk.org/en/latest/pymunk.batch.htmlexamplespymunk的官方教程并不怎么详细但是读者可以通过pymunk文件夹下的examples文件中的程序学习pymunk。其中包含一些经典的示例。关于全部示例以及介绍参见https://www.pymunk.org/en/latest/examples.html10 总结关于本教程的任何疏漏之处和建议欢迎提出。另附一pymunkpygame游戏实战代码包含详细注释可在https://gitcode.com/qq_48979387/csdn-articles/tree/master/pymunk/2048下载。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑