Skip to content

Commit 6988eff

Browse files
authored
Fix incorrect PhyicsEnginePymunk doc and expand some stubs (#2216)
* Add intersphinx support for Pymunk * Fix the PymunkPhysicsEngine.set_friction docstring * Fix meaningless duplicated docstring * Add link target to Pymunk engine tutorial file * Add cross-references to set_friction * Fix broken line reference for tmx loading of platforms * Fix doc for the set_velocity function * Correct incorrect doctsring for set_position * Expand unclear get_sprite_for_shape docstring * Document pymunk.Body type constant aliases * Whitespace fixes * Corrections to add_sprite doc * Typo fix in cross-reference * Improvements to add_sprite docstring * Add some cross-refs * Rework and cross-ref the top-level docstring of PymunkPhysicsEngine * Fix cross-ref to PhysicsEnginePymunk * Fix whitespace for ruff * Fix whitespace issues in docstring * Fix more typos * Fix external links
1 parent 4250758 commit 6988eff

File tree

4 files changed

+226
-33
lines changed

4 files changed

+226
-33
lines changed

arcade/physics_engines.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,8 @@ class PhysicsEngineSimple:
301301
302302
For side-scrolling games focused on jumping puzzles, you may want
303303
the :py:class:`PlatformerPhysicsEngine` instead. Experienced users
304-
may want to try the :py:class:`~arcade.pymunk_phyics_engine.PymunkPhysicsEngine`.
304+
may want to try the
305+
:py:class:`~arcade.pymunk_physics_engine.PymunkPhysicsEngine`.
305306
306307
Args:
307308
player_sprite:

arcade/pymunk_physics_engine.py

Lines changed: 217 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,103 @@ class PymunkException(Exception):
3737
# Temp fix for https://github.com/pythonarcade/arcade/issues/2074
3838
@copy_dunders_unimplemented
3939
class PymunkPhysicsEngine:
40-
"""
41-
Pymunk Physics Engine
40+
"""An Arcade-specific adapter for Pymunk.
41+
42+
.. _Pymunk: https://www.pymunk.org/en/latest/index.html
43+
.. _Chipmunk2D: https://chipmunk-physics.net/
44+
.. _CONTRIBUTING.md: https://github.com/pythonarcade/arcade/blob/development/CONTRIBUTING.md
45+
46+
`Pymunk`_ is itself a Python adapter for the professional-grade
47+
`Chipmunk2D`_ engine. However, Arcade's ``PymunkPhysicsEngine``
48+
and its doc are currently in need of improvement.
49+
50+
.. note:: Arcade would welcome assistance with improving it.
51+
52+
If you are interested, please see Arcade's
53+
`CONTRIBUTING.md`_.
54+
55+
Args:
56+
gravity:
57+
The direction where gravity is pointing.
58+
See :py:attr:`pymunk.Space.gravity` to learn more.
59+
damping:
60+
The default velocity loss per tick across the
61+
:py:class:`~pymunk.Space` for all :py:attr:`DYNAMIC`
62+
objects.
63+
64+
* Override this for objects by passing different value
65+
:`add_sprite` or :py:meth:`add_spritelist`
66+
* See :py:attr:`pymunk.Space.damping` to learn more
67+
68+
maximum_incline_on_ground:
69+
The maximum incline the ground can have before
70+
:py:meth:`is_on_ground` returns ``False``.
71+
72+
* Defaults to ``0.708`` radians (a bit over 45 °)
73+
* Not a pymunk value, but an Arcade feature
4274
43-
:param gravity: The direction where gravity is pointing
44-
:param damping: The amount of speed which is kept to the next tick. A value of 1.0 means no speed loss,
45-
while 0.9 has 10% loss of speed etc.
46-
:param maximum_incline_on_ground: The maximum incline the ground can have, before is_on_ground() becomes False
47-
default = 0.708 or a little bit over 45° angle
4875
"""
4976

5077
DYNAMIC = pymunk.Body.DYNAMIC
78+
"""A ``body_type`` for moving Pymunk-controlled objects.
79+
80+
An indirect approach is best for controlling the velocity and
81+
positioning of dynamic objects:
82+
83+
* :py:meth:`apply_force`
84+
* :py:meth:`apply_impulse`
85+
86+
.. warning:: Avoid setting velocity directly on dynamic objects!
87+
88+
If you need to set velocity directly, you may want to
89+
pass :py:attr:`KINEMATIC` as the ``body_type`` to
90+
:py:meth:`add_sprite` instead.
91+
92+
If you :py:class:`set_velocity` directly anyway, the
93+
following may occur:
94+
95+
#. Setting velocity approaches infinite acceleration
96+
#. ``f = m * a`` approaches ``f = m * infinity``
97+
#. Collisions go haywire
98+
99+
In some games, you may be able to find a way to harness this for
100+
comedic effect.
101+
102+
.. note:: This value is an alias of :py:attr:`pymunk.Body.DYNAMIC`.
103+
104+
Please see the Pymunk page linked above to learn more.
105+
"""
51106
STATIC = pymunk.Body.STATIC
107+
"""A ``body_type`` for objects which do not move.
108+
109+
This is best used for terrain or non-moving platforms.
110+
111+
.. note:: This value is an alias of :py:attr:`pymunk.Body.STATIC`.
112+
113+
Please see the Pymunk page linked above to learn more.
114+
"""
52115
KINEMATIC = pymunk.Body.KINEMATIC
116+
"""A ``body_type`` for objects controlled by your code or Arcade's.
117+
118+
When colliding, Kinematic objects:
119+
120+
* act as if they have infinite mass
121+
* prevent joined and touching objects from sleeping
122+
123+
This makes them excellent for game elements like moving platforms or
124+
hazards which move or crush game objects. You can control kinematic
125+
objects by setting their positions and velocities directly:
126+
127+
* :py:meth:`set_velocity`
128+
* :py:meth:`set_velocity_horizontal`
129+
* :py:meth:`set_velocity_vertical`
130+
* :py:meth:`set_position`
131+
132+
133+
.. note:: This value is an alias of :py:attr:`pymunk.Body.KINEMATIC`.
134+
135+
Please see the Pymunk page linked above to learn more.
136+
"""
53137
MOMENT_INF = float("inf")
54138

55139
def __init__(
@@ -67,7 +151,7 @@ def __init__(
67151
def add_sprite(
68152
self,
69153
sprite: Sprite,
70-
mass: float = 1,
154+
mass: float = 1.0,
71155
friction: float = 0.2,
72156
elasticity: Optional[float] = None,
73157
moment_of_inertia: Optional[float] = None, # correct spelling
@@ -80,23 +164,72 @@ def add_sprite(
80164
radius: float = 0,
81165
collision_type: Optional[str] = "default",
82166
):
83-
""" Add a sprite to the physics engine.
84-
85-
:param sprite: The sprite to add.
86-
:param mass: The mass of the object. Defaults to 1.
87-
:param friction: The friction the object has. Defaults to 0.2.
88-
:param elasticity: How bouncy this object is. 0 is no bounce. Values of 1.0 and higher may behave badly.
89-
:param moment_of_inertia: The moment of inertia, or force needed to change angular momentum. \
90-
Providing infinite makes this object stuck in its rotation.
91-
:param body_type: The type of the body. Defaults to Dynamic, meaning, the body can move, rotate etc. \
92-
Providing STATIC makes it fixed to the world.
93-
:param damping: See class docs.
94-
:param gravity: See class docs.
95-
:param max_velocity: The maximum velocity of the object.
96-
:param max_horizontal_velocity: Maximum velocity on the x axis in pixels.
97-
:param max_vertical_velocity: Maximum velocity on the y axis in pixels.
98-
:param radius: Radius for the shape created for the sprite in pixels.
99-
:param collision_type: Assign a name to the sprite, use this name when adding collision handler.
167+
"""Add a sprite to the physics engine.
168+
169+
Args:
170+
sprite:
171+
A :py:class:`.Sprite` to add
172+
mass:
173+
The mass of the object (Defaults to ``1.0``).
174+
friction:
175+
How much the object resists sliding against surfaces:
176+
177+
.. list-table::
178+
:header-rows: 0
179+
180+
* - ``0.0``
181+
- Absolutely slippery with no resistance at all
182+
* - ``0.2``
183+
- Default (Waxed wood on very wet snow)
184+
* - ``friction > 1.0``
185+
- Very rough
186+
187+
*Higher values may not make a meaningful difference.*
188+
189+
See :py:attr:`pymunk.Shape.friction` to learn more.
190+
191+
elasticity:
192+
How bouncy the object is.
193+
194+
.. list-table::
195+
:header-rows: 0
196+
197+
* - ``0.0``
198+
- No bounce
199+
* - ``0.99``
200+
- Very bouncy
201+
* - ``elasticity >= 1.0``
202+
- May behave badly (breaks conservation of energy)
203+
204+
See :py:attr:`pymunk.Shape.elasticity` to learn more.
205+
206+
moment_of_inertia:
207+
How much force is needed to change the object's rotation (
208+
pass :py:attr:`MOMENT_INF` or ``float('inf')`` to "lock"
209+
its angle).
210+
211+
See :py:attr:`pymunk.Shape.moment_of_inertia` to learn more.
212+
213+
body_type:
214+
:py:attr:`DYNAMIC` (default), :py:attr:`KINEMATIC`, or
215+
:py:attr:`STATIC`.
216+
damping:
217+
Like air resistance. See the :py:class:`.PymunkPhysicsEngine`
218+
top-level doc.
219+
gravity:
220+
See the :py:class:`.PymunkPhysicsEngine` top-level doc.
221+
max_velocity:
222+
The maximum velocity of this object.
223+
max_horizontal_velocity:
224+
Clamp the velocity on the x axis to this.
225+
max_vertical_velocity:
226+
Clamp the velocity along the y axis to this.
227+
radius:
228+
The radius for the :py:class:`pymunk.Shape` created for
229+
the :py:class:`sprite <.Sprite>`.
230+
collision_type:
231+
Assign a collision name to this sprite. It will be used
232+
by :py:meth:`add_collision_handler` if called.
100233
"""
101234

102235
if damping is not None:
@@ -228,7 +361,6 @@ def add_sprite_list(
228361
collision_type: Optional[str] = None,
229362
):
230363
"""Add all sprites in a sprite list to the physics engine."""
231-
232364
for sprite in sprite_list:
233365
self.add_sprite(
234366
sprite=sprite,
@@ -251,7 +383,22 @@ def remove_sprite(self, sprite: Sprite):
251383
self.non_static_sprite_list.remove(sprite)
252384

253385
def get_sprite_for_shape(self, shape: Optional[pymunk.Shape]) -> Optional[Sprite]:
254-
"""Given a shape, what sprite is associated with it?"""
386+
"""Try to get the sprite registered with this engine for ``shape``.
387+
388+
This method returns ``None`` when:
389+
390+
* ``shape`` is ``None``
391+
* No :py:class:`.Sprite` was to this engine for ``shape``
392+
393+
The second item may occur if you are using multiple instances of
394+
:py:class:`.PymunkPhysicsEngine`.
395+
396+
Args:
397+
shape:
398+
A Pymunk shape to perform lookup for.
399+
Returns:
400+
A sprite for the ``shape``; ``None`` if no sprite is known.
401+
"""
255402
for sprite in self.sprites:
256403
if self.sprites[sprite].shape is shape:
257404
return sprite
@@ -281,7 +428,16 @@ def apply_impulse(self, sprite: Sprite, impulse: tuple[float, float]):
281428
physics_object.body.apply_impulse_at_local_point(impulse)
282429

283430
def set_position(self, sprite: Sprite, position: Union[pymunk.Vec2d, tuple[float, float]]):
284-
"""Apply an impulse force on a sprite"""
431+
"""Set the position of the sprite in the engine's simulation.
432+
433+
To learn more, please see :py:attr:`pymunk.Body.position`.
434+
435+
Args:
436+
sprite:
437+
An Arcade :py:class:`.Sprite` known to the engine.
438+
position:
439+
A two-dimensional position in world space.
440+
"""
285441
physics_object = self.get_physics_object(sprite)
286442
if physics_object.body is None:
287443
raise PymunkException(
@@ -298,7 +454,20 @@ def set_rotation(self, sprite: Sprite, rotation: float):
298454
physics_object.body.angle = math.radians(rotation)
299455

300456
def set_velocity(self, sprite: Sprite, velocity: tuple[float, float]):
301-
"""Apply an impulse force on a sprite"""
457+
"""Directly set the velocity of a sprite known to the engine.
458+
459+
.. warning:: Avoid using this on any :py:attr:`DYNAMIC` objects!
460+
461+
This function is meant for :py:attr:`KINEMATIC` objects. Using
462+
it on a sprite added as :py:attr:`DYNAMIC` can cause strange and
463+
very broken behavior.
464+
465+
To learn more, please see:
466+
467+
* Pymunk's documentation on :py:attr:`~pymunk.Body.DYNAMIC` and
468+
:py:attr:`~pymunk.Body.KINEMATIC`
469+
470+
"""
302471
physics_object = self.get_physics_object(sprite)
303472
if physics_object.body is None:
304473
raise PymunkException(
@@ -440,7 +609,25 @@ def set_horizontal_velocity(self, sprite: Sprite, velocity: float):
440609
physics_object.body.velocity = new_cv
441610

442611
def set_friction(self, sprite: Sprite, friction: float):
443-
"""Apply force to a Sprite."""
612+
"""Set the friction a sprite experiences against other surfaces.
613+
614+
This is how "rough" a sprite is during a collision with others:
615+
616+
* ``0.0`` is the lowest value allowed (absolute slipperiness)
617+
* Higher values slide less on surfaces and other objects
618+
619+
Pymunk allows setting ``friction`` higher than ``1.0``, but very
620+
high values might not have meaningful gameplay impact.
621+
622+
.. _Simple Wikipedia's Article on Friction: https://simple.wikipedia.org/wiki/Friction
623+
624+
To learn more, please see:
625+
626+
* The :ref:`pymunk_platformer_tutorial-add_physics_engine` step
627+
of the :ref:`pymunk_platformer_tutorial`
628+
* `Simple Wikipedia's Article on Friction`_
629+
* :py:attr:`pymunk.Poly.friction`
630+
"""
444631
physics_object = self.sprites[sprite]
445632
if physics_object.shape is None:
446633
raise PymunkException(

doc/conf.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,9 @@
177177
# Example configuration for intersphinx: refer to the Python standard library.
178178
intersphinx_mapping = {'python': ('https://docs.python.org/3', None),
179179
'pyglet': ('https://pyglet.readthedocs.io/en/latest/', None),
180-
'PIL': ('https://pillow.readthedocs.io/en/stable', None)}
180+
'PIL': ('https://pillow.readthedocs.io/en/stable', None),
181+
'pymunk': ('https://www.pymunk.org/en/latest/', None)
182+
}
181183

182184
# Fix: "more than one target found for cross-reference 'Texture'"
183185
suppress_warnings = [

doc/tutorials/pymunk_platformer/index.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ tiled map you created:
155155
* :ref:`pymunk_demo_platformer_04`
156156
* :ref:`pymunk_demo_platformer_04_diff`
157157

158+
159+
.. _pymunk_platformer_tutorial-add_physics_engine:
160+
158161
Add Physics Engine
159162
------------------
160163

@@ -503,7 +506,7 @@ In the ``setup`` method, load in the sprite list from the tmx layer.
503506

504507
.. literalinclude:: pymunk_demo_platformer_11.py
505508
:caption: Moving Platforms - Adding the sprite list
506-
:lines: 227
509+
:lines: 230-231
507510

508511
Also in the ``setup`` method, we need to add these sprites to the physics engine.
509512
In this case we'll add the sprites as ``KINEMATIC``. Static sprites don't move.

0 commit comments

Comments
 (0)