Python AngryBirds complete code + explanation

python AngryBirds



Game introduction

Introduction:

This game is a small pygame project based on the 2D engine library pymunk. After downloading and debugging the github project, it has been optimized.

Pictures, music and other resources come from the resource files of angry birds' game. The game can be in the forum https://tieba.baidu.com/p/6492186070 Download.

Introduction to Pymunk Library

pymunk official website

Introduction to Pymunk official website

(the following is a brief introduction to Pymunk from Google translation)

Basics

You will use four basic classes in Pymunk.

  • Rigid body( pymunk.Body)

    Rigid bodies have the physical properties of objects. (mass, position, rotation, speed, etc.) it has no shape itself. If you have used particle physics before, the main difference between rigid bodies is that they can rotate. Rigid bodies usually have a 1:1 correlation with sprites in the game. You should build your game to draw sprites using the position and rotation of rigid bodies.

  • Collision shape( pymunk.Circle,pymunk.Segment and pymunk.Poly)

    By attaching a shape to a solid, you can define the shape of the solid. You can attach multiple shapes to a single solid to define complex shapes, or if you don't need shapes, don't attach any shapes.

  • Constraints / joints (pymunk.constraint.PinJoint, pymunk.constraint.SimpleMotor and many others)

    You can attach constraints between two entities to constrain their behavior, such as maintaining a fixed distance between two entities.

  • Space( pymunk.Space)

    Space is the basic simulation unit in Pymunk. You add solids, shapes, and constraints to the space, and then update the space as a whole. They control how all rigid bodies, shapes, and constraints interact.

The actual simulation is completed by space. After adding the object that should be simulated to spacetime, the pymunk.Space.step() The function moves forward in small steps.

Model your physical objects

Object shape

What you see on the screen is not necessarily the same shape as the actual physical object. Shapes commonly used for collision detection (and other physical simulations) are simplified versions of what is drawn on the screen. Even high-end AAA games separate the collision shape from the shape drawn on the screen.

There are many reasons why it is good to separate the collision shape from the drawn content.

  • Using simpler collision shapes is faster. Therefore, if you have a very complex object, such as a pine tree, it may make sense to simplify its collision shape to a triangle for performance.
  • Using simpler collision shapes can make the simulation better. Suppose you have a stone floor with a small crack in the middle. If you drag a box on this floor, it will get stuck in a crack. However, if you simplify the floor into a plane, you don't have to worry about things getting stuck in cracks.
  • Making the collision shape smaller (or larger) than the actual object will make the game play better. Suppose you have a player controlled ship in a shooting game. Many times, if you think the collision shape should be smaller than the appearance based on it, it will feel more interesting to play.

You can Pymunk Contained using_sprites.py You can see such an example in the example. The physical shape there is a triangle, but it draws three boxes in the pyramid with a snake on the top. Another example is platformer.py Example, where the player is drawn as a red gray girl. However, the physical shape is only a few circles superimposed on each other.

Mass, weight and unit

Sometimes pymunk users may be confused about the definition unit of everything. For example, is the mass of an object grams or kilograms? Pymunk is unitless and doesn't care which unit you use. If you pass seconds to a function of the desired time, your time unit is seconds. If you pass pixels to a function that requires distance, your distance unit is pixels.

Then the derived unit is only a combination of the above. Therefore, in the case of seconds and pixels, the speed unit will be pixels / second.

(this is in contrast to some other physics engines, which can have fixed units that you should use)

Game analysis

Next, let's get to the point. How to call Pymunk to complete this small project?

First of all, we should make a simple analysis of the game:

Then it makes a simple analysis of the process in the game


After analyzing the process, we can probably know that there are such modules

No code empty talk, these are playing hooligans!!!

Now enter the code section hhh

In game object class

Bird

class Bird:
    def __init__(self, distance, angle, x, y, space):
        self.life = 20  # HP 20
        mass = 5        # Quality 5
        radius = 12     # Radius of small circle of rigid force 12
        inertia = pm.moment_for_circle(mass, 0, radius, (0, 0))  # Moment of inertia
        body = pm.Body(mass, inertia)   # Initialize rigid body
        body.position = x, y            # Bird position
        power = distance * 53           # Enlarge the distance, optimize parameters and enhance the game experience
        impulse = power * Vec2d(1, 0)   # momentum
        angle = -angle
        body.apply_impulse_at_local_point(impulse.rotated(angle))   # rotated rotation vector apply_impulse_at_local_point apply pulse at local point
        shape = pm.Circle(body, radius, (0, 0))  # The collision type is easy to calculate
        shape.elasticity = 0.95         # elastic
        shape.friction = 0.9            # friction
        shape.collision_type = 0        # Collision type
        space.add(body, shape)          # Add to 2D plane

        self.body = body
        self.shape = shape

Pig

class Pig:
    def __init__(self, x, y, space):
        self.life = 20
        mass = 5
        radius = 14
        inertia = pm.moment_for_circle(mass, 0, radius, (0, 0))
        body = pm.Body(mass, inertia)
        body.position = x, y
        shape = pm.Circle(body, radius, (0, 0))
        shape.elasticity = 0.95
        shape.friction = 0.9
        shape.collision_type = 1
        space.add(body, shape)
        self.body = body
        self.shape = shape

Polygon

class Polygon:
    def __init__(self, pos, length, height, space, mass=5.0):
        moment = 1000
        body = pm.Body(mass, moment)
        body.position = Vec2d(pos)
        shape = pm.Poly.create_box(body, (length, height))
        shape.color = (0, 0, 255)
        shape.friction = 0.5
        shape.collision_type = 2
        space.add(body, shape)
        self.body = body
        self.shape = shape
        wood = pygame.image.load("E:/py/AngryBirds/images/wood.png").convert_alpha()
        wood2 = pygame.image.load("E:/py/AngryBirds/images/wood2.png").convert_alpha()
        rect = pygame.Rect(251, 357, 86, 22)
        self.beam_image = wood.subsurface(rect).copy()
        rect = pygame.Rect(16, 252, 22, 84)
        self.column_image = wood2.subsurface(rect).copy()

    def to_pygame(self, p):
        """Convert pymunk to pygame coordinates"""
        # pygame right x axis, lower y axis
        return int(p.x), int(-p.y+600)

    def draw_poly(self, element, screen):
        """Draw beams and columns"""
        # Beam transverse column vertical
        poly = self.shape
        ps = poly.get_vertices()
        ps.append(ps[0])
        ps = map(self.to_pygame, ps)
        ps = list(ps)
        color = (255, 0, 0)
        pygame.draw.lines(screen, color, False, ps)
        if element == 'beams':
            p = poly.body.position
            p = Vec2d(self.to_pygame(p))
            angle_degrees = math.degrees(poly.body.angle) + 180
            rotated_logo_img = pygame.transform.rotate(self.beam_image,
                                                       angle_degrees)
            offset = Vec2d(rotated_logo_img.get_size()) / 2.
            p = p - offset
            np = p
            screen.blit(rotated_logo_img, (np.x, np.y))
        if element == 'columns':
            p = poly.body.position
            p = Vec2d(self.to_pygame(p))
            angle_degrees = math.degrees(poly.body.angle) + 180
            rotated_logo_img = pygame.transform.rotate(self.column_image,
                                                       angle_degrees)
            offset = Vec2d(rotated_logo_img.get_size()) / 2.
            p = p - offset
            np = p
            screen.blit(rotated_logo_img, (np.x, np.y))

Tips:

Note that the coordinate systems of pygame and pymunk are different!!!

The following figure is the coordinate diagram of pygame (I'm lazy to draw my own picture, please forgive me)

pymunk's y-axis is up.

Introduction to main modules

Pig bird collision

def post_solve_bird_pig(arbiter, space, _):
    """Collision between bird and pig"""
    surface = screen
    a, b = arbiter.shapes
    bird_body = a.body
    pig_body = b.body
    p = to_pygame(bird_body.position)
    p2 = to_pygame(pig_body.position)
    r = 30
    pygame.draw.circle(surface, BLACK, p, r, 4)
    pygame.draw.circle(surface, RED, p2, r, 4)
    pigs_to_remove = []
    for pig in pigs:
        if pig_body == pig.body:
            pig.life -= 20
            pigs_to_remove.append(pig)
            global score
            score += 10000
    for pig in pigs_to_remove:
        space.remove(pig.shape, pig.shape.body)
        pigs.remove(pig)

Launch birds

if (event.type == pygame.MOUSEBUTTONUP and
        event.button == 1 and mouse_pressed):
    # Release new bird
    mouse_pressed = False
    if level.number_of_birds > 0:
        song_fly = 'E:/py/AngryBirds/music/bird 01 flying.wav'
        pygame.mixer.music.load(song_fly)
        pygame.mixer.music.play(0)

        level.number_of_birds -= 1
        t1 = time.time() * 1000
        xo = 154
        yo = 156
        if mouse_distance > rope_length:
            mouse_distance = rope_length
        if x_mouse < sling_x + 5:
            bird = Bird(mouse_distance, angle, xo, yo, space)
            birds.append(bird)
        else:
            bird = Bird(-mouse_distance, angle, xo, yo, space)
            birds.append(bird)
        if level.number_of_birds == 0:
            t2 = time.time()

The rest is seen in the code (mainly because the writing is a little messy, it doesn't look good and funny)

Complete code

The complete code will be uploaded to github in the afternoon

Keywords: Game Development pygame

Added by lillyapps on Wed, 02 Feb 2022 05:54:18 +0200