Skip to content

Commit

Permalink
Merge branch 'shader_experimental' of https://github.com/pvcraven/arcade
Browse files Browse the repository at this point in the history
 into shader_experimental
  • Loading branch information
einarf committed Apr 25, 2020
2 parents c9aacbe + a14884a commit eaf5367
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 29 deletions.
9 changes: 3 additions & 6 deletions arcade/examples/sprite_bullets_aimed.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,7 @@ def setup(self):
arcade.set_background_color(arcade.color.AMAZON)

def on_draw(self):
"""
Render the screen.
"""
""" Render the screen. """

# This command has to happen before we start drawing
arcade.start_render()
Expand All @@ -112,9 +110,8 @@ def on_draw(self):
arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14)

def on_mouse_press(self, x, y, button, modifiers):
"""
Called whenever the mouse moves.
"""
""" Called whenever the mouse button is clicked. """

# Create a bullet
bullet = arcade.Sprite(":resources:images/space_shooter/laserBlue01.png", SPRITE_SCALING_LASER)

Expand Down
85 changes: 79 additions & 6 deletions arcade/experimental/pymunk_demo_top_down.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
Top-down
"""
import math
import arcade
from typing import Optional
from arcade.experimental.pymunk_physics_engine import PymunkPhysicsEngine

SCREEN_TITLE = "PyMunk Top-Down"
Expand All @@ -18,18 +20,23 @@

# Physics force used to move the player. Higher number, faster accelerating.
PLAYER_MOVE_FORCE = 4000
BULLET_MOVE_FORCE = 2500


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

arcade.set_background_color(arcade.color.AMAZON)

self.player_list = None
self.wall_list = None
self.bullet_list = None
self.item_list = None
self.player_sprite = None
self.physics_engine = None
self.physics_engine: Optional[PymunkPhysicsEngine] = None

# Track the current state of what key is pressed
self.left_pressed = False
Expand All @@ -38,9 +45,11 @@ def __init__(self, width, height, title):
self.down_pressed = False

def setup(self):
""" Set up everything """
# Create the sprite lists
self.player_list = arcade.SpriteList()
self.wall_list = arcade.SpriteList()
self.bullet_list = arcade.SpriteList()
self.item_list = arcade.SpriteList()

# Set up the player
Expand Down Expand Up @@ -80,7 +89,7 @@ def setup(self):

# Add some movable boxes
for x in range(SPRITE_SIZE * 3, SPRITE_SIZE * 8, SPRITE_SIZE):
item = arcade.Sprite(":resources:images/tiles/boxCrate.png",
item = arcade.Sprite(":resources:images/space_shooter/meteorGrey_big1.png",
SPRITE_SCALING_PLAYER)
item.center_x = x
item.center_y = 400
Expand All @@ -103,6 +112,13 @@ def setup(self):
self.physics_engine = PymunkPhysicsEngine(damping=damping,
gravity=gravity)

def rock_hit_handler(arbiter, space, data):
""" Called for bullet/rock collision """
bullet_shape = arbiter.shapes[0]
bullet_sprite = self.physics_engine.get_sprite_for_shape(bullet_shape)
bullet_sprite.remove_from_sprite_lists()

self.physics_engine.add_collision_handler("bullet", "rock", rock_hit_handler)
# Add the player.
# For the player, we set the damping to a lower value, which increases
# the damping rate. This prevents the character from traveling too far
Expand All @@ -117,6 +133,7 @@ def setup(self):
friction=0.6,
moment=PymunkPhysicsEngine.MOMENT_INF,
damping=0.01,
collision_type="player",
max_velocity=400)

# Create the walls.
Expand All @@ -128,13 +145,56 @@ def setup(self):
# Dynamic is default.
self.physics_engine.add_sprite_list(self.wall_list,
friction=0.6,
collision_type="wall",
body_type=PymunkPhysicsEngine.STATIC)

# Create some boxes to push around.
# Mass controls, well, the mass of an object. Defaults to 1.
self.physics_engine.add_sprite_list(self.item_list,
mass=1,
friction=0.6)
friction=0.6,
collision_type="rock")

def on_mouse_press(self, x, y, button, modifiers):
""" Called whenever the mouse button is clicked. """

bullet = arcade.SpriteSolidColor(5, 5, arcade.color.RED)
self.bullet_list.append(bullet)

# Position the bullet at the player's current location
start_x = self.player_sprite.center_x
start_y = self.player_sprite.center_y
bullet.position = self.player_sprite.position

# Get from the mouse the destination location for the bullet
# IMPORTANT! If you have a scrolling screen, you will also need
# to add in self.view_bottom and self.view_left.
dest_x = x
dest_y = y

# Do math to calculate how to get the bullet to the destination.
# Calculation the angle in radians between the start points
# and end points. This is the angle the bullet will travel.
x_diff = dest_x - start_x
y_diff = dest_y - start_y
angle = math.atan2(y_diff, x_diff)

force = [math.cos(angle), math.sin(angle)]
size = max(self.player_sprite.width, self.player_sprite.height) / 2

bullet.center_x += size * force[0]
bullet.center_y += size * force[1]

self.physics_engine.add_sprite(bullet,
mass=0.1,
damping=1.0,
friction=0.6)

# Taking into account the angle, calculate our force.
force[0] *= BULLET_MOVE_FORCE
force[1] *= BULLET_MOVE_FORCE

self.physics_engine.apply_force(bullet, force)

def on_key_press(self, key, modifiers):
"""Called whenever a key is pressed. """
Expand All @@ -147,6 +207,18 @@ def on_key_press(self, key, modifiers):
self.left_pressed = True
elif key == arcade.key.RIGHT:
self.right_pressed = True
elif key == arcade.key.SPACE:
bullet = arcade.SpriteSolidColor(5, 5, arcade.color.RED)
bullet.position = self.player_sprite.position
bullet.center_x += 30
self.bullet_list.append(bullet)
self.physics_engine.add_sprite(bullet,
mass=0.1,
damping=1.0,
friction=0.6,
collision_type="bullet")
force = (3000, 0)
self.physics_engine.apply_force(bullet, force)

def on_key_release(self, key, modifiers):
"""Called when the user releases a key. """
Expand Down Expand Up @@ -180,19 +252,20 @@ def on_update(self, delta_time):
elif self.right_pressed and not self.left_pressed:
force = (PLAYER_MOVE_FORCE, 0)
self.physics_engine.apply_force(self.player_sprite, force)
else:
print("Pow")

# --- Move items in the physics engine
self.physics_engine.step()
self.physics_engine.resync_sprites()

def on_draw(self):
""" Draw everything """
arcade.start_render()
self.wall_list.draw()
self.bullet_list.draw()
self.item_list.draw()
self.player_list.draw()

def main():
""" Main method """
window = MyWindow(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
window.setup()
arcade.run()
Expand Down
64 changes: 52 additions & 12 deletions arcade/experimental/pymunk_physics_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,30 @@ def __init__(self, gravity=(0, 0), damping: float = 1.0):
self.space = pymunk.Space()
self.space.gravity = gravity
self.space.damping = damping
self.collision_types = []
self.sprites = {}


def add_sprite(self,
sprite: Sprite,
mass: float=1,
friction: float=0.2,
mass: float = 1,
friction: float = 0.2,
moment=None,
body_type=DYNAMIC,
damping=None,
gravity=(0, 0),
max_velocity=None,
radius=0
radius: float = 0,
collision_type: str = None,
):
""" Add a sprite to the physics engine. """

if sprite in self.sprites:
print("Sprite already in added.")
print("Sprite already in space.")

if collision_type not in self.collision_types:
self.collision_types.append(collision_type)

collision_type_id = self.collision_types.index(collision_type)

if moment is None:
moment = pymunk.moment_for_box(mass, (sprite.width, sprite.height))
Expand All @@ -53,9 +60,10 @@ def add_sprite(self,
body.position = pymunk.Vec2d(sprite.center_x, sprite.center_y)

def velocity_callback(my_body, my_gravity, my_damping, dt):
""" Used for custom damping, gravity, and max_velocity. """
if damping is not None:

adj_damping = ((damping * 100) / 100) ** (dt)
adj_damping = ((damping * 100) / 100) ** dt
# print(damping, my_damping, adj_damping)
my_damping = adj_damping
if gravity is not None:
Expand All @@ -65,9 +73,9 @@ def velocity_callback(my_body, my_gravity, my_damping, dt):

if max_velocity:

l = my_body.velocity.length
if l > max_velocity:
scale = max_velocity / l
velocity = my_body.velocity.length
if velocity > max_velocity:
scale = max_velocity / velocity
body.velocity = body.velocity * scale

if damping is not None:
Expand All @@ -78,38 +86,70 @@ def velocity_callback(my_body, my_gravity, my_damping, dt):
scaled_poly = [[x * sprite.scale for x in z] for z in poly]

shape = pymunk.Poly(body, scaled_poly, radius=radius)
if collision_type:
shape.collision_type = collision_type_id
shape.friction = friction

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

self.space.add(body, shape)
sprite.register_physics_engine(self)

def add_sprite_list(self,
sprite_list: SpriteList,
mass: float = 1,
friction: float = 0.2,
moment=None,
body_type=DYNAMIC
body_type=DYNAMIC,
collision_type=None
):
""" Add all sprites in a sprite list to the physics engine. """

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

def remove_sprite(self, sprite: Sprite):
physics_object = self.sprites[sprite]
self.space.remove(physics_object.body)
self.space.remove(physics_object.shape)

def get_sprite_for_shape(self, shape) -> Sprite:
for sprite in self.sprites:
if self.sprites[sprite].shape is shape:
return sprite

def add_collision_handler(self, first_type, second_type, handler):
""" Add code to handle collisions between objects. """
if first_type not in self.collision_types:
self.collision_types.append(first_type)
first_type_id = self.collision_types.index(first_type)

if second_type not in self.collision_types:
self.collision_types.append(second_type)
second_type = self.collision_types.index(second_type)

h = self.space.add_collision_handler(first_type_id, second_type)
h.post_solve = handler

def resync_sprites(self):
""" Set visual sprites to be the same location as physics engine sprites. """
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):
def step(self, delta_time=1 / 60.0):
""" Tell the physics engine to perform calculations. """
# 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)
self.resync_sprites()

def apply_force(self, sprite, force):
""" Apply force to a Sprite. """
physics_object = self.sprites[sprite]
physics_object.body.apply_force_at_local_point(force, (0, 0))
12 changes: 7 additions & 5 deletions arcade/gl/texture.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,14 +215,16 @@ def wrap_y(self, value):
def read(self, level: int = 0, alignment: int = 1) -> bytearray:
"""
Read the contents of the texture.
:param int level: The texture level to read
:param int alignment: Alignment of the start of each row in memory in number of bytes. Possible values: 1,2,4
"""
gl.glActiveTexture(gl.GL_TEXTURE0)

gl.glBindTexture(self._target, self._glo)
gl.glPixelStorei(gl.GL_PACK_ALIGNMENT, 1)
gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1)
gl.glPixelStorei(gl.GL_PACK_ALIGNMENT, alignment)

buffer = (gl.GLubyte * (self.width * self.height * self._component_size))()
gl.glGetTexImage(gl.GL_TEXTURE_2D, 0, self._format, self._type, buffer)
buffer = (gl.GLubyte * (self.width * self.height * self._component_size * self._components))()
gl.glGetTexImage(gl.GL_TEXTURE_2D, level, self._format, self._type, buffer)

return bytearray(buffer)

def write(self, data: Union[bytes, Buffer], level: int = 0, viewport=None):
Expand Down
9 changes: 9 additions & 0 deletions arcade/sprite.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def __init__(self,
raise ValueError("Height can't be zero.")

self.sprite_lists: List[Any] = []
self.physics_engines: List[Any] = []

self._texture: Optional[Texture]
if filename is not None:
Expand Down Expand Up @@ -770,6 +771,9 @@ def register_sprite_list(self, new_list):
"""
self.sprite_lists.append(new_list)

def register_physics_engine(self, physics_engine):
self.physics_engines.append(physics_engine)

def draw(self):
""" Draw the sprite. """

Expand Down Expand Up @@ -825,6 +829,11 @@ def remove_from_sprite_lists(self):
for sprite_list in sprite_lists:
if self in sprite_list:
sprite_list.remove(self)

for engine in self.physics_engines:
engine.remove_sprite(self)

self.physics_engines.clear()
self.sprite_lists.clear()

def kill(self):
Expand Down

0 comments on commit eaf5367

Please sign in to comment.