Last active
January 9, 2021 02:00
-
-
Save dfsklar/a8fb22d543189646a492fe4d9ea8b885 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
INCLINED-PLANK version 1 | |
""" | |
import arcade | |
import pymunk | |
import random | |
import timeit | |
import math | |
SCREEN_TITLE = "Pymunk Inclined-Plane Example" | |
SCREEN_WIDTH = 800 | |
SCREEN_HEIGHT = 800 | |
SCREEN_HALF_WIDTH = SCREEN_WIDTH / 2 | |
GROUND_Y_LOC = 300 | |
FULCRUM_HEIGHT_FROM_GROUND = 40 | |
GROUND_ENABLED = True | |
GROUND_WIDTH = 560 | |
GROUND_HALF_WIDTH = GROUND_WIDTH / 2 | |
FULCRUM_Y_LOC = GROUND_Y_LOC + FULCRUM_HEIGHT_FROM_GROUND | |
SCREEN_UPDATE_FREQUENCY = 120.0 # Larger values make the ball movement *SLOWER* | |
# If automatic ball dropping is disabled, then only a mouse click generates a new ball for the scene to process. | |
AUTOMATIC_BALL_DROP_ENABLED = False | |
AUTOMATIC_BALL_DROP_DELAY = 1 # Smaller values make the balls drop more frequently | |
GRAVITY_DOWNWARD = -900 | |
GRAVITY_RIGHTWARD = 0 | |
class ArcadeGame(arcade.Window): | |
""" | |
This is the first time you're seeing a python concept called "class". | |
Think of it as a giant storage box that contains not only data-storage variables but also has "behavior" | |
in the form of built-in functions. | |
So it is "data PLUS behavior" all wrapped up in one giant box. | |
The most important thing to note about using classes is that you must use "self." to refer to a | |
function or data-storage box that is part of the class. | |
You'll see a lot of references to "self." below. | |
""" | |
def register_body_into_scene(self, body, *objs): | |
if not (body.body_type == pymunk.Body.STATIC): | |
print("Adding a dynamic body") | |
self.space.add(body) | |
else: | |
print("Adding a static body") | |
for shape in body.shapes: | |
self.space.add(shape) | |
self.list_of_segments.append(shape) | |
# Every arcade program must have an INITIALIZATION method that sets up the physics engine and creates | |
# the initial set of physics objects and "sprites". | |
# In this case, the floor and the pegs must be set up here, but the balls are created/dropped dynamically | |
# after "time has begun" so the balls are not created here. | |
def __init__(self, width, height, title): | |
super().__init__(width, height, title) | |
arcade.set_background_color(arcade.color.DARK_SLATE_GRAY) | |
self.time = 0 | |
self.space = pymunk.Space() | |
self.space.gravity = (GRAVITY_RIGHTWARD, GRAVITY_DOWNWARD) | |
self.list_of_segments = [] | |
# FIRST PHYSICS BODY: INCLINED PLANE/PLANK | |
# It is a *static* body -- it will never move. | |
# We are not required to specify the mass (weight) of a static body. | |
# But we MUST specify a friction value for any segment that might be touched by another object. | |
# So in this case, this one-segment body must have a friction value on that one segment. | |
plank = pymunk.Body(body_type=pymunk.Body.STATIC) | |
plank.position = (SCREEN_HALF_WIDTH, GROUND_Y_LOC) | |
plank_segment = (pymunk.Segment(body=plank, a=(0,0), b=(200,100), radius=2)) | |
plank_segment.friction = 1.8 # Without this setting, the plank will be "covered in slippery ice"! | |
plank_segment.color = arcade.color.YELLOW | |
self.register_body_into_scene(plank) | |
# SECOND PHYSICS BODY: GROUND (static) | |
ground_body = pymunk.Body(body_type=pymunk.Body.STATIC) | |
ground_body.position = (SCREEN_HALF_WIDTH, GROUND_Y_LOC) | |
ground_segment = \ | |
pymunk.Segment(body=ground_body, a=[-(GROUND_WIDTH/2), 0], b=[GROUND_WIDTH/2, 0], radius=3) | |
ground_segment.friction = 0.8 # Giving the ground *less* friction than the inclined plank | |
ground_segment.color = arcade.color.GREEN | |
self.register_body_into_scene(ground_body) | |
# THIRD PHYSICS BODY: The concrete block. This is dynamic of course! | |
# Any dynamic body needs to have a mass; the best approach is to set the mass information on its shapes, not on the body itself. | |
block_body = pymunk.Body(body_type=pymunk.Body.DYNAMIC) | |
block_body.position = (550, GROUND_Y_LOC+200) | |
thickness = 1 | |
# Obviously, the next few lines are awkward. I'm going to build a utility function that | |
# will make it easier for you to build polygonal objects, assign them colors/friction amounts, etc. | |
# So that's forthcoming - meanwhile, this approach of having to create a variable to store each segment | |
# is unfortunately necessary. | |
segment1 = pymunk.Segment(body=block_body, a=(0,0), b=(20,0), radius=thickness) # < LOCAL coords | |
segment2 = pymunk.Segment(body=block_body, a=(20,0), b=(20,20), radius=thickness) | |
segment3 = pymunk.Segment(body=block_body, a=(20,20), b=(0,20), radius=thickness) | |
# NOTE: We are intentionally giving only one line segment an actual mass value and friction value. | |
# This is what leads to the delightful tumbling that the block does when it falls. | |
# I'm giving this segment an orange color so you can distinguish this special segment from the others. | |
segment4 = pymunk.Segment(body=block_body, a=(0,20), b=(0,0), radius=thickness) | |
segment4.color = arcade.color.ORANGE | |
segment4.friction = 1 | |
segment4.mass = 300 | |
# Obviously, in the "real world", all 4 sides would have mass and friction. Keep that in mind... | |
self.register_body_into_scene(block_body) | |
def on_draw(self): | |
""" | |
Render the screen. | |
""" | |
arcade.start_render() | |
for line in self.list_of_segments: | |
body = line.body | |
pv1 = body.position + line.a.rotated(body.angle) | |
pv2 = body.position + line.b.rotated(body.angle) | |
try: | |
color = line.color | |
except: | |
color = arcade.color.WHITE | |
arcade.draw_line(pv1.x, pv1.y, pv2.x, pv2.y, color, 2) | |
def on_update(self, delta_time): | |
# Update physics | |
# See "Game loop / moving time forward" | |
# http://www.pymunk.org/en/latest/overview.html#game-loop-moving-time-forward | |
self.space.step(1 / SCREEN_UPDATE_FREQUENCY) | |
# So now that the class definition is finished, we must *build* an object of the class, and then run it. | |
ArcadeGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) | |
arcade.run() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment