From a81ef337e2355038184c6b6f016acceb4b3c5118 Mon Sep 17 00:00:00 2001 From: DarkSun Date: Fri, 3 Jan 2020 00:55:22 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=89=E9=A2=98:=2020200102=20Put=20some=20l?= =?UTF-8?q?oot=20in=20your=20Python=20platformer=20game?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sources/tech/20200102 Put some loot in your Python platformer game.md --- ...ome loot in your Python platformer game.md | 535 ++++++++++++++++++ 1 file changed, 535 insertions(+) create mode 100644 sources/tech/20200102 Put some loot in your Python platformer game.md diff --git a/sources/tech/20200102 Put some loot in your Python platformer game.md b/sources/tech/20200102 Put some loot in your Python platformer game.md new file mode 100644 index 0000000000..678fb69516 --- /dev/null +++ b/sources/tech/20200102 Put some loot in your Python platformer game.md @@ -0,0 +1,535 @@ +[#]: collector: (lujun9972) +[#]: translator: ( ) +[#]: reviewer: ( ) +[#]: publisher: ( ) +[#]: url: ( ) +[#]: subject: (Put some loot in your Python platformer game) +[#]: via: (https://opensource.com/article/20/1/loot-python-platformer-game) +[#]: author: (Seth Kenlon https://opensource.com/users/seth) + +Put some loot in your Python platformer game +====== +Give your players some treasures to collect and boost their score in +this installment on programming video games with Python's Pygame module. +![Hearts, stars, and dollar signs][1] + +This is part 9 in an ongoing series about creating video games in [Python 3][2] using the [Pygame][3] module. Previous articles are: + + * [Learn how to program in Python by building a simple dice game][4] + * [Build a game framework with Python using the Pygame module][5] + * [How to add a player to your Python game][6] + * [Using Pygame to move your game character around][7] + * [What's a hero without a villain? How to add one to your Python game][8] + * [Simulate gravity in your Python game][9] + * [Add jumping to your Python platformer game][10] + * [Enable your Python game player to run forward and backward][11] + + + +If you've followed along with the previous articles in this series, then you know all the basics of programming video game mechanics. You can build upon these basics to create a fully functional video game all your own. Following a "recipe" like the code samples in this series is helpful when you're first learning, but eventually, the recipe becomes a constraint. It's time to use the principles you've learned and apply them in new ways. + +If that sounds easier said than done, this article demonstrates an example of how to leverage what you already know for new purposes. Specifically, it covers how to implement a looting system + +using what you have already learned about platforms from previous lessons. + +In most video games, you have the opportunity to "loot," or collect treasures and other items within the game world. Loot usually increases your score or your health or provides information leading to your next quest. + +Including loot in your game is similar to programming platforms. Like platforms, loot has no user controls, scrolls with the game world, and must check for collisions with the player sprite. + +### Creating the loot function + +Loot is so similar to platforms that you don't even need a Loot class. You can just reuse the **Platform** class and call the results loot. + +Since loot type and placement probably differ from level to level, create a new function called **loot** in your **Level** class, if you don't already have one. Since loot items are not platforms, you must also create a new **loot_list** group and then add loot objects to it. As with platforms, ground, and enemies, this group is used when checking for collisions: + + +``` +    def loot(lvl,lloc): +        if lvl == 1: +            loot_list = pygame.sprite.Group() +            loot = Platform(300,ty*7,tx,ty, 'loot_1.png') +            loot_list.add(loot) + +        if lvl == 2: +            print(lvl) + +        return loot_list +``` + +You can add as many loot objects as you like; just remember to add each one to your loot list. The arguments for the **Platform** class are the X position, the Y position, the width and height of the loot sprite (it's usually easiest to keep your loot sprite the same size as all other tiles), and the image you want to use as loot. Placement of loot can be just as complex as mapping platforms, so use the level design document you created when creating the level. + +Call your new loot function in the **Setup** section of your script. In the following code, the first three lines are for context, so just add the fourth: + + +``` +enemy_list = Level.bad( 1, eloc ) +ground_list = Level.ground( 1,gloc,tx,ty ) +plat_list = Level.platform( 1,tx,ty ) +loot_list = Level.loot(1,tx,ty) +``` + +As you know by now, the loot won't get drawn to the screen unless you include it in your main loop. Add the final line from the following code sample to your loop: + + +``` +    enemy_list.draw(world) +    ground_list.draw(world) +    plat_list.draw(world) +    loot_list.draw(world) +``` + +Launch your game to see what happens. + +![Loot in Python platformer][12] + +Your loot objects are spawned, but they don't do anything when your player runs into them, nor do they scroll when your player runs past them. Fix these issues next. + +### Scrolling loot + +Like platforms, loot has to scroll when the player moves through the game world. The logic is identical to platform scrolling. To scroll the loot forward, add the last two lines: + + +``` +        for e in enemy_list: +            e.rect.x -= scroll +        for l in loot_list: +            l.rect.x -= scroll +``` + +To scroll it backward, add the last two lines: + + +``` +        for e in enemy_list: +            e.rect.x += scroll +        for l in loot_list: +            l.rect.x += scroll +``` + +Launch your game again to see that your loot objects now act like they're _in_ the game world instead of just painted on top of it. + +### Detecting collisions + +As with platforms and enemies, you can check for collisions between loot and your player. The logic is the same as other collisions, except that a hit doesn't (necessarily) affect gravity or health. Instead, a hit causes the loot to disappear and increment the player's score. + +When your player touches a loot object, you can remove that object from the **loot_list**. This means that when your main loop redraws all loot items in **loot_list**, it won't redraw that particular object, so it will look like the player has grabbed the loot. + +Add the following code above the platform collision detection in the **update** function of your **Player** class (the last line is just for context): + + +``` +                loot_hit_list = pygame.sprite.spritecollide(self, loot_list, False) +                for loot in loot_hit_list: +                        loot_list.remove(loot) +                        self.score += 1 +                print(self.score) +  +        plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False) +``` + +Not only do you remove the loot object from its group when a collision happens, but you also award your player a bump in score. You haven't created a score variable yet, so add that to your player's properties, created in the **__init__** function of the **Player** class. In the following code, the first two lines are for context, so just add the score variable: + + +``` +        self.frame = 0 +        self.health = 10 +        self.score = 0 +``` + +When calling the **update** function in your main loop, include the **loot_list**: + + +``` +        player.gravity() +        player.update() +``` + +As you can see, you've got all the basics. All you have to do now is use what you know in new ways. + +There are a few more tips in the next article, but in the meantime, use what you've learned to make a few simple, single-level games. Limiting the scope of what you are trying to create is important so that you don't overwhelm yourself. It also makes it easier to end up with a finished product that looks and feels finished. + +Here's all the code you've written for this Python platformer so far: + + +``` +#!/usr/bin/env python3 +# draw a world +# add a player and player control +# add player movement +# add enemy and basic collision +# add platform +# add gravity +# add jumping +# add scrolling + +# GNU All-Permissive License +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved.  This file is offered as-is, +# without any warranty. + +import pygame +import sys +import os + +''' +Objects +''' + +class Platform(pygame.sprite.Sprite): +    # x location, y location, img width, img height, img file     +    def __init__(self,xloc,yloc,imgw,imgh,img): +        pygame.sprite.Sprite.__init__(self) +        self.image = pygame.image.load(os.path.join('images',img)).convert() +        self.image.convert_alpha() +        self.rect = self.image.get_rect() +        self.rect.y = yloc +        self.rect.x = xloc + +class Player(pygame.sprite.Sprite): +    ''' +    Spawn a player +    ''' +    def __init__(self): +        pygame.sprite.Sprite.__init__(self) +        self.movex = 0 +        self.movey = 0 +        self.frame = 0 +        self.health = 10 +        self.collide_delta = 0 +        self.jump_delta = 6 +        self.score = 1 +        self.images = [] +        for i in range(1,9): +            img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert() +            img.convert_alpha() +            img.set_colorkey(ALPHA) +            self.images.append(img) +            self.image = self.images[0] +            self.rect  = self.image.get_rect() + +    def jump(self,platform_list): +        self.jump_delta = 0 + +    def gravity(self): +        self.movey += 3.2 # how fast player falls +        +        if self.rect.y > worldy and self.movey >= 0: +            self.movey = 0 +            self.rect.y = worldy-ty +        +    def control(self,x,y): +        ''' +        control player movement +        ''' +        self.movex += x +        self.movey += y +        +    def update(self): +        ''' +        Update sprite position +        ''' +        +        self.rect.x = self.rect.x + self.movex +        self.rect.y = self.rect.y + self.movey + +        # moving left +        if self.movex < 0: +            self.frame += 1 +            if self.frame > ani*3: +                self.frame = 0 +            self.image = self.images[self.frame//ani] + +        # moving right +        if self.movex > 0: +            self.frame += 1 +            if self.frame > ani*3: +                self.frame = 0 +            self.image = self.images[(self.frame//ani)+4] + +        # collisions +        enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False) +        for enemy in enemy_hit_list: +            self.health -= 1 +            #print(self.health) + +        loot_hit_list = pygame.sprite.spritecollide(self, loot_list, False) +        for loot in loot_hit_list: +            loot_list.remove(loot) +            self.score += 1 +            print(self.score) + +        plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False) +        for p in plat_hit_list: +            self.collide_delta = 0 # stop jumping +            self.movey = 0 +            if self.rect.y > p.rect.y: +                self.rect.y = p.rect.y+ty +            else: +                self.rect.y = p.rect.y-ty +            +        ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False) +        for g in ground_hit_list: +            self.movey = 0 +            self.rect.y = worldy-ty-ty +            self.collide_delta = 0 # stop jumping +            if self.rect.y > g.rect.y: +                self.health -=1 +                print(self.health) +                +        if self.collide_delta < 6 and self.jump_delta < 6: +            self.jump_delta = 6*2 +            self.movey -= 33  # how high to jump +            self.collide_delta += 6 +            self.jump_delta    += 6 +            +class Enemy(pygame.sprite.Sprite): +    ''' +    Spawn an enemy +    ''' +    def __init__(self,x,y,img): +        pygame.sprite.Sprite.__init__(self) +        self.image = pygame.image.load(os.path.join('images',img)) +        self.movey = 0 +        #self.image.convert_alpha() +        #self.image.set_colorkey(ALPHA) +        self.rect = self.image.get_rect() +        self.rect.x = x +        self.rect.y = y +        self.counter = 0 + +                +    def move(self): +        ''' +        enemy movement +        ''' +        distance = 80 +        speed = 8 + +        self.movey += 3.2 +        +        if self.counter >= 0 and self.counter <= distance: +            self.rect.x += speed +        elif self.counter >= distance and self.counter <= distance*2: +            self.rect.x -= speed +        else: +            self.counter = 0 +        +        self.counter += 1 + +        if not self.rect.y >= worldy-ty-ty: +            self.rect.y += self.movey + +        plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False) +        for p in plat_hit_list: +            self.movey = 0 +            if self.rect.y > p.rect.y: +                self.rect.y = p.rect.y+ty +            else: +                self.rect.y = p.rect.y-ty + +        ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False) +        for g in ground_hit_list: +            self.rect.y = worldy-ty-ty + +        +class Level(): +    def bad(lvl,eloc): +        if lvl == 1: +            enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemy +            enemy_list = pygame.sprite.Group() # create enemy group +            enemy_list.add(enemy)              # add enemy to group +            +        if lvl == 2: +            print("Level " + str(lvl) ) + +        return enemy_list + +    def loot(lvl,tx,ty): +        if lvl == 1: +            loot_list = pygame.sprite.Group() +            loot = Platform(200,ty*7,tx,ty, 'loot_1.png') +            loot_list.add(loot) + +        if lvl == 2: +            print(lvl) + +        return loot_list + +    def ground(lvl,gloc,tx,ty): +        ground_list = pygame.sprite.Group() +        i=0 +        if lvl == 1: +            while i < len(gloc): +                ground = Platform(gloc[i],worldy-ty,tx,ty,'ground.png') +                ground_list.add(ground) +                i=i+1 + +        if lvl == 2: +            print("Level " + str(lvl) ) + +        return ground_list + +    def platform(lvl,tx,ty): +        plat_list = pygame.sprite.Group() +        ploc = [] +        i=0 +        if lvl == 1: +            ploc.append((20,worldy-ty-128,3)) +            ploc.append((300,worldy-ty-256,3)) +            ploc.append((500,worldy-ty-128,4)) + +            while i < len(ploc): +                j=0 +                while j <= ploc[i][2]: +                    plat = Platform((ploc[i][0]+(j*tx)),ploc[i][1],tx,ty,'ground.png') +                    plat_list.add(plat) +                    j=j+1 +                print('run' + str(i) + str(ploc[i])) +                i=i+1 + +        if lvl == 2: +            print("Level " + str(lvl) ) + +        return plat_list + +''' +Setup +''' +worldx = 960 +worldy = 720 + +fps = 40 # frame rate +ani = 4  # animation cycles +clock = pygame.time.Clock() +pygame.init() +main = True + +BLUE  = (25,25,200) +BLACK = (23,23,23 ) +WHITE = (254,254,254) +ALPHA = (0,255,0) + +world = pygame.display.set_mode([worldx,worldy]) +backdrop = pygame.image.load(os.path.join('images','stage.png')).convert() +backdropbox = world.get_rect() +player = Player() # spawn player +player.rect.x = 0 +player.rect.y = 0 +player_list = pygame.sprite.Group() +player_list.add(player) +steps = 10 +forwardx = 600 +backwardx = 230 + +eloc = [] +eloc = [200,20] +gloc = [] +#gloc = [0,630,64,630,128,630,192,630,256,630,320,630,384,630] +tx = 64 #tile size +ty = 64 #tile size + +i=0 +while i <= (worldx/tx)+tx: +    gloc.append(i*tx) +    i=i+1 + +enemy_list = Level.bad( 1, eloc ) +ground_list = Level.ground( 1,gloc,tx,ty ) +plat_list = Level.platform( 1,tx,ty ) +loot_list = Level.loot(1,tx,ty) + +''' +Main loop +''' +while main == True: +    for event in pygame.event.get(): +        if event.type == pygame.QUIT: +            pygame.quit(); sys.exit() +            main = False + +        if event.type == pygame.KEYDOWN: +            if event.key == pygame.K_LEFT or event.key == ord('a'): +                print("LEFT") +                player.control(-steps,0) +            if event.key == pygame.K_RIGHT or event.key == ord('d'): +                print("RIGHT") +                player.control(steps,0) +            if event.key == pygame.K_UP or event.key == ord('w'): +                print('jump') + +        if event.type == pygame.KEYUP: +            if event.key == pygame.K_LEFT or event.key == ord('a'): +                player.control(steps,0) +            if event.key == pygame.K_RIGHT or event.key == ord('d'): +                player.control(-steps,0) +            if event.key == pygame.K_UP or event.key == ord('w'): +                player.jump(plat_list) + +            if event.key == ord('q'): +                pygame.quit() +                sys.exit() +                main = False + +    # scroll the world forward +    if player.rect.x >= forwardx: +        scroll = player.rect.x - forwardx +        player.rect.x = forwardx +        for p in plat_list: +            p.rect.x -= scroll +        for e in enemy_list: +            e.rect.x -= scroll +        for l in loot_list: +            l.rect.x -= scroll +                +    # scroll the world backward +    if player.rect.x <= backwardx: +        scroll = backwardx - player.rect.x +        player.rect.x = backwardx +        for p in plat_list: +            p.rect.x += scroll +        for e in enemy_list: +            e.rect.x += scroll +        for l in loot_list: +            l.rect.x += scroll + +    world.blit(backdrop, backdropbox)     +    player.gravity() # check gravity +    player.update() +    player_list.draw(world) #refresh player position +    enemy_list.draw(world)  # refresh enemies +    ground_list.draw(world)  # refresh enemies +    plat_list.draw(world)   # refresh platforms +    loot_list.draw(world)   # refresh loot + +    for e in enemy_list: +        e.move() +    pygame.display.flip() +    clock.tick(fps) +``` + +-------------------------------------------------------------------------------- + +via: https://opensource.com/article/20/1/loot-python-platformer-game + +作者:[Seth Kenlon][a] +选题:[lujun9972][b] +译者:[译者ID](https://github.com/译者ID) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://opensource.com/users/seth +[b]: https://github.com/lujun9972 +[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/BUS_lovemoneyglory2.png?itok=AvneLxFp (Hearts, stars, and dollar signs) +[2]: https://www.python.org/ +[3]: https://www.pygame.org/news +[4]: https://opensource.com/article/17/10/python-101 +[5]: https://opensource.com/article/17/12/game-framework-python +[6]: https://opensource.com/article/17/12/game-python-add-a-player +[7]: https://opensource.com/article/17/12/game-python-moving-player +[8]: https://opensource.com/article/18/5/pygame-enemy +[9]: https://opensource.com/article/19/11/simulate-gravity-python +[10]: https://opensource.com/article/19/12/jumping-python-platformer-game +[11]: https://opensource.com/article/19/12/python-platformer-game-run +[12]: https://opensource.com/sites/default/files/uploads/pygame-loot.jpg (Loot in Python platformer)