Skip to content

Commit

Permalink
Start work on a pymunk physics engine front.
Browse files Browse the repository at this point in the history
  • Loading branch information
pvcraven committed Apr 22, 2020
1 parent 3e71e9a commit 53e2871
Show file tree
Hide file tree
Showing 2 changed files with 254 additions and 0 deletions.
93 changes: 93 additions & 0 deletions arcade/experimental/pymunk_physics_engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""
Pymunk Physics Engine
"""

import pymunk
import math
from arcade import Sprite
from arcade import SpriteList

class _PhysicsObject:
def __init__(self, body=None, shape=None):
self.body = body
self.shape = shape


class PymunkPhysicsEngine:
"""
Pymunk Physics Engine
"""

DYNAMIC = pymunk.Body.DYNAMIC
STATIC = pymunk.Body.STATIC
KINEMATIC = pymunk.Body.KINEMATIC
MOMENT_INF = pymunk.inf

def __init__(self, gravity=(0,0)):
# -- Pymunk
self.space = pymunk.Space()
self.space.gravity = gravity
self.sprites = {}


def add_sprite(self,
sprite: Sprite,
mass: float=1,
friction: float=0.2,
moment=None,
body_type=DYNAMIC,
radius=0
):
if sprite in self.sprites:
print("Sprite already in added.")

if moment is None:
moment = pymunk.moment_for_box(mass, (sprite.width, sprite.height))

body = pymunk.Body(mass, moment, body_type=body_type)
body.position = pymunk.Vec2d(sprite.center_x, sprite.center_y)

scaled_poly = []
poly = sprite.get_hit_box()
for vert in poly:
scaled_vert = []
for coord in vert:
scaled_vert.append(coord * sprite.scale)
scaled_poly.append(scaled_vert)

shape = pymunk.Poly(body, scaled_poly, radius=radius)
shape.friction = friction

physics_object = _PhysicsObject(body, shape)
self.sprites[sprite] = physics_object

self.space.add(body, shape)

def add_sprite_list(self,
sprite_list: SpriteList,
mass: float = 1,
friction: float = 0.2,
moment=None,
body_type=DYNAMIC
):

for sprite in sprite_list:
self.add_sprite(sprite, mass, friction, moment, body_type)

def resync_sprites(self):
for sprite in self.sprites:
physics_object = self.sprites[sprite]
sprite.center_x = physics_object.body.position.x
sprite.center_y = physics_object.body.position.y
sprite.angle = math.degrees(physics_object.body.angle)

def step(self, delta_time = 1/60.0):
# Update physics
# Use a constant time step, don't use delta_time
# See "Game loop / moving time forward"
# http://www.pymunk.org/en/latest/overview.html#game-loop-moving-time-forward
self.space.step(delta_time)

def apply_force(self, sprite, force):
physics_object = self.sprites[sprite]
physics_object.body.apply_force_at_local_point(force, (0, 0))
161 changes: 161 additions & 0 deletions arcade/experimental/pymunk_test_1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import arcade

SCREEN_TITLE = "Starting Template Simple"
SPRITE_SCALING_PLAYER = 0.5
MOVEMENT_SPEED = 5
PLAYER_MOVE_FORCE = 700

SPRITE_IMAGE_SIZE = 128
SPRITE_SIZE = int(SPRITE_IMAGE_SIZE * SPRITE_SCALING_PLAYER)

SCREEN_WIDTH = SPRITE_SIZE * 20
SCREEN_HEIGHT = SPRITE_SIZE * 15

from arcade.experimental.pymunk_physics_engine import PymunkPhysicsEngine

class MyWindow(arcade.Window):
def __init__(self, width, height, title):
super().__init__(width, height, title)

arcade.set_background_color(arcade.color.AMAZON)

self.player_list = None
self.wall_list = None
self.item_list = None
self.player_sprite = None
self.physics_engine = None

# Track the current state of what key is pressed
self.left_pressed = False
self.right_pressed = False
self.up_pressed = False
self.down_pressed = False

def setup(self):
# Create the sprite lists
self.player_list = arcade.SpriteList()
self.wall_list = arcade.SpriteList()
self.item_list = arcade.SpriteList()

# Set up the player
self.player_sprite = arcade.Sprite(":resources:images/animated_characters/female_person/femalePerson_idle.png",
SPRITE_SCALING_PLAYER)
self.player_sprite.center_x = 250
self.player_sprite.center_y = 250
self.player_list.append(self.player_sprite)

# Set up the walls
for x in range(0, SCREEN_WIDTH + 1, SPRITE_SIZE):
wall = arcade.Sprite(":resources:images/tiles/grassCenter.png",
SPRITE_SCALING_PLAYER)
wall.center_x = x
wall.center_y = 0
self.wall_list.append(wall)

wall = arcade.Sprite(":resources:images/tiles/grassCenter.png",
SPRITE_SCALING_PLAYER)
wall.center_x = x
wall.center_y = SCREEN_HEIGHT
self.wall_list.append(wall)

# Set up the walls
for y in range(SPRITE_SIZE, SCREEN_HEIGHT, SPRITE_SIZE):
wall = arcade.Sprite(":resources:images/tiles/grassCenter.png",
SPRITE_SCALING_PLAYER)
wall.center_x = 0
wall.center_y = y
self.wall_list.append(wall)

wall = arcade.Sprite(":resources:images/tiles/grassCenter.png",
SPRITE_SCALING_PLAYER)
wall.center_x = SCREEN_WIDTH
wall.center_y = y
self.wall_list.append(wall)

# Add some movable boxes
for x in range(SPRITE_SIZE * 3, SPRITE_SIZE * 6, SPRITE_SIZE):
item = arcade.Sprite(":resources:images/tiles/boxCrate.png",
SPRITE_SCALING_PLAYER)
item.center_x = x
item.center_y = 400
self.item_list.append(item)

# Create the physics engine
self.physics_engine = PymunkPhysicsEngine()
self.physics_engine.add_sprite(self.player_sprite, moment=PymunkPhysicsEngine.MOMENT_INF)

self.physics_engine.add_sprite_list(self.wall_list,
mass=1,
friction=0,
moment=None,
body_type=PymunkPhysicsEngine.STATIC)

self.physics_engine.add_sprite_list(self.item_list,
mass=1,
friction=0,
moment=None,
body_type=PymunkPhysicsEngine.DYNAMIC)


def on_key_press(self, key, modifiers):
"""Called whenever a key is pressed. """

if key == arcade.key.UP:
self.up_pressed = True
elif key == arcade.key.DOWN:
self.down_pressed = True
elif key == arcade.key.LEFT:
self.left_pressed = True
elif key == arcade.key.RIGHT:
self.right_pressed = True

def on_key_release(self, key, modifiers):
"""Called when the user releases a key. """

if key == arcade.key.UP:
self.up_pressed = False
elif key == arcade.key.DOWN:
self.down_pressed = False
elif key == arcade.key.LEFT:
self.left_pressed = False
elif key == arcade.key.RIGHT:
self.right_pressed = False

def on_update(self, delta_time):
""" Movement and game logic """

# Calculate speed based on the keys pressed
self.player_sprite.change_x = 0
self.player_sprite.change_y = 0

if self.up_pressed and not self.down_pressed:
force = (0, PLAYER_MOVE_FORCE)
self.physics_engine.apply_force(self.player_sprite, force)
elif self.down_pressed and not self.up_pressed:
force = (0, -PLAYER_MOVE_FORCE)
self.physics_engine.apply_force(self.player_sprite, force)
if self.left_pressed and not self.right_pressed:
self.player_sprite.change_x = -MOVEMENT_SPEED
force = (-PLAYER_MOVE_FORCE, 0)
self.physics_engine.apply_force(self.player_sprite, force)
elif self.right_pressed and not self.left_pressed:
force = (PLAYER_MOVE_FORCE, 0)
self.physics_engine.apply_force(self.player_sprite, force)

self.physics_engine.step()
self.physics_engine.resync_sprites()

def on_draw(self):
arcade.start_render()
self.wall_list.draw()
self.item_list.draw()
self.player_list.draw()

def main():
window = MyWindow(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
window.setup()
arcade.run()


if __name__ == "__main__":
main()

0 comments on commit 53e2871

Please sign in to comment.