Created
January 6, 2013 22:43
-
-
Save desophos/4470807 to your computer and use it in GitHub Desktop.
The beginnings of a ragdoll game.
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
from Gun import Gun | |
from globals import * | |
import pygame | |
import pymunk | |
class Character(pygame.sprite.Sprite): | |
""" Describes a moving character ingame made up of pymunk bodies/shapes. """ | |
def __init__( self, body_type="ragdoll", w=10, h=10, m=1, mo=1, pos=(SCREEN_SIZE/2, SCREEN_SIZE/2) ): | |
# pygame init | |
pygame.sprite.Sprite.__init__(self) | |
self.image = pygame.Surface([w, h]) | |
self.rect = self.image.get_rect() | |
# pymunk init | |
# construct the character's body here | |
# need PinJoint and RotaryLimitJoint between the gun and the character to keep them stuck together | |
if body_type == "square": | |
# for testing | |
self.right_hand = pymunk.Body(m,mo) | |
self.right_hand.position = pos | |
# poly vertices are ordered counterclockwise | |
# pymunk coordinates: positive (x,y) is quadrant 4, i.e. bottom right | |
self.right_hand_shape = pymunk.Poly(self.right_hand, [ (0,0), (0,-h), (w,-h), (w,0) ]) # vertices are ordered counterclockwise | |
self.bodies = [self.right_hand] | |
self.body_shapes = [self.right_hand_shape] | |
elif body_type == "ragdoll": | |
# create the body pieces | |
head = pymunk.Body(HEAD_MASS, HEAD_MOMENT) | |
torso = pymunk.Body(TORSO_MASS, TORSO_MOMENT) | |
upper_arm_l = pymunk.Body(UPPER_ARM_MASS, UPPER_ARM_MOMENT) | |
upper_arm_r = pymunk.Body(UPPER_ARM_MASS, UPPER_ARM_MOMENT) | |
lower_arm_l = pymunk.Body(LOWER_ARM_MASS, LOWER_ARM_MOMENT) | |
lower_arm_r = pymunk.Body(LOWER_ARM_MASS, LOWER_ARM_MOMENT) | |
upper_leg_l = pymunk.Body(UPPER_LEG_MASS, UPPER_LEG_MOMENT) | |
upper_leg_r = pymunk.Body(UPPER_LEG_MASS, UPPER_LEG_MOMENT) | |
lower_leg_l = pymunk.Body(LOWER_LEG_MASS, LOWER_LEG_MOMENT) | |
lower_leg_r = pymunk.Body(LOWER_LEG_MASS, LOWER_LEG_MOMENT) | |
self.bodies = [head, | |
torso, | |
upper_arm_l, | |
upper_arm_r, | |
lower_arm_l, | |
lower_arm_r, | |
upper_leg_l, | |
upper_leg_r, | |
lower_leg_l, | |
lower_leg_r | |
] | |
# give the body pieces shapes | |
head_shape = pymunk.Circle(head, HEAD_RADIUS) | |
torso_shape = pymunk.Poly(torso, [(0,0), (0,TORSO_LENGTH), (BODY_THICKNESS, TORSO_LENGTH), (BODY_THICKNESS,0)]) | |
upper_arm_l_shape = pymunk.Poly(upper_arm_l, [(0,0), (0,UPPER_ARM_LENGTH), (BODY_THICKNESS, UPPER_ARM_LENGTH), (BODY_THICKNESS,0)]) | |
upper_arm_r_shape = pymunk.Poly(upper_arm_r, [(0,0), (0,UPPER_ARM_LENGTH), (BODY_THICKNESS, UPPER_ARM_LENGTH), (BODY_THICKNESS,0)]) | |
lower_arm_l_shape = pymunk.Poly(lower_arm_l, [(0,0), (0,LOWER_ARM_LENGTH), (BODY_THICKNESS, LOWER_ARM_LENGTH), (BODY_THICKNESS,0)]) | |
lower_arm_r_shape = pymunk.Poly(lower_arm_r, [(0,0), (0,LOWER_ARM_LENGTH), (BODY_THICKNESS, LOWER_ARM_LENGTH), (BODY_THICKNESS,0)]) | |
upper_leg_l_shape = pymunk.Poly(upper_leg_l, [(0,0), (0,UPPER_LEG_LENGTH), (BODY_THICKNESS, UPPER_LEG_LENGTH), (BODY_THICKNESS,0)]) | |
upper_leg_r_shape = pymunk.Poly(upper_leg_r, [(0,0), (0,UPPER_LEG_LENGTH), (BODY_THICKNESS, UPPER_LEG_LENGTH), (BODY_THICKNESS,0)]) | |
lower_leg_l_shape = pymunk.Poly(lower_leg_l, [(0,0), (0,LOWER_LEG_LENGTH), (BODY_THICKNESS, LOWER_LEG_LENGTH), (BODY_THICKNESS,0)]) | |
lower_leg_r_shape = pymunk.Poly(lower_leg_r, [(0,0), (0,LOWER_LEG_LENGTH), (BODY_THICKNESS, LOWER_LEG_LENGTH), (BODY_THICKNESS,0)]) | |
self.body_shapes = [head_shape, | |
torso_shape, | |
upper_arm_l_shape, | |
upper_arm_r_shape, | |
lower_arm_l_shape, | |
lower_arm_r_shape, | |
upper_leg_l_shape, | |
upper_leg_r_shape, | |
lower_leg_l_shape, | |
lower_leg_r_shape | |
] | |
for s in self.body_shapes: | |
s.color = pygame.color.THECOLORS["black"] | |
s.group = COLLISION_GROUP["character"] | |
# set positions of bodies | |
offset = 0 | |
torso.position = pos | |
head.position = (torso.position.x, torso.position.y + TORSO_LENGTH/2 + HEAD_RADIUS + offset) | |
upper_arm_l.position = (torso.position.x-offset, torso.position.y + TORSO_LENGTH/2) | |
upper_arm_r.position = (torso.position.x+offset, torso.position.y + TORSO_LENGTH/2) | |
lower_arm_l.position = (upper_arm_l.position.x-offset, upper_arm_l.position.y - UPPER_ARM_LENGTH/2) | |
lower_arm_r.position = (upper_arm_r.position.x+offset, upper_arm_r.position.y - UPPER_ARM_LENGTH/2) | |
upper_leg_l.position = (torso.position.x-offset, torso.position.y - TORSO_LENGTH/2) | |
upper_leg_r.position = (torso.position.x+offset, torso.position.y - TORSO_LENGTH/2) | |
lower_leg_l.position = (upper_leg_l.position.x-offset, upper_leg_l.position.y - UPPER_LEG_LENGTH/2) | |
lower_leg_r.position = (upper_leg_r.position.x+offset, upper_leg_r.position.y - UPPER_LEG_LENGTH/2) | |
# link pieces of the body together | |
# attach bodies at midpoints of edges | |
self.joints = [pymunk.PivotJoint(head, torso, (0,-HEAD_RADIUS), (BODY_THICKNESS/2,0)), | |
pymunk.PivotJoint(torso, upper_arm_l, (0,0), (BODY_THICKNESS/2,0)), | |
pymunk.PivotJoint(torso, upper_arm_r, (BODY_THICKNESS,0), (BODY_THICKNESS/2,0)), | |
pymunk.PivotJoint(upper_arm_l, lower_arm_l, (BODY_THICKNESS/2,UPPER_ARM_LENGTH), (BODY_THICKNESS/2,0)), | |
pymunk.PivotJoint(upper_arm_r, lower_arm_r, (BODY_THICKNESS/2,UPPER_ARM_LENGTH), (BODY_THICKNESS/2,0)), | |
pymunk.PivotJoint(torso, upper_leg_l, (0,TORSO_LENGTH), (BODY_THICKNESS/2,0)), | |
pymunk.PivotJoint(torso, upper_leg_r, (BODY_THICKNESS,TORSO_LENGTH), (BODY_THICKNESS/2,0)), | |
pymunk.PivotJoint(upper_leg_l, lower_leg_l, (BODY_THICKNESS/2,UPPER_LEG_LENGTH), (BODY_THICKNESS/2,0)), | |
pymunk.PivotJoint(upper_leg_r, lower_leg_r, (BODY_THICKNESS/2,UPPER_LEG_LENGTH), (BODY_THICKNESS/2,0)) | |
] | |
# set joint rotation constraints | |
neck_rot = pymunk.RotaryLimitJoint(head, torso, NECK_ROTATION_MIN, NECK_ROTATION_MAX) | |
shoulder_rot_l = pymunk.RotaryLimitJoint(torso, upper_arm_l, SHOULDER_L_ROTATION_MIN, SHOULDER_L_ROTATION_MAX) | |
shoulder_rot_r = pymunk.RotaryLimitJoint(torso, upper_arm_r, SHOULDER_R_ROTATION_MIN, SHOULDER_R_ROTATION_MAX) | |
elbow_rot_l = pymunk.RotaryLimitJoint(upper_arm_l, lower_arm_l, ELBOW_L_ROTATION_MIN, ELBOW_L_ROTATION_MAX) | |
elbow_rot_r = pymunk.RotaryLimitJoint(upper_arm_r, lower_arm_r, ELBOW_R_ROTATION_MIN, ELBOW_R_ROTATION_MAX) | |
hip_rot_l = pymunk.RotaryLimitJoint(torso, upper_leg_l, HIP_L_ROTATION_MIN, HIP_L_ROTATION_MAX) | |
hip_rot_r = pymunk.RotaryLimitJoint(torso, upper_leg_r, HIP_R_ROTATION_MIN, HIP_R_ROTATION_MAX) | |
knee_rot_l = pymunk.RotaryLimitJoint(upper_leg_l, lower_leg_l, KNEE_L_ROTATION_MIN, KNEE_L_ROTATION_MAX) | |
knee_rot_r = pymunk.RotaryLimitJoint(upper_leg_r, lower_leg_r, KNEE_R_ROTATION_MIN, KNEE_R_ROTATION_MAX) | |
self.joints.extend([neck_rot, | |
shoulder_rot_l, | |
shoulder_rot_r, | |
elbow_rot_l, | |
elbow_rot_r, | |
hip_rot_l, | |
hip_rot_r, | |
knee_rot_l, | |
knee_rot_r | |
]) | |
# finally done creating the body! phew! | |
# now we just have to create the location at which the ragdoll holds its gun | |
self.right_hand = pymunk.Body() | |
self.right_hand.position = (lower_arm_l.position.x + BODY_THICKNESS/2, lower_arm_l.position.y + LOWER_ARM_LENGTH) | |
self.joints.append(pymunk.PivotJoint(lower_arm_r, self.right_hand, self.right_hand.position)) | |
# now we're done at last! | |
# locate the gun at the character's right hand | |
self.gun = Gun("pistol") | |
self.gun.body.position = self.right_hand.position | |
self.gun_constraints = [pymunk.PivotJoint(self.right_hand, self.gun.body, (0,0), self.gun.handle), # stick 'em together | |
pymunk.RotaryLimitJoint(self.right_hand, self.gun.body, 0, 0) # don't pivot at all | |
] | |
for b in self.bodies: | |
b.velocity_limit = CHARACTER_VELOCITY_LIMIT | |
b.angular_velocity_limit = CHARACTER_ANGULAR_VELOCITY_LIMIT | |
for b in self.body_shapes: | |
b.color = pygame.color.THECOLORS["black"] | |
b.friction = BODY_FRICTION | |
def update(self): | |
self.gun.update() | |
def update_hand_position(self): | |
# body #4 is the left lower arm, don't care about fixing this right now | |
self.right_hand.position = (self.bodies[4].position.x + BODY_THICKNESS/2, self.bodies[4].position.y + LOWER_ARM_LENGTH) | |
def shoot_gun(self): | |
f = pymunk.vec2d.Vec2d(self.gun.force, 0) | |
f.angle = self.gun.body.angle | |
print f | |
self.bodies[4].apply_impulse(f, (BODY_THICKNESS/2, LOWER_ARM_LENGTH)) |
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
from math import pi | |
from pymunk import moment_for_box | |
# velocity limits | |
CHARACTER_VELOCITY_LIMIT = 10 | |
CHARACTER_ANGULAR_VELOCITY_LIMIT = pi/16 | |
GUN_VELOCITY_LIMIT = 10 | |
GUN_ANGULAR_VELOCITY_LIMIT = pi/16 | |
# ragdoll constants | |
BODY_THICKNESS = 2 | |
BODY_FRICTION = 0.5 | |
HEAD_RADIUS = 4 | |
TORSO_LENGTH = 15 | |
UPPER_ARM_LENGTH = 6 | |
LOWER_ARM_LENGTH = 6 | |
UPPER_LEG_LENGTH = 8 | |
LOWER_LEG_LENGTH = 8 | |
HEAD_MASS = 5 | |
TORSO_MASS = 20 | |
UPPER_ARM_MASS = 4 | |
LOWER_ARM_MASS = 3 | |
UPPER_LEG_MASS = 6 | |
LOWER_LEG_MASS = 4 | |
HEAD_MOMENT = moment_for_box(HEAD_MASS, HEAD_RADIUS, HEAD_RADIUS) | |
TORSO_MOMENT = moment_for_box(TORSO_MASS, BODY_THICKNESS, TORSO_LENGTH) | |
UPPER_ARM_MOMENT = moment_for_box(UPPER_ARM_MASS, BODY_THICKNESS, UPPER_ARM_LENGTH) | |
LOWER_ARM_MOMENT = moment_for_box(LOWER_ARM_MASS, BODY_THICKNESS, LOWER_ARM_LENGTH) | |
UPPER_LEG_MOMENT = moment_for_box(UPPER_LEG_MASS, BODY_THICKNESS, UPPER_LEG_LENGTH) | |
LOWER_LEG_MOMENT = moment_for_box(LOWER_LEG_MASS, BODY_THICKNESS, LOWER_LEG_LENGTH) | |
# pymunk angle starts on the negative x axis and increases counterclockwise | |
NECK_ROTATION_MIN = pi - pi/8 | |
NECK_ROTATION_MAX = pi + pi/8 | |
SHOULDER_L_ROTATION_MIN = pi/16 | |
SHOULDER_L_ROTATION_MAX = pi - pi/16 | |
SHOULDER_R_ROTATION_MIN = -pi + pi/16 | |
SHOULDER_R_ROTATION_MAX = -pi/16 | |
ELBOW_L_ROTATION_MIN = 0 | |
ELBOW_L_ROTATION_MAX = pi - pi/16 | |
ELBOW_R_ROTATION_MIN = -pi + pi/16 | |
ELBOW_R_ROTATION_MAX = 0 | |
HIP_L_ROTATION_MIN = -pi/2 | |
HIP_L_ROTATION_MAX = 0 | |
HIP_R_ROTATION_MIN = 0 | |
HIP_R_ROTATION_MAX = pi/2 | |
KNEE_L_ROTATION_MIN = 0 | |
KNEE_L_ROTATION_MAX = pi - pi/16 | |
KNEE_R_ROTATION_MIN = -pi + pi/16 | |
KNEE_R_ROTATION_MAX = 0 | |
# timing constants | |
FPS = 60 | |
STEP_TIME = 1./FPS | |
# collision group enum | |
COLLISION_GROUP = {"wall":1, | |
"character":2 | |
} | |
# misc. constants | |
SCREEN_SIZE = 200 | |
WALL_WIDTH = 2 | |
CROSSHAIRS_SIZE = 3 | |
def flipy(xy): | |
return (xy[0], SCREEN_SIZE-xy[1]) |
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
from globals import GUN_VELOCITY_LIMIT, GUN_ANGULAR_VELOCITY_LIMIT | |
import pygame | |
import pymunk | |
class Gun(pygame.sprite.Sprite): | |
""" Describes a gun held by a Character. | |
For now, this is just a line segment. """ | |
_gun_types = {"pistol":{"length":5, | |
"thickness":1, | |
"mass":1, | |
"force":5, # force exerted upon firing | |
"handle":(5,0) # relative coordinates of the point at which the gun attaches to a Character's hand | |
} | |
} | |
def __init__(self, gun_type="pistol"): | |
# set gun attributes | |
gun_attrs = self._gun_types[gun_type] | |
l = self.length = gun_attrs["length"] | |
t = self.thickness = gun_attrs["thickness"] | |
m = self.mass = gun_attrs["mass"] | |
mo = self.moment = pymunk.moment_for_box(m, l, t) | |
f = self.force = gun_attrs["force"] | |
h = self.handle = gun_attrs["handle"] | |
# pygame init | |
pygame.sprite.Sprite.__init__(self) | |
self.image = pygame.Surface([l, t]) | |
self.rect = self.image.get_rect() | |
# pymunk init | |
self.body = pymunk.Body(m, mo) | |
self.body_shape = pymunk.Poly(self.body, [(0,0), (0,-t), (l,-t), (l,0)]) | |
self.body_shape.color = pygame.color.THECOLORS["black"] | |
self.body.velocity_limit = GUN_VELOCITY_LIMIT | |
self.body.angular_velocity_limit = GUN_ANGULAR_VELOCITY_LIMIT |
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
from Character import Character | |
from globals import SCREEN_SIZE, FPS, STEP_TIME, CROSSHAIRS_SIZE, WALL_WIDTH, \ | |
COLLISION_GROUP, flipy | |
from pymunk import pygame_util | |
import pygame | |
import pymunk | |
pygame.init() | |
clock = pygame.time.Clock() | |
screen = pygame.display.set_mode((SCREEN_SIZE, SCREEN_SIZE)) | |
pygame.mouse.set_visible(0) | |
background = pygame.Surface(screen.get_size()) | |
background = background.convert() | |
background.fill(pygame.color.THECOLORS["white"]) | |
me = Character() | |
space = pymunk.Space() | |
space.gravity = (0.0,-900.0) | |
# add walls | |
walls = [pymunk.Segment(space.static_body, (0,0+WALL_WIDTH), (SCREEN_SIZE, 0+WALL_WIDTH), WALL_WIDTH), # bottom | |
pymunk.Segment(space.static_body, (0,0), (0, SCREEN_SIZE), WALL_WIDTH), # left | |
pymunk.Segment(space.static_body, (0, SCREEN_SIZE), (SCREEN_SIZE, SCREEN_SIZE), WALL_WIDTH), # top | |
pymunk.Segment(space.static_body, (SCREEN_SIZE-WALL_WIDTH, 0), (SCREEN_SIZE-WALL_WIDTH, SCREEN_SIZE), WALL_WIDTH) # right | |
] | |
for w in walls: | |
w.color = pygame.color.THECOLORS["black"] | |
w.friction = 1.0 | |
w.group = COLLISION_GROUP["wall"] | |
space.add(walls) | |
space.add(me.bodies, me.body_shapes, me.joints) | |
space.add(me.gun.body, me.gun.body_shape, me.gun_constraints) | |
# add crosshairs at the location of the mouse | |
pointer_body = pymunk.Body() | |
pointer_shape1 = pymunk.Segment(pointer_body, (0,CROSSHAIRS_SIZE), (0,-CROSSHAIRS_SIZE), 1) # vertical segment | |
pointer_shape2 = pymunk.Segment(pointer_body, (-CROSSHAIRS_SIZE,0), (CROSSHAIRS_SIZE,0), 1) # horizontal segment | |
# add a spring that will angle the gun toward the mouse | |
spring = pymunk.constraint.DampedRotarySpring(me.gun.body, pointer_body, 0, 125000., 6000.) | |
space.add(pointer_shape1, pointer_shape2, spring) | |
while True: | |
# handle event queue | |
for event in pygame.event.get(): | |
if event.type == pygame.MOUSEMOTION: | |
# update location of pointer | |
pointer_body.position = pygame_util.get_mouse_pos(screen) | |
# update angle of pointer | |
pointer_body.angle = (pointer_body.position - me.gun.body.position).angle | |
elif event.type == pygame.MOUSEBUTTONDOWN: | |
me.shoot_gun() | |
# the gun is constrained to the right hand, which is a rogue body | |
me.update_hand_position() | |
# blit background | |
screen.blit(background, (0, 0)) | |
# draw stuff | |
pygame_util.draw_space(screen, space) | |
# update physics | |
space.step(STEP_TIME) | |
# flip display | |
pygame.display.flip() | |
# maintain FPS | |
clock.tick(FPS) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment