ThePythonGameBook

learn Python. Create Games

User Tools

Site Tools


en:pygame:step017

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
en:pygame:step017 [2020/05/03 21:35]
Horst JENS ↷ Links adapted because of a move operation
en:pygame:step017 [2020/05/15 22:50] (current)
Horst JENS
Line 252: Line 252:
 View/​Edit/​Download the file directly in Github: https://​github.com/​horstjens/​ThePythonGameBook/​blob/​master/​pygame/​017_turning_and_physic.py View/​Edit/​Download the file directly in Github: https://​github.com/​horstjens/​ThePythonGameBook/​blob/​master/​pygame/​017_turning_and_physic.py
  
-//click reload in your browser if you see no code here:// +<code python>​ 
-<​html>​ +#!/usr/bin/env python 
- <script src="​http://​gist-it.appspot.com/github/​horstjens/​ThePythonGameBook/​blob/​master/​pygame/​017_turning_and_physic.py">​ +# -*- coding: utf-8 -*- 
- </script><​/html>+"""​ 
 +017_turning_and_physic.py 
 +pygame sprites primitive physic (elastic collision) 
 +url: http://thepythongamebook.com/en:part2:pygame:step017 
 +author: horst.jens@spielend-programmieren.at 
 +physic by Leonard Michlmayr 
 +licence: gpl, see http://www.gnu.org/​licenses/​gpl.html
  
-^ [[:​en:​pygame:​step016| ← previous]] ^ [[en:​pygame:​start| ↑ Overview]] ^ [[:​en:​pygame:​step018| → next ]] ^+move the big bird around with the keys w,​a,​s,​d ​ and q and e 
 +fire with space, toggle gravity with g
  
-====== comment this page ====== +works with pyhton3.4 and python2.7 
-~~DISQUS~~+"""​ 
 +#the next line is only needed for python2.x and not necessary for python3.x 
 +from __future__ import print_function,​ division
  
 +import menu1
 +
 +
 +def game(folder = "​data"​):​
 +    import pygame
 +    import os
 +    import random
 +    import math 
 +    #------ starting pygame -------------
 +    pygame.mixer.pre_init(44100,​ -16, 2, 2048) # setup mixer to avoid sound lag
 +    pygame.init()
 +    screen=pygame.display.set_mode((640,​480)) # try out larger values and see what happens !
 +    #winstyle = 0  # |FULLSCREEN # Set the display mode
 +    print("​pygame version",​ pygame.ver)
 +    # ------- game constants ----------------------
 +    BIRDSPEEDMAX = 200
 +    FRAGMENTMAXSPEED = 200
 +    FRICTION =.991  # between 1 and 0. 1 means no friction at all (deep space)
 +    FORCE_OF_GRAVITY = 2.81 # pixel per second square earth: 9.81 m/s²
 +    GRAD = math.pi / 180 # 2 * pi / 360   # math module needs Radiant instead of Grad
 +    # ----------- functions -----------
 +    def write(msg="​pygame is cool", color=(0,​0,​0)):​
 +        """​write text into pygame surfaces"""​
 +        myfont = pygame.font.SysFont("​None",​ 32)
 +        mytext = myfont.render(msg,​ True, color)
 +        mytext = mytext.convert_alpha()
 +        return mytext
 +    def getclassname(class_instance):​
 +        """​this function extract the class name of a class instance.
 +        For an instance of a XWing class, it will return '​XWing'​."""​
 +        text = str(class_instance.__class__) # like "<​class '​__main__.XWing'>"​
 +        parts = text.split("​."​) # like ["<​class '​__main__","​XWing'>"​]
 +        return parts[-1][0:​-2] # from the last (-1) part, take all but the last 2 chars
 +    ​
 +    def elastic_collision(sprite1,​ sprite2):
 +        """​elasitc collision between 2 sprites (calculated as disc'​s).
 +           The function alters the dx and dy movement vectors of both sprites.
 +           The sprites need the property .mass, .radius, .pos[0], .pos[1], .dx, dy
 +           ​pos[0] is the x postion, pos[1] the y position"""​
 +        # here we do some physics: the elastic
 +        # collision
 +        #
 +        # first we get the direction of the push.
 +        # Let's assume that the sprites are disk
 +        # shaped, so the direction of the force is
 +        # the direction of the distance.
 +        dirx = sprite1.pos[0] - sprite2.pos[0]
 +        diry = sprite1.pos[1] - sprite2.pos[1]
 +        #
 +        # the velocity of the centre of mass
 +        sumofmasses = sprite1.mass + sprite2.mass
 +        sx = (sprite1.dx * sprite1.mass + sprite2.dx * sprite2.mass) / sumofmasses
 +        sy = (sprite1.dy * sprite1.mass + sprite2.dy * sprite2.mass) / sumofmasses
 +        # if we sutract the velocity of the centre
 +        # of mass from the velocity of the sprite,
 +        # we get it's velocity relative to the
 +        # centre of mass. And relative to the
 +        # centre of mass, it looks just like the
 +        # sprite is hitting a mirror.
 +        #
 +        bdxs = sprite2.dx - sx
 +        bdys = sprite2.dy - sy
 +        cbdxs = sprite1.dx - sx
 +        cbdys = sprite1.dy - sy
 +        # (dirx,diry) is perpendicular to the mirror
 +        # surface. We use the dot product to
 +        # project to that direction.
 +        distancesquare = dirx * dirx + diry * diry
 +        if distancesquare == 0:
 +            # no distance? this should not happen,
 +            # but just in case, we choose a random
 +            # direction
 +            dirx = random.randint(0,​11) - 5.5
 +            diry = random.randint(0,​11) - 5.5
 +            distancesquare = dirx * dirx + diry * diry
 +        dp = (bdxs * dirx + bdys * diry) # scalar product
 +        dp /= distancesquare # divide by distance * distance.
 +        cdp = (cbdxs * dirx + cbdys * diry)
 +        cdp /= distancesquare
 +        # We are done. (dirx * dp, diry * dp) is
 +        # the projection of the velocity
 +        # perpendicular to the virtual mirror
 +        # surface. Subtract it twice to get the
 +        # new direction.
 +        #
 +        # Only collide if the sprites are moving
 +        # towards each other: dp > 0
 +        if dp > 0:
 +            sprite2.dx -= 2 * dirx * dp 
 +            sprite2.dy -= 2 * diry * dp
 +            sprite1.dx -= 2 * dirx * cdp 
 +            sprite1.dy -= 2 * diry * cdp
 +    # ----------- classes ------------------------
 +    class Text(pygame.sprite.Sprite):​
 +        """​a pygame Sprite displaying text"""​
 +        def __init__(self,​ msg="​The Python Game Book", color=(0,​0,​0)):​
 +            self.groups = allgroup
 +            self._layer = 1
 +            pygame.sprite.Sprite.__init__(self,​ self.groups)
 +            self.newmsg(msg,​color)
 +            ​
 +        def update(self,​ time):
 +            pass # allgroup sprites need update method that accept time
 +        ​
 +        def newmsg(self,​ msg, color=(0,​0,​0)):​
 +            self.image =  write(msg,​color)
 +            self.rect = self.image.get_rect()
 +
 +    class Lifebar(pygame.sprite.Sprite):​
 +        """​shows a bar with the hitpoints of a Bird sprite
 +           with a given bossnumber, the Lifebar class can 
 +           ​identify the boos (Bird sprite) with this codeline:
 +           ​Bird.birds[bossnumber] """​
 +        def __init__(self,​ boss):
 +            self.groups = allgroup
 +            self.boss = boss
 +            self._layer = self.boss._layer
 +            pygame.sprite.Sprite.__init__(self,​ self.groups)
 +            self.oldpercent = 0
 +            self.paint()
 +            ​
 +        def paint(self):​
 +            self.image = pygame.Surface((self.boss.rect.width,​7))
 +            self.image.set_colorkey((0,​0,​0)) # black transparent
 +            pygame.draw.rect(self.image,​ (0,255,0), (0,​0,​self.boss.rect.width,​7),​1)
 +            self.rect = self.image.get_rect()
 + 
 +        def update(self,​ time):
 +            self.percent = self.boss.hitpoints / self.boss.hitpointsfull * 1.0
 +            if self.percent != self.oldpercent:​
 +                self.paint() # important ! boss.rect.width may have changed (because rotating)
 +                pygame.draw.rect(self.image,​ (0,0,0), (1,​1,​self.boss.rect.width-2,​5)) # fill black
 +                pygame.draw.rect(self.image,​ (0,255,0), (1,1,
 +                                 ​int(self.boss.rect.width * self.percent),​5),​0) # fill green
 +            self.oldpercent = self.percent
 +            self.rect.centerx = self.boss.rect.centerx
 +            self.rect.centery = self.boss.rect.centery - self.boss.rect.height /2 - 10
 +            if self.boss.hitpoints < 1:   #​check if boss is still alive
 +                self.kill() # kill the hitbar
 +       
 +    class Bird(pygame.sprite.Sprite):​
 +        """​generic Bird class, to be called from SmallBird and BigBird"""​
 +        image=[] ​ # list of all images
 +        birds = {} # a dictionary of all Birds, each Bird has its own number
 +        number = 0  ​
 +        waittime = 1.0 # seconds
 +        def __init__(self,​ layer = 4, bigbird = False ):
 +            self.groups = birdgroup, allgroup, gravitygroup # assign groups ​
 +            self._layer = layer                   # assign level
 +            #self.layer = layer
 +            pygame.sprite.Sprite.__init__(self, ​ self.groups ​ ) #call parent class. NEVER FORGET !
 +            self.pos = [random.randint(50,​screen.get_width()-50),​
 +                        random.randint(25,​screen.get_height()-25)] ​
 +            self.area = screen.get_rect()
 +            self.image = Bird.image[2]
 +            self.image = Bird.image[0]
 +            self.hitpointsfull = float(30) # maximal hitpoints
 +            self.hitpoints = float(30) # actual hitpoints
 +            self.rect = self.image.get_rect()
 +            self.radius = max(self.rect.width,​ self.rect.height) / 2.0
 +            self.dx = 0   # wait at the beginning
 +            self.dy = 0            ​
 +            self.waittime = Bird.waittime # 1.0 # one second
 +            self.lifetime = 0.0
 +            self.waiting = True
 +            self.rect.center = (-100,-100) # out of visible screen
 +            self.crashing = False
 +            self.frags = 25 # number of framgents if Bird is killed
 +            self.number = Bird.number # get my personal Birdnumber
 +            Bird.number+= 1           # increase the number for next Bird
 +            Bird.birds[self.number] = self # store myself into the Bird dictionary
 +            print("​my number %i Bird number %i and i am a %s " % (self.number,​ Bird.number,​ getclassname(self)))
 +            self.mass = 100.0
 +            self.angle = 0.0
 +            self.boostspeed = 10 # speed to fly upward
 +            self.boostmax = 0.9 # max seconds of "​fuel"​ for flying upward
 +            self.boostmin = 0.4 # min seconds of "​fuel"​ for flying upward
 +            self.boosttime = 0.0 # time (fuel) remaining
 +            warpsound.play()
 +            for _ in range(8):
 +                BlueFragment(self.pos) # blue Frags
 +      ​
 +        def kill(self):
 +            # a shower of red fragments, exploding outward
 +            for _ in range(self.frags):​
 +                RedFragment(self.pos)
 +            pygame.sprite.Sprite.kill(self) # kill the actual Bird 
 +            ​
 +        def speedcheck(self):​
 +            #if abs(self.dx) > BIRDSPEEDMAX:​
 +            #   ​self.dx = BIRDSPEEDMAX * (self.dx/​abs(self.dx)) # dx/abs(dx) is 1 or -1
 +            #if abs(self.dy) > BIRDSPEEDMAX:​
 +            #   ​self.dy = BIRDSPEEDMAX * (self.dy/​abs(self.dy))
 +            if abs(self.dx) > 0 : 
 +                self.dx *= FRICTION ​ # make the Sprite slower over time
 +            if abs(self.dy) > 0 :
 +                self.dy *= FRICTION
 +
 +        def areacheck(self):​
 +            if not self.area.contains(self.rect):​
 +                self.crashing = True # change colour later
 +                # --- compare self.rect and area.rect
 +                if self.pos[0] + self.rect.width/​2 > self.area.right:​
 +                    self.pos[0] = self.area.right - self.rect.width/​2
 +                    self.dx *= -0.5 # bouncing off but loosing speed
 +                if self.pos[0] - self.rect.width/​2 < self.area.left:​
 +                    self.pos[0] = self.area.left + self.rect.width/​2
 +                    self.dx *= -0.5 # bouncing off the side but loosing speed
 +                if self.pos[1] + self.rect.height/​2 > self.area.bottom:​
 +                    self.pos[1] = self.area.bottom - self.rect.height/​2
 +                    #self.dy *= -1 # bouncing off the ground
 +                    #if reaching the bottom, the birds get a boost and fly upward to the sky
 +                    #at the bottom the bird "​refuel"​ a random amount of "​fuel"​ (the boostime)
 +                    self.dy = 0 # break at the bottom
 +                    self.dx *= 0.3 # x speed is reduced at the ground
 +                    self.boosttime = self.boostmin + random.random()* (self.boostmax - self.boostmin)
 +                if self.pos[1] - self.rect.height/​2 < self.area.top:​
 +                    self.pos[1] = self.area.top + self.rect.height/​2
 +                    self.dy = 0 # stop when reaching the sky
 +                    #self.dy *= -1 
 +                    self.hitpoints -= 1 # reaching the sky cost 1 hitpoint
 +        def update(self,​ seconds):
 +            #---make Bird only visible after waiting time
 +            self.lifetime += seconds
 +            if self.lifetime > (self.waittime):​
 +                self.waiting = False
 +            if self.waiting:​
 +                self.rect.center = (-100,-100)
 +            else: # the waiting time (Blue Fragments) is over
 +                if self.boosttime > 0:   # boost flying upwards ?
 +                    self.boosttime -= seconds
 +                    self.dy -= self.boostspeed # upward is negative y !
 +                    self.ddx = -math.sin(self.angle*GRAD) ​
 +                    self.ddy = -math.cos(self.angle*GRAD) ​
 +                    Smoke(self.rect.center,​ -self.ddx , -self.ddy )
 +                self.speedcheck() ​   # ------------- movement
 +                self.pos[0] += self.dx * seconds
 +                self.pos[1] += self.dy * seconds
 +                self.areacheck() # ------- check if Bird out of screen
 +                #--- calculate actual image: crasing, bigbird, both, nothing ?
 +                self.image = Bird.image[self.crashing+self.big] # 0 for not crashing, 1 for crashing
 +                self.image0 = Bird.image[self.crashing+self.big] # 0 for not crashing, 1 for crashing
 +                #--------- rotate into direction of movement ------------
 +                self.angle = math.atan2(-self.dx,​ -self.dy)/​math.pi*180.0 ​
 +                self.image = pygame.transform.rotozoom(self.image0,​self.angle,​1.0)
 +                #--- calculate new position on screen -----
 +                self.rect.centerx = round(self.pos[0],​0)
 +                self.rect.centery = round(self.pos[1],​0)
 +                if self.hitpoints <= 0:
 +                    self.kill()
 +    ​
 +    class SmallBird(Bird):​
 +        """​A bird that get pushed around by shots, red fragments and other birds."""​
 +        #gravity = True
 +        def __init__(self):​
 +            self.big = 0
 +            Bird.__init__(self)
 +            Lifebar(self)
 +        ​
 +        def kill(self):
 +            crysound.play()
 +            Bird.kill(self)
 +    ​
 +    class BigBird(Bird):​
 +        """​A big bird controlled by the player"""​
 +        def __init__(self):​
 +            self.big = 2 # smallsprites have the value 0 for this attribute (.big) -> important for Bird.image
 +            Bird.__init__(self) # create a "​little"​ Bird but do more than that
 +            self.hitpoints = float(100)
 +            self.hitpointsfull = float(100)
 +            self.image = Bird.image[2] # big bird image
 +            self.pos = [screen.get_width()/​2,​ screen.get_height()/​2]
 +            self.rect = self.image.get_rect()
 +            self.angle = 0
 +            self.speed = 20.0 # base movement speed factor
 +            self.rotatespeed = 1.0 # rotating speed
 +            self.frags = 100
 +            Lifebar(self)
 +            self.cooldowntime = 0.08 #seconds
 +            self.cooldown = 0.0
 +            self.damage = 5 # how many damage one bullet inflict
 +            self.shots = 0
 +            self.radius = self.image.get_width() / 2.0
 +            self.mass = 400.0
 +        ​
 +        def kill(self):
 +            bombsound.play()
 +            Bird.kill(self)
 +            ​
 +        def update(self,​ time):
 +            """​BigBird has its own update method, overwriting the 
 +               ​update method from the Bird class"""​
 +            self.lifetime += seconds
 +            if self.lifetime > (self.waittime):​
 +                self.waiting = False
 +            if self.waiting:​
 +                self.rect.center = (-100,-100)
 +            else:
 +                #--- calculate actual image: crasing, bigbird, both, nothing ?
 +                self.image = Bird.image[self.crashing+self.big] # 0 for not crashing, 2 for big
 +                pressedkeys = pygame.key.get_pressed()
 +                self.ddx = 0.0
 +                self.ddy = 0.0
 +                if pressedkeys[pygame.K_w]:​ # forward
 +                         ​self.ddx = -math.sin(self.angle*GRAD) ​
 +                         ​self.ddy = -math.cos(self.angle*GRAD) ​
 +                         ​Smoke(self.rect.center,​ -self.ddx , -self.ddy )
 +                if pressedkeys[pygame.K_s]:​ # backward
 +                         ​self.ddx = +math.sin(self.angle*GRAD) ​
 +                         ​self.ddy = +math.cos(self.angle*GRAD) ​
 +                         ​Smoke(self.rect.center,​ -self.ddx, -self.ddy )
 +                if pressedkeys[pygame.K_e]:​ # right side
 +                         ​self.ddx = +math.cos(self.angle*GRAD)
 +                         ​self.ddy = -math.sin(self.angle*GRAD)
 +                         ​Smoke(self.rect.center,​ -self.ddx , -self.ddy )
 +                if pressedkeys[pygame.K_q]:​ # left side
 +                         ​self.ddx = -math.cos(self.angle*GRAD) ​
 +                         ​self.ddy = +math.sin(self.angle*GRAD) ​
 +                         ​Smoke(self.rect.center,​ -self.ddx , -self.ddy )
 +                # ------------shoot-----------------
 +                if self.cooldown > 0:
 +                    self.cooldown -= time 
 +                else:
 +                    if pressedkeys[pygame.K_SPACE]:​ # shoot forward
 +                        self.ddx = +math.sin(self.angle*GRAD)#​recoil
 +                        self.ddy = +math.cos(self.angle*GRAD)
 +                        lasersound.play() # play sound
 +                        self.shots += 1
 +                        Bullet(self,​ -math.sin(self.angle*GRAD) ,
 +                               ​-math.cos(self.angle*GRAD) )
 +                    self.cooldown = self.cooldowntime
 +                # ------------move------------------
 +                if not self.waiting:​
 +                    self.dx += self.ddx * self.speed
 +                    self.dy += self.ddy * self.speed
 +                #​self.speedcheck() ​  # friction, maxspeed ​            
 +                self.pos[0] += self.dx * seconds
 +                self.pos[1] += self.dy * seconds
 +                # -- check if Bird out of screen
 +                self.areacheck()
 +                # ------------- rotate ------------------
 +                if pressedkeys[pygame.K_a]:​ # left turn , counterclockwise
 +                    self.angle += self.rotatespeed
 +                if pressedkeys[pygame.K_d]:​ # right turn, clockwise
 +                    self.angle -= self.rotatespeed
 +                self.oldcenter = self.rect.center
 +                self.image = pygame.transform.rotate(self.image,​ self.angle)
 +                self.rect = self.image.get_rect()
 +                self.rect.center = self.oldcenter
 +                #--- calculate new position on screen -----
 +                self.rect.centerx = round(self.pos[0],​0)
 +                self.rect.centery = round(self.pos[1],​0)
 +                if self.hitpoints <= 0: # ----- alive---- ​
 +                    self.kill()
 +            ​
 +    class Fragment(pygame.sprite.Sprite):​
 +        """​generic Fragment class. Inherits to blue Fragment (implosion),​
 +           red Fragment (explosion),​ smoke (black) and shots (purple)"""​
 +        def __init__(self,​ pos, layer = 9):
 +            self._layer = layer
 +            pygame.sprite.Sprite.__init__(self,​ self.groups)
 +            self.pos = [0.0,0.0]
 +            self.fragmentmaxspeed = FRAGMENTMAXSPEED#​ try out other factors !
 +
 +        def init2(self): ​ # split the init method into 2 parts for better access from subclasses
 +            self.image = pygame.Surface((10,​10))
 +            self.image.set_colorkey((0,​0,​0)) # black transparent
 +            pygame.draw.circle(self.image,​ self.color, (5,5), random.randint(2,​5))
 +            self.image = self.image.convert_alpha()
 +            self.rect = self.image.get_rect()
 +            self.rect.center = self.pos #if you forget this line the sprite sit in the topleft corner
 +            self.time = 0.0
 +            ​
 +        def update(self,​ seconds):
 +            self.time += seconds
 +            if self.time > self.lifetime:​
 +                self.kill() ​
 +            self.pos[0] += self.dx * seconds
 +            self.pos[1] += self.dy * seconds
 +            self.rect.centerx = round(self.pos[0],​0)
 +            self.rect.centery = round(self.pos[1],​0)
 +    ​
 +    class RedFragment(Fragment):​
 +        """​explodes outward from (killed) Bird"""​
 +        def __init__(self,​pos):​
 +            self.groups = allgroup, fragmentgroup,​ gravitygroup
 +            Fragment.__init__(self,​pos)
 +            #red-only part -----------------------------
 +            self.color = (random.randint(25,​255),​0,​0) # red            ​
 +            self.pos[0] = pos[0]
 +            self.pos[1] = pos[1]
 +            self.dx = random.randint(-self.fragmentmaxspeed,​self.fragmentmaxspeed)
 +            self.dy = random.randint(-self.fragmentmaxspeed,​self.fragmentmaxspeed)
 +            self.lifetime = 1 + random.random()*3 # max 3 seconds
 +            self.init2() # continue with generic Fragment class
 +            self.mass = 48.0
 +            ​
 +    class BlueFragment(Fragment):​
 +        """​implode inward toward new Bird (and a bittle outward after
 +           ​reaching the target position"""​
 +        def __init__(self,​ pos):
 +            self.groups = allgroup ​
 +            Fragment.__init__(self,​ pos)
 +            self.target = pos
 +            self.color = (0,​0,​random.randint(25,​255)) # blue
 +            self.side = random.randint(1,​4)
 +            if self.side == 1:  # left side
 +                self.pos[0] = 0   
 +                self.pos[1] = random.randint(0,​screen.get_height())
 +            elif self.side == 2: # top
 +                self.pos[0] = random.randint(0,​screen.get_width()) ​
 +                self.pos[1] = 0
 +            elif self.side == 3: #right
 +                self.pos[0] = screen.get_width() ​
 +                self.pos[1] = random.randint(0,​screen.get_height())
 +            else: #bottom
 +                self.pos[0] = random.randint(0,​screen.get_width()) ​
 +                self.pos[1] = screen.get_height()
 +            # calculating flytime for one second.. Bird.waittime should be 1.0
 +            self.dx = (self.target[0] - self.pos[0]) * 1.0 / Bird.waittime
 +            self.dy = (self.target[1] - self.pos[1]) * 1.0 / Bird.waittime
 +            self.lifetime = Bird.waittime + random.random() * .5 # a bit more livetime after the Bird appears
 +            self.init2()
 +            ​
 +    class Smoke(Fragment):​
 +        """​black exhaust indicating that the BigBird sprite is moved by
 +           the player. Exhaust direction is inverse of players movement direction"""​
 +        def __init__(self,​ pos, dx, dy):
 +           ​self.color = ( random.randint(1,​50),​ random.randint(1,​50),​ random.randint(1,​50) )
 +           ​self.groups = allgroup
 +           ​Fragment.__init__(self,​pos,​ 3) # give startpos and layer 
 +           ​self.pos[0] = pos[0]
 +           ​self.pos[1] = pos[1]
 +           ​self.lifetime = 1 + random.random()*2 # max 3 seconds
 +           ​Fragment.init2(self)
 +           ​self.smokespeed = 120.0 # how fast the smoke leaves the Bird
 +           ​self.smokearc = .3 # 0 = thin smoke stream, 1 = 180 Degrees
 +           arc = self.smokespeed * self.smokearc
 +           ​self.dx = dx * self.smokespeed + random.random()*2*arc - arc
 +           ​self.dy = dy * self.smokespeed + random.random()*2*arc - arc
 +           
 +    class Bullet(Fragment):​
 +        """​a bullet flying in the direction of the BigBird'​s heading. May 
 +           be subject to gravity"""​
 +        def __init__(self,​ boss, dx, dy):
 +            self.color = (200,0,200)
 +            self.boss = boss
 +            self.groups = allgroup, bulletgroup,​ gravitygroup
 +            Fragment.__init__(self,​ self.boss.rect.center,​ 3) # layer behind Bird
 +            self.pos[0] = self.boss.pos[0]
 +            self.pos[1] = self.boss.pos[1]
 +            self.lifetime = 5 # 5 seconds
 +            self.image = pygame.Surface((4,​20))
 +            self.image.set_colorkey((0,​0,​0)) # black transparent
 +            pygame.draw.rect(self.image,​ self.color, (0,0,4,20) )
 +            pygame.draw.rect(self.image,​ (10,0,0), (0,0,4,4)) # point
 +            self.image = self.image.convert_alpha()
 +            self.image0 = self.image.copy()
 +            self.rect = self.image.get_rect()
 +            self.rect.center = self.boss.rect.center
 +            self.image = pygame.transform.rotate(self.image,​ self.boss.angle)
 +            self.rect = self.image.get_rect()
 +            self.rect.center = self.boss.rect.center
 +            self.time = 0.0
 +            self.bulletspeed = 250.0 # pixel per second ?
 +            self.bulletarc = 0.05 # perfect shot has 0.0
 +            arc = self.bulletspeed * self.bulletarc
 +            self.dx = dx * self.bulletspeed + random.random()*2*arc -arc
 +            self.dy = dy * self.bulletspeed + random.random()*2*arc -arc
 +            self.mass = 25.0
 +            self.angle = self.boss.angle
 +            ​
 +        def update(self,​ time):
 +            Fragment.update(self,​time)
 +            #--------- rotate into direction of movement ------------
 +            self.angle = math.atan2(-self.dx,​ -self.dy)/​math.pi*180.0 ​
 +            self.image = pygame.transform.rotozoom(self.image0,​self.angle,​1.0)
 +            ​
 +    # ----------------- background artwork -------------  ​
 +    background = pygame.Surface((screen.get_width(),​ screen.get_height()))
 +    background.fill((255,​255,​255)) ​    # fill white
 +    background.blit(write("​navigate with w,a,s,d and q and e "​),​(50,​40))
 +    background.blit(write("​press SPACE to fire bullets"​),​(50,​70))
 +    background.blit(write("​press g to toggle gravity"​),​ (50, 100))
 +    background.blit(write("​Press ESC to quit "), (50,130))
 +    background = background.convert() ​ # jpg can not have transparency
 +    screen.blit(background,​ (0,​0)) ​    # blit background on screen (overwriting all)
 +    #​-----------------define sprite groups------------------------
 +    birdgroup = pygame.sprite.Group() ​
 +    bulletgroup = pygame.sprite.Group()
 +    fragmentgroup = pygame.sprite.Group()
 +    gravitygroup = pygame.sprite.Group()
 +    # only the allgroup draws the sprite, so i use LayeredUpdates() instead Group()
 +    allgroup = pygame.sprite.LayeredUpdates() # more sophisticated,​ can draw sprites in layers ​
 +
 +    #​-------------loading files from data subdirectory -------------------------------
 +    try: # load images into classes (class variable !). if not possible, draw ugly images
 +        Bird.image.append(pygame.image.load(os.path.join(folder,"​babytux.png"​)))
 +        Bird.image.append(pygame.image.load(os.path.join(folder,"​babytux_neg.png"​)))
 +    except:
 +        print("​no image files '​babytux.png'​ and '​babytux_neg.png'​ in subfolder %s" % folder)
 +        print("​therfore drawing ugly sprites instead"​)
 +        image = pygame.Surface((32,​36))
 +        image.fill((255,​255,​255))
 +        pygame.draw.circle(image,​ (0,0,0), (16,18), 15,2)
 +        pygame.draw.polygon(image,​ (0,0,0), ((32,​36),​(0,​36),​(16,​0)),​1) # triangle
 +        image.set_colorkey((255,​255,​255))
 +        Bird.image.append(image) # alternative ugly image
 +        image2 = image.copy()
 +        pygame.draw.circle(image2,​ (0,0,255), (16,18), 13,0)
 +        Bird.image.append(image2)
 +    Bird.image.append(pygame.transform.scale2x(Bird.image[0])) # copy of first image, big bird
 +    Bird.image.append(pygame.transform.scale2x(Bird.image[1])) # copy of blue image, big bird
 +    Bird.image[0] = Bird.image[0].convert_alpha()
 +    Bird.image[1] = Bird.image[1].convert_alpha()
 +    Bird.image[2] = Bird.image[2].convert_alpha()
 +    Bird.image[3] = Bird.image[3].convert_alpha()
 +
 +    try: # ------- load sound -------
 +        crysound = pygame.mixer.Sound(os.path.join(folder,'​claws.ogg'​)) ​ #load sound
 +        warpsound = pygame.mixer.Sound(os.path.join(folder,'​wormhole.ogg'​))
 +        bombsound = pygame.mixer.Sound(os.path.join(folder,'​bomb.ogg'​))
 +        lasersound = pygame.mixer.Sound(os.path.join(folder,'​shoot.ogg'​))
 +        hitsound = pygame.mixer.Sound(os.path.join(folder,'​beep.ogg'​))
 +    except:
 +        print("​could not load one of the sound files from folder %s. no sound, sorry" %folder)
 +    # ------------- before the main loop ----------------------
 +    screentext = Text()
 +    clock = pygame.time.Clock() ​       # create pygame clock object ​
 +    mainloop = True
 +    FPS = 60                           # desired max. framerate in frames per second. ​
 +    amount = 7 # how many small birds should always be present on the screen
 +    player = BigBird() # big Bird
 +    overtime = 15 # time in seconds to admire the explosion of player before the game ends
 +    gameOver = False
 +    hits = 0  # how often the player was hitting a small Bird
 +    quota = 0 # hit/miss quota
 +    gametime = 60 # how long to play (seconds)
 +    playtime = 0  # how long the game was played
 +    gravity = True
 +        ​
 +    while mainloop:
 +        milliseconds = clock.tick(FPS) ​ # milliseconds passed since last frame
 +        seconds = milliseconds / 1000.0 # seconds passed since last frame
 +        playtime += seconds # keep track of playtime
 +        for event in pygame.event.get():​
 +            if event.type == pygame.QUIT:​
 +                mainloop = False # pygame window closed by user
 +            elif event.type == pygame.KEYDOWN:​
 +                if event.key == pygame.K_ESCAPE:​
 +                    mainloop = False # user pressed ESC
 +                elif event.key == pygame.K_x:
 +                    player.hitpoints -= 1
 +                    print(player.hitpoints)
 +                elif event.key == pygame.K_y:
 +                    player.hitpoints += 1
 +                    print(player.hitpoints)
 +                elif event.key == pygame.K_g:
 +                    gravity = not gravity # toggle gravity
 +                elif event.key == pygame.K_p: # get sprites at mouse position, print info
 +                    print("​========================="​)
 +                    print( "​-----Spritelist---------"​)
 +                    spritelist = allgroup.get_sprites_at(pygame.mouse.get_pos())
 +                    for sprite in spritelist:
 +                        print(sprite,​ "​Layer:",​allgroup.get_layer_of_sprite(sprite))
 +                    print("​------------------------"​)
 +         
 +   
 +        if player.shots > 0:
 +            quota = (float(hits)/​player.shots )* 100
 +        pygame.display.set_caption("​fps:​ %.2f gravity: %s hits:%i shots:%i quota:​%.2f%%" ​ % (clock.get_fps(), ​
 +                                     ​gravity,​ hits, player.shots,​ quota))
 +        # ------ collision detection
 +        for bird in birdgroup: ​ # test if a bird collides with another bird
 +            bird.crashing = False # make bird NOT blue
 +            # check the Bird.number to make sure the bird is not crashing with himself
 +            if not bird.waiting:​ # do not check birds outside the screen
 +                crashgroup = pygame.sprite.spritecollide(bird,​ birdgroup, False )
 +                for crashbird in crashgroup: ​ # test bird with other bird collision
 +                    if crashbird.number > bird.number:​ #avoid checking twice
 +                        bird.crashing = True # make bird blue
 +                        crashbird.crashing = True # make other bird blue
 +                        if not (bird.waiting or crashbird.waiting):​
 +                            elastic_collision(crashbird,​ bird) # change dx and dy of both birds
 +                                            ​
 +                crashgroup = pygame.sprite.spritecollide(bird,​ bulletgroup,​ False)
 +                for ball in crashgroup: ​ # test for collision with bullet
 +                    if ball.boss.number != bird.number:​
 +                        hitsound.play()
 +                        hits +=1
 +                        bird.hitpoints -= ball.boss.damage
 +                        factor =  (ball.mass / bird.mass)
 +                        bird.dx += ball.dx * factor
 +                        bird.dy += ball.dy * factor
 +                        ball.kill()
 +                        ​
 +                crashgroup = pygame.sprite.spritecollide(bird,​ fragmentgroup,​ False)
 +                for frag in crashgroup: # test for red fragments
 +                    bird.hitpoints -=1
 +                    factor =  frag.mass / bird.mass
 +                    bird.dx += frag.dx * factor
 +                    bird.dy += frag.dy * factor
 +                    frag.kill()
 +                    ​
 +        if gravity: # ---- gravity check ---
 +            for thing in gravitygroup:​
 +                thing.dy += FORCE_OF_GRAVITY # gravity suck down all kind of things
 +                    ​
 +        if len(birdgroup) < amount: # create enough SmallBirds
 +            for _ in range(random.randint(1,​3)):​
 +                SmallBird()
 +        ​
 +        # ------game Over ? -------------
 +        if (player.hitpoints < 1 or playtime > gametime) and not gameOver:
 +            gameOver = True # do those things once when the game ends
 +            screentext.newmsg("​Game Over. hits/shots: %i/%i quota: %.2f%%"​ % (hits, player.shots,​ quota), (255,0,0))
 +            player.hitpoints = 0 # kill the player into a big explosion
 +        if gameOver: # overtime to watch score, explosion etc
 +            overtime -= seconds
 +            if overtime < 0:
 +                mainloop = False
 +        else: # not yet gameOver
 +            screentext.newmsg("​Time left: %.2f" % (gametime - playtime))
 +        ​
 +        # ----------- clear, draw , update, flip -----------------  ​
 +        allgroup.clear(screen,​ background)
 +        allgroup.update(seconds)
 +        allgroup.draw(screen) ​          
 +        pygame.display.flip() ​        
 +
 +if __name__ == "​__main__":​
 +    game()
 +    ​
 +
 +</​code>​
 +^ [[:​en:​pygame:​step016| ← previous]] ^ [[en:​pygame:​start| ↑ Overview]] ^ [[:​en:​pygame:​step018| → next ]] ^
  
  
/var/www/horst/thepythongamebook.com/data/pages/en/pygame/step017.txt · Last modified: 2020/05/15 22:50 by Horst JENS