[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] PyODE



Oops heres the example.
import pygame
import sys
########################################################################
import operator
import math
 
########################################################################
 
class vec2d(object):
 
    def __init__(self, x_or_pair, y = None):
        if y == None:
            try:
                self.vec = [x_or_pair[0],x_or_pair[1]]
            except TypeError:
                raise TypeError("vec2d constructor requires a tuple or"
                    " two arguments")
        else:
            self.vec = [x_or_pair,y]
 
    def get_x(self):
        return self.vec[0]
    def set_x(self, value):
        self.vec[0] = value
    x = property(get_x, set_x)
    
    def get_y(self):
        return self.vec[1]
    def set_y(self, value):
        self.vec[1] = value
    y = property(get_y, set_y)
    
    def set(self, x, y):
        self.vec[0] = x
        self.vec[1] = y
        
    # String representaion (for debugging)
    def __repr__(self):
        return 'vec2d(%s, %s)' % (self.x, self.y)
    
    # Array-style access
    def __len__(self): return 2
 
    def __getitem__(self, key):
        return self.vec[key]
 
    def __setitem__(self, key, value):
        self.vec[key] = value
 
    # Comparison
    def __eq__(self, other):
        return self.vec[0] == other[0] and self.vec[1] == other[1]
    
    def __ne__(self, other):
        return self.vec[0] != other[0] or self.vec[1] != other[1]
 
    def __nonzero__(self):
        return self.vec[0] or self.vec[1]
 
    # Generic operator handlers
    def _o2(self, other, f):
        "Any two-operator operation where the left operand is a vec2d"
        try:
            return vec2d(f(self.vec[0], other[0]),
                         f(self.vec[1], other[1]))
        except TypeError:
            return vec2d(f(self.vec[0], other),
                         f(self.vec[1], other))
 
    def _r_o2(self, other, f):
        "Any two-operator operation where the right operand is a vec2d"
        try:
            return vec2d(f(other[0], self.vec[0]),
                         f(other[1], self.vec[1]))
        except TypeError:
            return vec2d(f(other, self.vec[0]),
                         f(other, self.vec[1]))
 
    def _o1(self, f):
        "Any unary operation on a vec2d"
        return vec2d(f(self.vec[0]), f(self.vec[1]))
 
    # Addition
    def __add__(self, other):
        return self._o2(other, operator.add)
    __radd__ = __add__
 
    # Subtraction
    def __sub__(self, other):
        return self._o2(other, operator.sub)
    def __rsub__(self, other):
        return self._r_o2(other, operator.sub)
 
    # Multiplication
    def __mul__(self, other):
        return self._o2(other, operator.mul)
    __rmul__ = __mul__
 
    # Division
    def __div__(self, other):
        return self._o2(other, operator.div)
    def __rdiv__(self, other):
        return self._r_o2(other, operator.div)
 
    def __floordiv__(self, other):
        return self._o2(other, operator.floordiv)
    def __rfloordiv__(self, other):
        return self._r_o2(other, operator.floordiv)
 
    def __truediv__(self, other):
        return self._o2(other, operator.truediv)
    def __rtruediv__(self, other):
        return self._r_o2(other, operator.truediv)
 
    # Modulo
    def __mod__(self, other):
        return self._o2(other, operator.mod)
    def __rmod__(self, other):
        return self._r_o2(other, operator.mod)
 
    def __divmod__(self, other):
        return self._o2(other, operator.divmod)
    def __rdivmod__(self, other):
        return self._r_o2(other, operator.divmod)
 
    # Exponentation
    def __pow__(self, other):
        return self._o2(other, operator.pow)
    def __rpow__(self, other):
        return self._r_o2(other, operator.pow)
 
    # Bitwise operators
    def __lshift__(self, other):
        return self._o2(other, operator.lshift)
    def __rlshift__(self, other):
        return self._r_o2(other, operator.lshift)
 
    def __rshift__(self, other):
        return self._o2(other, operator.rshift)
    def __rrshift__(self, other):
        return self._r_o2(other, operator.rshift)
 
    def __and__(self, other):
        return self._o2(other, operator.and_)
    __rand__ = __and__
 
    def __or__(self, other):
        return self._o2(other, operator.or_)
    __ror__ = __or__
 
    def __xor__(self, other):
        return self._o2(other, operator.xor)
    __rxor__ = __xor__
 
    # Unary operations
    def __neg__(self):
        return self._o1(operator.neg)
 
    def __pos__(self):
        return self._o1(operator.pos)
 
    def __abs__(self):
        return self._o1(operator.abs)
 
    def __invert__(self):
        return self._o1(operator.invert)
 
    # vectory functions
    def get_length_sqrd(self): 
        return self.vec[0]**2 + self.vec[1]**2
 
    def get_length(self):
        return math.sqrt(self.vec[0]**2 + self.vec[1]**2)    
    def __setlength(self, value):
        self.normalize_return_length()
        self.vec[0] *= value
        self.vec[1] *= value
    length = property(get_length, __setlength, None,
        "gets or sets the magnitude of the vector")
       
    def rotate(self, angle_degrees):
        radians = math.radians(angle_degrees)
        cos = math.cos(radians)
        sin = math.sin(radians)
        x = self.vec[0]*cos - self.vec[1]*sin
        y = self.vec[0]*sin + self.vec[1]*cos
        self.vec[0] = x
        self.vec[1] = y
    
    def get_angle(self):
        if (self.get_length_sqrd() == 0):
            return 0
        return math.degrees(math.atan2(self.vec[1], self.vec[0]))
 
    def get_angle_between(self, other):
        cross = self.vec[0]*other[1] - self.vec[1]*other[0]
        dot = self.vec[0]*other[0] + self.vec[1]*other[1]
        return math.degrees(math.atan2(cross, dot))
    
    def __setangle(self, angle_degrees):
        self.vec[0] = self.length
        self.vec[1] = 0
        self.rotate(angle_degrees)
    angle = property(get_angle, __setangle, None,
        "gets or sets the angle of a vector")
        
    def normalized(self):
        length = self.length
        if length != 0:
            return self/length
        return vec2d(self)
 
    def perpendicular(self):
        return vec2d(self.vec[1], -self.vec[0])
    
    def perpendicular_normal(self):
        length = self.length
        if length != 0:
            return vec2d(self.vec[1]/length, -self.vec[0]/length)
        return vec2d(self)
        
    def normalize_return_length(self):
        length = self.length
        if length != 0:
            self.vec[0] /= length
            self.vec[1] /= length
        return length
 
    def dot(self, other):
        return self.vec[0]*other[0] + self.vec[1]*other[1]
        
    def get_distance(self, other):
        return math.sqrt((self.vec[0] - other[0])**2 +
            (self.vec[1] - other[1])**2)
        
    def projection(self, other):
        normal = other.normalized()
        projected_length = self.dot(normal)
        return normal*projected_length
    
    def cross(self, other):
        return self.vec[0]*other[1] - self.vec[1]*other[0]
    
    def interpolate_to(self, other, range):
        return vec2d(self.vec[0] + (other.vec[0] - self.vec[0])*range,
            self.vec[1] + (other.vec[1] - self.vec[1])*range)
    
    def convert_to_basis(self, x_vector, y_vector):
        return vec2d(self.dot(x_vector)/x_vector.get_length_sqrd(),
            self.dot(y_vector)/y_vector.get_length_sqrd())
pygame.init()

screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
background = pygame.Surface((640, 480))

terrain = pygame.sprite.Group()

gravity = vec2d(0, -0.2)
bounciness = 0.2
friction = 0

def origin(coord):
    return coord[0], 480 - coord[1]

def squareCollideTriangle(square, triangle):
    assert isinstance(square, Square)
    assert isinstance(triangle, RightTriangle)
    
    squareVertices = vec2d(origin(square.rect.topright)), vec2d(origin(square.rect.topleft)), vec2d(origin(square.rect.bottomleft)), vec2d(origin(square.rect.bottomright))
    triangleVertices = None
    
    triangleNormal = None
    squareNormal = [vec2d(-1, 0), vec2d(1, 0), vec2d(0, -1), vec2d(0, 1)]
    if triangle.orientation == "bottomright":
        triangleNormal = [vec2d(1, 0), vec2d(0, -1), triangle.hypotenuseNormal]
        triangleVertices = vec2d(origin(triangle.rect.bottomleft)),vec2d( origin(triangle.rect.bottomright)), vec2d(origin(triangle.rect.topright))
    elif triangle.orientation == "bottomleft":
        triangleNormal = [vec2d(-1, 0), vec2d(0, -1), triangle.hypotenuseNormal]
        triangleVertices = vec2d(origin(triangle.rect.bottomleft)), vec2d(origin(triangle.rect.topleft)), vec2d(origin(triangle.rect.bottomright))
    elif triangle.orientation == "topleft":
        triangleNormal = [vec2d(-1, 0), vec2d(0, 1), triangle.hypotenuseNormal]
        triangleVertices = vec2d(origin(triangle.rect.topleft)), vec2d(origin(triangle.rect.bottomleft)), vec2d(origin(triangle.rect.topright))
    elif triangle.orientation == "topright":
        triangleNormal = [vec2d(1, 0), vec2d(0, 1), triangle.hypotenuseNormal]
        triangleVertices = vec2d(origin(triangle.rect.topright)), vec2d(origin(triangle.rect.topleft)), vec2d(origin(triangle.rect.bottomright))
    
    pushVectors = []
    
    for n in triangleNormal:
        separate, push = axisSeparatePolygons(n, squareVertices, triangleVertices)
        if separate:
            return False, None
        pushVectors.append(push)
        
    #for n in squareNormal:
        #separate, push = axisSeparatePolygons(n, squareVertices, triangleVertices)
        #if separate:
            #return False, None
        #pushVectors.append(push)
    
    mtd = findMTD(pushVectors)
    
    d = vec2d(square.rect.centerx - triangle.rect.centerx, (480 - square.rect.centery) - (480 - triangle.rect.centery))
    if (d.dot(mtd) < 0):
        mtd = -mtd
    
    return True, (mtd.x, -mtd.y)
    
    
    

def findMTD(pushVectors):
    mtd = pushVectors[0]
    mind2 = pushVectors[0].dot(pushVectors[0])
    
    for v in pushVectors:
        d2 = v.dot(v)

        if (d2 < mind2):
            mind2 = d2
            mtd = v
    
    return mtd
def calculateInterval(normal, points):
    dot = normal.dot(points[0])
    min, max = dot, dot
    for p in points:
        dot = p.dot(normal)
        if dot < min:
            min = dot
        else:
            if dot > max:
                max = dot
    
    return min, max

def axisSeparatePolygons(normal, pointsA, pointsB):
    minA, maxA = calculateInterval(normal, pointsA)
    minB, maxB = calculateInterval(normal, pointsB)
    
    if minA > maxB or minB > maxA:
        return True, None
    
    depth1 = maxA - minB
    depth2 = maxB - minA
    
    depth = 0
    if depth1 < depth2:
        depth = depth1
    else:
        depth = depth2
        
    return False, normal * depth
        

    
        
class Square(pygame.sprite.Sprite):
    def __init__(self, rect, fillColour):
        pygame.sprite.Sprite.__init__(self)
        self.rect = rect
        self.image = pygame.Surface(rect.size)
        self.image.fill(fillColour)
        
        self.touchingGround = False
        
    def collideSquare(self, otherSquare):
        otherRect = otherSquare.rect
        rect = self.rect
        
        overlapX = 0
        overlapY = 0
        
        if rect.centerx < otherRect.centerx:
            overlapX = rect.right - otherRect.left
            if overlapX <= 0:
                return False, None
        else:
            overlapX = rect.left - otherRect.right
            if overlapX >= 0:
                return False, None
        
        if rect.centery > otherRect.centery:
            overlapY = rect.top - otherRect.bottom
            if overlapY >= 0:
                return False, None
        else:
            overlapY = rect.bottom - otherRect.top
            if overlapY <= 0:
                return False, None
            
        
        if abs(overlapX) < abs(overlapY):
            return True, (-overlapX, 0)
        else:
            return True, (0, -overlapY)
            

    

        
class SquareTerrain(Square):
    def __init__(self, center):
        dimensions = (40, 90)
        Square.__init__(self, center, dimensions, (0, 255, 0))

class RightTriangle(pygame.sprite.Sprite):
    def __init__(self, rect, fillColour=(255, 255, 255), orientation="bottomleft"):
        
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface(rect.size)
        self.image.set_colorkey((0, 0, 0))
        self.rect = rect
        self.orientation = orientation
        dimensions = rect.size
        if orientation == "bottomleft":
            pointlist = [(0, dimensions[1]), (0, 0), (dimensions[0], dimensions[1]), (0, dimensions[1])]
            self.hypotenuseVector = vec2d(-self.rect.width, self.rect.height)
            self.hypotenuseNormal = self.hypotenuseVector.perpendicular_normal()
        elif orientation == "bottomright":
            pointlist = [(dimensions[0], dimensions[1]), (0, dimensions[1]), (dimensions[0], 0), (dimensions[0], dimensions[1])]
            self.hypotenuseVector = vec2d(-self.rect.width, -self.rect.height)
            self.hypotenuseNormal = self.hypotenuseVector.perpendicular_normal()
        elif orientation == "topleft":
            pointlist = [(0, 0), (0, dimensions[1]), (dimensions[0], 0), (0, 0)]
        elif orientation == "topright":
            pointlist = [(dimensions[0], 0), (0, 0), (0, dimensions[1]), (dimensions[0], 0)]
    
        pygame.draw.polygon(self.image, fillColour, pointlist)

class Player(Square):
    def __init__(self, center):
        dimensions = (25, 25)
        rect = pygame.Rect((0, 0), dimensions)
        rect.center = center
        Square.__init__(self, rect, (255, 0, 0))
    
        self.velocity = vec2d(0, 0)
        self.acceleration = vec2d(0, 0)
        
    def update(self):
        self.rect.move_ip(self.velocity.x, -self.velocity.y)
        self.velocity += gravity
        self.velocity += self.acceleration
        
        self.acceleration.x = 0; self.acceleration.y = 0
        

                    
        keysPressed = pygame.key.get_pressed()
    
        if keysPressed[pygame.K_DOWN]:
            self.moveDown()
        elif keysPressed[pygame.K_UP]:
            if self.touchingGround:
                self.moveUp()
            
        if keysPressed[pygame.K_LEFT]:
            self.moveLeft()
        elif keysPressed[pygame.K_RIGHT]:
            self.moveRight()
            
        for t in terrain:
            if isinstance(t, RightTriangle):
                collision, overlap = squareCollideTriangle(self, t)
                if collision:
                    self.rect.move_ip(overlap)
                    self.collisionResponse(vec2d(overlap[0], -overlap[1]))
                    self.touchingGround = True
                    
            elif isinstance(t, Square):
                collision, overlap = self.collideSquare(t)
                if collision:
                    self.rect.move_ip(overlap)
                    self.collisionResponse(vec2d(overlap[0], -overlap[1]))
                    self.touchingGround = True


        

    
    def collisionResponse(self, normal):
        normal = normal.normalized()
        #vn = self.velocity.dot(normal) * normal
        #vt = self.velocity - vn
        #self.velocity = vt * (1 - friction) + vn * -(bounciness)
        
        self.velocity = self.velocity - (((1 + bounciness) * self.velocity.dot(normal)) * normal)
    
            
    def moveLeft(self):
        self.acceleration += vec2d(-0.5, 0)
        #self.rect.move_ip(-10, 0)
    def moveRight(self):
        self.acceleration += vec2d(0.5, 0)
        #self.rect.move_ip(10, 0)
    
    def moveUp(self):
        self.acceleration += vec2d(0, 5)
        self.touchingGround = False
        #self.rect.move_ip(0, -10)
    def moveDown(self):
        #self.rect.move_ip((0, 10))
        pass
    
sprites = pygame.sprite.RenderUpdates()

playerSquare = Player((320, 240))

#setup terrain
tRect1 = pygame.Rect((0, 0), (100, 300))
terrain.add(RightTriangle(tRect1, (0, 255, 0), "bottomleft"))
tRect2 = pygame.Rect(tRect1.bottomright, (100, 100))
terrain.add(RightTriangle(tRect2, (0, 255, 0), "bottomleft"))
tRect3 = pygame.Rect(tRect2.bottomright, (400, 100))
terrain.add(Square(tRect3, (0, 255, 0)))
tRect4 = pygame.Rect((0, 0), (25, 25))
tRect4.bottomleft = tRect3.topright
terrain.add(RightTriangle(tRect4, (0, 255, 0), "bottomright"))
tRect5 = pygame.Rect((0, 0), (25, tRect4.top))
tRect5.bottomleft = tRect4.topright
terrain.add(Square(tRect5, (0, 255, 0)))
tRect6 = pygame.Rect((0, 0), (640, 25))
terrain.add(Square(tRect6,(0, 255, 0)))

#filler
tRect7 = pygame.Rect(tRect1.bottomleft, (100, 180))
terrain.add(Square(tRect7, (0, 255, 0)))
tRect8 = pygame.Rect(tRect2.bottomleft, (100, 400))
terrain.add(Square(tRect8, (0, 255, 0)))
tRect9 = pygame.Rect(tRect4.topright, (200, 400))
terrain.add(Square(tRect9, (0, 255, 0)))
tRect10 = pygame.Rect(tRect4.bottomleft, (100, 200))
terrain.add(Square(tRect10, (0, 255, 0)))
sprites.add(playerSquare)
sprites.add(terrain)

def main():
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return
            
        screen.fill((0, 0, 0))
        sprites.update()
        dirtyRects = sprites.draw(screen)
        pygame.display.flip()
        
        clock.tick(40)
        
if __name__ == "__main__":
    main()