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
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