Skip to content

Instantly share code, notes, and snippets.

@desophos
Created January 6, 2013 22:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save desophos/4470807 to your computer and use it in GitHub Desktop.
Save desophos/4470807 to your computer and use it in GitHub Desktop.
The beginnings of a ragdoll game.
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))
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])
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
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